ADDED: Card sub-components

TODO: Complete styles for card sub-components and section button
This commit is contained in:
doryan04 2024-02-03 23:52:25 +04:00
parent 80702813f4
commit d0a34e7a1f
22 changed files with 1239 additions and 873 deletions

View File

@ -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

10
next.config.js Normal file
View File

@ -0,0 +1,10 @@
module.exports = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: '**',
},
],
},
};

View File

@ -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}

View File

@ -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}

View File

@ -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'}>

View File

@ -1,7 +0,0 @@
import React from 'react';
export function CardActions(props) {
return (
<div></div>
);
}

View File

@ -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>
);
},
);

View File

@ -1,7 +0,0 @@
import React from 'react';
export function CardContent(props) {
return (
<div></div>
);
}

View File

@ -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>
);
});

View File

@ -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>
); );
} });

View File

@ -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: ...props,
return React.createElement(type, { className: extraClassStyles,
...props, ref: ref,
className: classes, },
ref: ref, props.children,
}); );
}
}, },
); );

View File

@ -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}
> >

View File

@ -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;

View File

@ -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>
); );

View File

@ -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}
/> />
); );

View File

@ -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}

View File

@ -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'}>

View File

@ -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>

View File

@ -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