From 4f768afe543bbf9e1857c41d03804f8e37ab3512 Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Fri, 29 Sep 2023 21:29:45 +0200 Subject: refactor(components): rewrite List component * change `items` prop to children * replace `kind` prop with `isHierarchical`, `isOrdered` & `isInline` props * add `hideMarker` prop * add `spacing` prop to control item spacing * move lists styles to Sass placeholders to avoid repeats because of headless WordPress --- src/components/atoms/lists/list/list.tsx | 150 +++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 src/components/atoms/lists/list/list.tsx (limited to 'src/components/atoms/lists/list/list.tsx') diff --git a/src/components/atoms/lists/list/list.tsx b/src/components/atoms/lists/list/list.tsx new file mode 100644 index 0000000..6e58433 --- /dev/null +++ b/src/components/atoms/lists/list/list.tsx @@ -0,0 +1,150 @@ +import { + forwardRef, + type CSSProperties, + type HTMLAttributes, + type OlHTMLAttributes, + type ReactNode, + type ForwardedRef, +} from 'react'; +import type { Spacing } from '../../../../types'; +import styles from './list.module.scss'; + +type OrderedListProps = Omit, 'children'>; + +type UnorderedListProps = Omit, 'children'>; + +type BaseListProps = O extends true + ? OrderedListProps + : H extends true + ? OrderedListProps + : UnorderedListProps; + +type AdditionalProps = { + /** + * An array of list items. + */ + children: ReactNode; + /** + * Should the items marker be hidden? + * + * @default false + */ + hideMarker?: boolean; + /** + * Should the list be ordered and hierarchical? + * + * @default false + */ + isHierarchical?: H; + /** + * Should the list be inlined? + * + * @default false + */ + isInline?: boolean; + /** + * Should the list be ordered? + * + * @default false + */ + isOrdered?: O; + /** + * Define the spacing between list items. + * + * @default null + */ + spacing?: Spacing | null; +}; + +type BuildClassNameConfig = Pick< + BaseListProps, + 'className' +> & + Pick< + AdditionalProps, + 'hideMarker' | 'isHierarchical' | 'isInline' | 'isOrdered' + >; + +const buildClassName = ({ + className = '', + hideMarker, + isHierarchical, + isInline, + isOrdered, +}: BuildClassNameConfig) => { + const orderedClassName = isHierarchical + ? 'list--hierarchical' + : 'list--ordered'; + const classNames: string[] = [ + isHierarchical || isOrdered ? orderedClassName : 'list--unordered', + isInline ? 'list--inline' : 'list--stack', + hideMarker ? 'list--no-marker' : 'list--has-marker', + className, + ].map((key) => styles[key]); + + if (className) classNames.push(className); + + return classNames.join(' '); +}; + +export type ListProps = BaseListProps< + O, + H +> & + AdditionalProps; + +const ListWithRef = ( + { + className, + children, + hideMarker = false, + isHierarchical, + isInline = false, + isOrdered, + spacing = null, + style, + ...props + }: ListProps, + ref: ForwardedRef< + O extends true + ? HTMLOListElement + : H extends true + ? HTMLOListElement + : HTMLUListElement + > +) => { + const itemSpacing = spacing === null ? 0 : `var(--spacing-${spacing})`; + const listClass = buildClassName({ + className, + hideMarker, + isHierarchical, + isInline, + isOrdered, + }); + const listStyles = { + ...style, + '--itemSpacing': itemSpacing, + } as CSSProperties; + + return isHierarchical || isOrdered ? ( +
    } + style={listStyles} + > + {children} +
+ ) : ( +
    + {children} +
+ ); +}; + +/** + * List component + * + * Render either an ordered or an unordered list. + */ +export const List = forwardRef(ListWithRef); -- cgit v1.2.3