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 testImage1 from './test-images/test-image-1.jpg';
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 { Button } from '../src/primitive-components/button-components/button/button';
const cardStyles = { width: '256px', aspectRatio: 1 };
import { CardHeader } from '../src/primitive-components/card/card-header';
import { CardBody } from '../src/primitive-components/card/card-body';
import { CardFooter } from '../src/primitive-components/card/card-footer';
export default function Page() {
return (
@ -17,17 +20,36 @@ export default function Page() {
padding: '8px',
}}
>
<div style={{ display: 'flex', gap: '8px' }}>
<Card style={cardStyles} variant={'filled'}>
<CardActionArea>
<div style={{ display: 'flex', gap: '8px', maxWidth: 512 }}>
<Card variant={'outlined'}>
<CardHeader>
<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>
<div>
<CardFooter>
<Button>label</Button>
</div>
</CardFooter>
</Card>
<Card style={cardStyles} variant={'outlined'} />
<Card style={cardStyles} variant={'elevated'} />
</div>
</div>
</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(),
events = useRippleEffect(ripplesRef, setIsActive);
const classes =
const extraClassStyles =
`m3${isActive ? ' is-active' : ''} ${props.className ?? ''}`.trimEnd();
return (
<button
{...props}
{...events}
className={classes}
className={extraClassStyles}
disabled={props.disabled}
id={buttonId}
ref={ref}

View File

@ -30,11 +30,11 @@ export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
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({
state: classes,
state: extraClassStyles,
icon: icon,
});
};
@ -60,7 +60,7 @@ export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
<ButtonLayout
{...props}
centralRipple={centralRipple}
className={classes}
className={extraClassStyles}
disabled={disabled}
onClick={callback}
ref={buttonRef}

View File

@ -14,14 +14,14 @@ export const CardActionArea = forwardRef<HTMLDivElement, CardActionAreaProps>(
setIsActive,
ripples,
);
const classes =
const extraClassStyles =
`m3 m3-card-action-area${isActive ? ' is-active' : ''} ${props.className ?? ''}`.trimEnd();
return (
<div
{...props}
{...CardActionAreaEvents}
className={classes}
className={extraClassStyles}
ref={ref}
>
<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 (
<div></div>
<header {...props} className={extraClassStyles} ref={ref}>
{props.children}
</header>
);
}
});

View File

@ -1,33 +1,19 @@
import Image from 'next/image';
import React, { forwardRef } from 'react';
import { createElement, forwardRef } from 'react';
import { CardMediaProps, CardMediaType } from './card.types';
export const CardMedia = forwardRef<CardMediaType, CardMediaProps>(
(
{
alt,
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();
({ type, rounded = true, className = '', ...props }, ref) => {
const extraClassStyles =
`m3 m3-card-media${rounded ? ' m3-rounded' : ''} ${className}`.trimEnd();
switch (type) {
case 'img':
return <Image alt={alt} quality={quality} src={src} />;
default:
return React.createElement(type, {
return createElement(
type,
{
...props,
className: classes,
className: extraClassStyles,
ref: ref,
});
}
},
props.children,
);
},
);

View File

@ -4,12 +4,12 @@ import { Container } from '../container/container';
export const Card = forwardRef<HTMLDivElement, CardProps>(
({ variant = 'filled', ...props }, ref) => {
const classes = `m3-card m3-card-${variant} ${props.className ?? ''}`;
const extraClassStyles = `m3-card m3-card-${variant} ${props.className ?? ''}`;
return (
<Container
{...props}
className={classes}
className={extraClassStyles}
ref={ref}
variant={variant}
>

View File

@ -1,26 +1,22 @@
import { HTMLAttributes } from 'react';
import { HTMLProps } from 'react';
import { ContainerProps } from '../container/container.types';
import { RipplePropsForComponents } from '../ripple/ripple.types';
export interface CardProps extends ContainerProps {}
export interface CardActionAreaProps
extends RipplePropsForComponents<HTMLDivElement> {
ripples?: boolean;
}
export interface CardProps extends ContainerProps {}
export interface CardMediaProps extends HTMLAttributes<CardMediaType> {
alt: string;
src: string;
quality?: number;
export interface CardMediaProps extends HTMLProps<CardMediaType> {
rounded?: boolean;
preview?: boolean;
type: 'img' | 'video' | 'picture' | 'iframe' | 'audio';
}
export type CardMediaType = HTMLImageElement &
HTMLVideoElement &
HTMLPictureElement &
HTMLMediaElement &
HTMLIFrameElement &
HTMLAudioElement;
export type CardMediaType =
| HTMLImageElement
| HTMLVideoElement
| HTMLPictureElement
| HTMLIFrameElement
| HTMLAudioElement;

View File

@ -3,11 +3,11 @@ import { ContainerProps } from './container.types';
export const Container = forwardRef<HTMLDivElement, ContainerProps>(
({ variant = 'filled', ...props }, ref) => {
const classes =
const extraClassStyles =
`m3 m3-container m3-container-${variant} ${props.className ?? ''}`.trimEnd();
return (
<div {...props} className={classes} ref={ref}>
<div {...props} className={extraClassStyles} ref={ref}>
{props.children}
</div>
);

View File

@ -21,7 +21,7 @@ export const CheckBoxLayout = forwardRef<HTMLInputElement, CheckboxLayoutProps>(
const classesType = typeInput || type;
const classes =
const extraClassStyles =
props.className !== undefined
? `m3 m3-${type} ${props.className}`
: `m3 m3-${classesType}`;
@ -30,7 +30,7 @@ export const CheckBoxLayout = forwardRef<HTMLInputElement, CheckboxLayoutProps>(
<input
ref={checkboxRef}
{...props}
className={classes.trimEnd()}
className={extraClassStyles.trimEnd()}
type={type}
/>
);

View File

@ -26,7 +26,7 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
checkboxRef = useRef(null),
events = useRippleEffect(ripplesRef, setIsActive);
const classes =
const extraClassStyles =
`m3 m3-checkbox-label ${isActive === true ? 'visible' : ''}`.trimEnd();
useImperativeHandle(ref, () => checkboxRef.current);
@ -36,7 +36,7 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
}, [checkboxRef.current?.checked]);
return (
<label {...events} className={classes}>
<label {...events} className={extraClassStyles}>
<CheckBoxLayout
{...props}
indeterminate={props.indeterminate}

View File

@ -18,11 +18,11 @@ export const Radio = forwardRef<HTMLInputElement, RadioProps>(
ripplesRef = useRef(null),
events = useRippleEffect(ripplesRef, setIsActive);
const classes =
const extraClassStyles =
`m3 m3-radio ${isActive === true ? 'visible' : ''}`.trimEnd();
return (
<div {...events} className={classes}>
<div {...events} className={extraClassStyles}>
<CheckBoxLayout {...props} ref={ref} type={'radio'} />
<span className={'m3 m3-radio-state-layer'} />
<svg height={'20px'} viewBox={'0 0 20 20'} width={'20px'}>

View File

@ -24,7 +24,7 @@ const RippleArea = forwardRef(
uniqueKey = useRef<number>(0),
uniqueId = useId();
const classes = props.className
const extraClassStyles = props.className
? `m3 m3-ripple-domain ${props.className}`.trimEnd()
: 'm3 m3-ripple-domain';
@ -121,7 +121,7 @@ const RippleArea = forwardRef(
);
return (
<span className={classes} id={uniqueId} ref={rippleDomain}>
<span className={extraClassStyles} id={uniqueId} ref={rippleDomain}>
<rippleAreaContext.Provider value={clicked.current}>
<Ripples>{ripples}</Ripples>
</rippleAreaContext.Provider>

View File

@ -1,8 +1,28 @@
@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%
height: 100%
display: block
contain: content
position: relative
box-sizing: border-box
div.m3.m3-card-action-area
display: block
cursor: pointer
contain: content
position: relative
@ -10,9 +30,12 @@ div.m3.m3-card-action-area
transition: box-shadow .2s cubic-bezier(0.2, 0, 0, 1)
& > 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%
height: 100%
position: absolute

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long