ADDED: Card sub-components
TODO: Complete styles for card sub-components and section button
This commit is contained in:
parent
80702813f4
commit
d0a34e7a1f
40
app/page.tsx
40
app/page.tsx
|
@ -1,9 +1,12 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import testImage1 from './test-images/test-image-1.jpg';
|
||||||
import { Card } from '../src/primitive-components/card/card';
|
import { Card } from '../src/primitive-components/card/card';
|
||||||
|
import { CardMedia } from '../src/primitive-components/card/card-media';
|
||||||
import { CardActionArea } from '../src/primitive-components/card/card-action-area';
|
import { CardActionArea } from '../src/primitive-components/card/card-action-area';
|
||||||
import { Button } from '../src/primitive-components/button-components/button/button';
|
import { Button } from '../src/primitive-components/button-components/button/button';
|
||||||
|
import { CardHeader } from '../src/primitive-components/card/card-header';
|
||||||
const cardStyles = { width: '256px', aspectRatio: 1 };
|
import { CardBody } from '../src/primitive-components/card/card-body';
|
||||||
|
import { CardFooter } from '../src/primitive-components/card/card-footer';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
|
@ -17,17 +20,36 @@ export default function Page() {
|
||||||
padding: '8px',
|
padding: '8px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ display: 'flex', gap: '8px' }}>
|
<div style={{ display: 'flex', gap: '8px', maxWidth: 512 }}>
|
||||||
<Card style={cardStyles} variant={'filled'}>
|
<Card variant={'outlined'}>
|
||||||
<CardActionArea>
|
<CardHeader>
|
||||||
<h1>Header</h1>
|
<h1>Header</h1>
|
||||||
|
</CardHeader>
|
||||||
|
<CardActionArea>
|
||||||
|
<CardMedia src={testImage1.src} type={'img'} />
|
||||||
|
<CardBody>
|
||||||
|
<h4>
|
||||||
|
<strong>Sub-header</strong>
|
||||||
|
</h4>
|
||||||
|
<p>
|
||||||
|
Lorem ipsum dolor sit amet, consecrate
|
||||||
|
adipiscing elit, sed do eiusmod tempor
|
||||||
|
incididunt ut labore et dolore magna aliqua.
|
||||||
|
Ut enim ad minim veniam, quis nostrud
|
||||||
|
exercitation ullamco laboris nisi ut aliquip
|
||||||
|
ex ea commodo consequat. Duis aute irure
|
||||||
|
dolor in reprehenderit in voluptate velit
|
||||||
|
esse cillum dolore eu fugiat nulla pariatur.
|
||||||
|
Excepteur sint occaecat cupidatat non
|
||||||
|
proident, sunt in culpa qui officia deserunt
|
||||||
|
mollit anim id est laborum.
|
||||||
|
</p>
|
||||||
|
</CardBody>
|
||||||
</CardActionArea>
|
</CardActionArea>
|
||||||
<div>
|
<CardFooter>
|
||||||
<Button>label</Button>
|
<Button>label</Button>
|
||||||
</div>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
<Card style={cardStyles} variant={'outlined'} />
|
|
||||||
<Card style={cardStyles} variant={'elevated'} />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 132 KiB |
|
@ -0,0 +1,10 @@
|
||||||
|
module.exports = {
|
||||||
|
images: {
|
||||||
|
remotePatterns: [
|
||||||
|
{
|
||||||
|
protocol: 'https',
|
||||||
|
hostname: '**',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
|
@ -13,14 +13,14 @@ export const ButtonLayout = forwardRef<HTMLButtonElement, ButtonLayoutProps>(
|
||||||
buttonId = useId(),
|
buttonId = useId(),
|
||||||
events = useRippleEffect(ripplesRef, setIsActive);
|
events = useRippleEffect(ripplesRef, setIsActive);
|
||||||
|
|
||||||
const classes =
|
const extraClassStyles =
|
||||||
`m3${isActive ? ' is-active' : ''} ${props.className ?? ''}`.trimEnd();
|
`m3${isActive ? ' is-active' : ''} ${props.className ?? ''}`.trimEnd();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
{...props}
|
{...props}
|
||||||
{...events}
|
{...events}
|
||||||
className={classes}
|
className={extraClassStyles}
|
||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
id={buttonId}
|
id={buttonId}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
|
|
@ -30,11 +30,11 @@ export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
|
||||||
icon: toggled ? toggled.unselected ?? 'add_circle' : 'add_circle',
|
icon: toggled ? toggled.unselected ?? 'add_circle' : 'add_circle',
|
||||||
});
|
});
|
||||||
|
|
||||||
const classes = `m3-icon-button ${toggleIcon.state} ${variant} ${toggled ? 'toggled' : ''} ${className}`;
|
const extraClassStyles = `m3-icon-button ${toggleIcon.state} ${variant} ${toggled ? 'toggled' : ''} ${className}`;
|
||||||
|
|
||||||
const toggle = (classes: string, icon: string) => {
|
const toggle = (extraClassStyles: string, icon: string) => {
|
||||||
setToggleIcon({
|
setToggleIcon({
|
||||||
state: classes,
|
state: extraClassStyles,
|
||||||
icon: icon,
|
icon: icon,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -60,7 +60,7 @@ export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
|
||||||
<ButtonLayout
|
<ButtonLayout
|
||||||
{...props}
|
{...props}
|
||||||
centralRipple={centralRipple}
|
centralRipple={centralRipple}
|
||||||
className={classes}
|
className={extraClassStyles}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onClick={callback}
|
onClick={callback}
|
||||||
ref={buttonRef}
|
ref={buttonRef}
|
||||||
|
|
|
@ -14,14 +14,14 @@ export const CardActionArea = forwardRef<HTMLDivElement, CardActionAreaProps>(
|
||||||
setIsActive,
|
setIsActive,
|
||||||
ripples,
|
ripples,
|
||||||
);
|
);
|
||||||
const classes =
|
const extraClassStyles =
|
||||||
`m3 m3-card-action-area${isActive ? ' is-active' : ''} ${props.className ?? ''}`.trimEnd();
|
`m3 m3-card-action-area${isActive ? ' is-active' : ''} ${props.className ?? ''}`.trimEnd();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
{...props}
|
{...props}
|
||||||
{...CardActionAreaEvents}
|
{...CardActionAreaEvents}
|
||||||
className={classes}
|
className={extraClassStyles}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
>
|
>
|
||||||
<div className={'m3 m3-card-action-area-content'}>
|
<div className={'m3 m3-card-action-area-content'}>
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
export function CardActions(props) {
|
|
||||||
return (
|
|
||||||
<div></div>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import React, { forwardRef, HTMLAttributes } from 'react';
|
||||||
|
|
||||||
|
export const CardBody = forwardRef<HTMLElement, HTMLAttributes<HTMLElement>>(
|
||||||
|
(props, ref) => {
|
||||||
|
const extraClassStyles =
|
||||||
|
`m3 m3-card-body ${props.className ?? ''}`.trimEnd();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section {...props} className={extraClassStyles} ref={ref}>
|
||||||
|
{props.children}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
|
@ -1,7 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
export function CardContent(props) {
|
|
||||||
return (
|
|
||||||
<div></div>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import React, { forwardRef, HTMLAttributes } from 'react';
|
||||||
|
|
||||||
|
export const CardFooter = forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
HTMLAttributes<HTMLDivElement>
|
||||||
|
>((props, ref) => {
|
||||||
|
const extraClassStyles =
|
||||||
|
`m3 m3-card-footer ${props.className ?? ''}`.trimEnd();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div {...props} className={extraClassStyles} ref={ref}>
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
|
@ -1,7 +1,15 @@
|
||||||
import React from 'react';
|
import React, { forwardRef, HTMLAttributes } from 'react';
|
||||||
|
|
||||||
|
export const CardHeader = forwardRef<
|
||||||
|
HTMLHeadElement,
|
||||||
|
HTMLAttributes<HTMLHeadElement>
|
||||||
|
>((props, ref) => {
|
||||||
|
const extraClassStyles =
|
||||||
|
`m3 m3-card-header ${props.className ?? ''}`.trimEnd();
|
||||||
|
|
||||||
export function CardHeader(props) {
|
|
||||||
return (
|
return (
|
||||||
<div></div>
|
<header {...props} className={extraClassStyles} ref={ref}>
|
||||||
|
{props.children}
|
||||||
|
</header>
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|
|
@ -1,33 +1,19 @@
|
||||||
import Image from 'next/image';
|
import { createElement, forwardRef } from 'react';
|
||||||
import React, { forwardRef } from 'react';
|
|
||||||
import { CardMediaProps, CardMediaType } from './card.types';
|
import { CardMediaProps, CardMediaType } from './card.types';
|
||||||
|
|
||||||
export const CardMedia = forwardRef<CardMediaType, CardMediaProps>(
|
export const CardMedia = forwardRef<CardMediaType, CardMediaProps>(
|
||||||
(
|
({ type, rounded = true, className = '', ...props }, ref) => {
|
||||||
{
|
const extraClassStyles =
|
||||||
alt,
|
`m3 m3-card-media${rounded ? ' m3-rounded' : ''} ${className}`.trimEnd();
|
||||||
src,
|
|
||||||
type,
|
|
||||||
quality = 80,
|
|
||||||
rounded = true,
|
|
||||||
preview = true,
|
|
||||||
className = '',
|
|
||||||
...props
|
|
||||||
},
|
|
||||||
ref,
|
|
||||||
) => {
|
|
||||||
const classes =
|
|
||||||
`m3 m3-card-media${rounded ? ' m3-rounded' : ''}${preview ? ' m3-preview' : ''} ${className}`.trimEnd();
|
|
||||||
|
|
||||||
switch (type) {
|
return createElement(
|
||||||
case 'img':
|
type,
|
||||||
return <Image alt={alt} quality={quality} src={src} />;
|
{
|
||||||
default:
|
|
||||||
return React.createElement(type, {
|
|
||||||
...props,
|
...props,
|
||||||
className: classes,
|
className: extraClassStyles,
|
||||||
ref: ref,
|
ref: ref,
|
||||||
});
|
},
|
||||||
}
|
props.children,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,12 +4,12 @@ import { Container } from '../container/container';
|
||||||
|
|
||||||
export const Card = forwardRef<HTMLDivElement, CardProps>(
|
export const Card = forwardRef<HTMLDivElement, CardProps>(
|
||||||
({ variant = 'filled', ...props }, ref) => {
|
({ variant = 'filled', ...props }, ref) => {
|
||||||
const classes = `m3-card m3-card-${variant} ${props.className ?? ''}`;
|
const extraClassStyles = `m3-card m3-card-${variant} ${props.className ?? ''}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container
|
<Container
|
||||||
{...props}
|
{...props}
|
||||||
className={classes}
|
className={extraClassStyles}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
variant={variant}
|
variant={variant}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,26 +1,22 @@
|
||||||
import { HTMLAttributes } from 'react';
|
import { HTMLProps } from 'react';
|
||||||
import { ContainerProps } from '../container/container.types';
|
import { ContainerProps } from '../container/container.types';
|
||||||
import { RipplePropsForComponents } from '../ripple/ripple.types';
|
import { RipplePropsForComponents } from '../ripple/ripple.types';
|
||||||
|
|
||||||
|
export interface CardProps extends ContainerProps {}
|
||||||
|
|
||||||
export interface CardActionAreaProps
|
export interface CardActionAreaProps
|
||||||
extends RipplePropsForComponents<HTMLDivElement> {
|
extends RipplePropsForComponents<HTMLDivElement> {
|
||||||
ripples?: boolean;
|
ripples?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CardProps extends ContainerProps {}
|
export interface CardMediaProps extends HTMLProps<CardMediaType> {
|
||||||
|
|
||||||
export interface CardMediaProps extends HTMLAttributes<CardMediaType> {
|
|
||||||
alt: string;
|
|
||||||
src: string;
|
|
||||||
quality?: number;
|
|
||||||
rounded?: boolean;
|
rounded?: boolean;
|
||||||
preview?: boolean;
|
|
||||||
type: 'img' | 'video' | 'picture' | 'iframe' | 'audio';
|
type: 'img' | 'video' | 'picture' | 'iframe' | 'audio';
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CardMediaType = HTMLImageElement &
|
export type CardMediaType =
|
||||||
HTMLVideoElement &
|
| HTMLImageElement
|
||||||
HTMLPictureElement &
|
| HTMLVideoElement
|
||||||
HTMLMediaElement &
|
| HTMLPictureElement
|
||||||
HTMLIFrameElement &
|
| HTMLIFrameElement
|
||||||
HTMLAudioElement;
|
| HTMLAudioElement;
|
||||||
|
|
|
@ -3,11 +3,11 @@ import { ContainerProps } from './container.types';
|
||||||
|
|
||||||
export const Container = forwardRef<HTMLDivElement, ContainerProps>(
|
export const Container = forwardRef<HTMLDivElement, ContainerProps>(
|
||||||
({ variant = 'filled', ...props }, ref) => {
|
({ variant = 'filled', ...props }, ref) => {
|
||||||
const classes =
|
const extraClassStyles =
|
||||||
`m3 m3-container m3-container-${variant} ${props.className ?? ''}`.trimEnd();
|
`m3 m3-container m3-container-${variant} ${props.className ?? ''}`.trimEnd();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div {...props} className={classes} ref={ref}>
|
<div {...props} className={extraClassStyles} ref={ref}>
|
||||||
{props.children}
|
{props.children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -21,7 +21,7 @@ export const CheckBoxLayout = forwardRef<HTMLInputElement, CheckboxLayoutProps>(
|
||||||
|
|
||||||
const classesType = typeInput || type;
|
const classesType = typeInput || type;
|
||||||
|
|
||||||
const classes =
|
const extraClassStyles =
|
||||||
props.className !== undefined
|
props.className !== undefined
|
||||||
? `m3 m3-${type} ${props.className}`
|
? `m3 m3-${type} ${props.className}`
|
||||||
: `m3 m3-${classesType}`;
|
: `m3 m3-${classesType}`;
|
||||||
|
@ -30,7 +30,7 @@ export const CheckBoxLayout = forwardRef<HTMLInputElement, CheckboxLayoutProps>(
|
||||||
<input
|
<input
|
||||||
ref={checkboxRef}
|
ref={checkboxRef}
|
||||||
{...props}
|
{...props}
|
||||||
className={classes.trimEnd()}
|
className={extraClassStyles.trimEnd()}
|
||||||
type={type}
|
type={type}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -26,7 +26,7 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
|
||||||
checkboxRef = useRef(null),
|
checkboxRef = useRef(null),
|
||||||
events = useRippleEffect(ripplesRef, setIsActive);
|
events = useRippleEffect(ripplesRef, setIsActive);
|
||||||
|
|
||||||
const classes =
|
const extraClassStyles =
|
||||||
`m3 m3-checkbox-label ${isActive === true ? 'visible' : ''}`.trimEnd();
|
`m3 m3-checkbox-label ${isActive === true ? 'visible' : ''}`.trimEnd();
|
||||||
|
|
||||||
useImperativeHandle(ref, () => checkboxRef.current);
|
useImperativeHandle(ref, () => checkboxRef.current);
|
||||||
|
@ -36,7 +36,7 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
|
||||||
}, [checkboxRef.current?.checked]);
|
}, [checkboxRef.current?.checked]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<label {...events} className={classes}>
|
<label {...events} className={extraClassStyles}>
|
||||||
<CheckBoxLayout
|
<CheckBoxLayout
|
||||||
{...props}
|
{...props}
|
||||||
indeterminate={props.indeterminate}
|
indeterminate={props.indeterminate}
|
||||||
|
|
|
@ -18,11 +18,11 @@ export const Radio = forwardRef<HTMLInputElement, RadioProps>(
|
||||||
ripplesRef = useRef(null),
|
ripplesRef = useRef(null),
|
||||||
events = useRippleEffect(ripplesRef, setIsActive);
|
events = useRippleEffect(ripplesRef, setIsActive);
|
||||||
|
|
||||||
const classes =
|
const extraClassStyles =
|
||||||
`m3 m3-radio ${isActive === true ? 'visible' : ''}`.trimEnd();
|
`m3 m3-radio ${isActive === true ? 'visible' : ''}`.trimEnd();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div {...events} className={classes}>
|
<div {...events} className={extraClassStyles}>
|
||||||
<CheckBoxLayout {...props} ref={ref} type={'radio'} />
|
<CheckBoxLayout {...props} ref={ref} type={'radio'} />
|
||||||
<span className={'m3 m3-radio-state-layer'} />
|
<span className={'m3 m3-radio-state-layer'} />
|
||||||
<svg height={'20px'} viewBox={'0 0 20 20'} width={'20px'}>
|
<svg height={'20px'} viewBox={'0 0 20 20'} width={'20px'}>
|
||||||
|
|
|
@ -24,7 +24,7 @@ const RippleArea = forwardRef(
|
||||||
uniqueKey = useRef<number>(0),
|
uniqueKey = useRef<number>(0),
|
||||||
uniqueId = useId();
|
uniqueId = useId();
|
||||||
|
|
||||||
const classes = props.className
|
const extraClassStyles = props.className
|
||||||
? `m3 m3-ripple-domain ${props.className}`.trimEnd()
|
? `m3 m3-ripple-domain ${props.className}`.trimEnd()
|
||||||
: 'm3 m3-ripple-domain';
|
: 'm3 m3-ripple-domain';
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ const RippleArea = forwardRef(
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className={classes} id={uniqueId} ref={rippleDomain}>
|
<span className={extraClassStyles} id={uniqueId} ref={rippleDomain}>
|
||||||
<rippleAreaContext.Provider value={clicked.current}>
|
<rippleAreaContext.Provider value={clicked.current}>
|
||||||
<Ripples>{ripples}</Ripples>
|
<Ripples>{ripples}</Ripples>
|
||||||
</rippleAreaContext.Provider>
|
</rippleAreaContext.Provider>
|
||||||
|
|
|
@ -1,8 +1,28 @@
|
||||||
@import 'mixins/m3-mixins'
|
@import 'mixins/m3-mixins'
|
||||||
|
|
||||||
div.m3.m3-card-action-area
|
$padding: 16px
|
||||||
|
|
||||||
|
.m3:not(:first-child):is(.m3-card)
|
||||||
|
padding: $padding
|
||||||
|
|
||||||
|
:is(div.m3-card-footer, header.m3-card-header, section.m3-card-body).m3
|
||||||
|
padding: $padding
|
||||||
|
display: block
|
||||||
|
box-sizing: border-box
|
||||||
|
|
||||||
|
.m3.m3-card-media
|
||||||
|
&.m3-rounded
|
||||||
|
border-radius: 12px
|
||||||
|
|
||||||
width: 100%
|
width: 100%
|
||||||
height: 100%
|
height: 100%
|
||||||
|
display: block
|
||||||
|
contain: content
|
||||||
|
position: relative
|
||||||
|
box-sizing: border-box
|
||||||
|
|
||||||
|
div.m3.m3-card-action-area
|
||||||
|
display: block
|
||||||
cursor: pointer
|
cursor: pointer
|
||||||
contain: content
|
contain: content
|
||||||
position: relative
|
position: relative
|
||||||
|
@ -10,9 +30,12 @@ div.m3.m3-card-action-area
|
||||||
transition: box-shadow .2s cubic-bezier(0.2, 0, 0, 1)
|
transition: box-shadow .2s cubic-bezier(0.2, 0, 0, 1)
|
||||||
|
|
||||||
& > div.m3.m3-card-action-area-content
|
& > div.m3.m3-card-action-area-content
|
||||||
position: absolute
|
top: 0
|
||||||
|
width: 100%
|
||||||
|
position: relative
|
||||||
|
|
||||||
& > span.m3.m3-card-state-layer
|
& > span.m3:is(.m3-card-state-layer, .m3-ripple-domain)
|
||||||
|
top: 0
|
||||||
width: 100%
|
width: 100%
|
||||||
height: 100%
|
height: 100%
|
||||||
position: absolute
|
position: absolute
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue