diff options
| author | Armand Philippot <git@armandphilippot.com> | 2023-09-29 21:29:45 +0200 | 
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2023-11-11 18:14:41 +0100 | 
| commit | 4f768afe543bbf9e1857c41d03804f8e37ab3512 (patch) | |
| tree | d751219a147688b5665c51db3c8dbdca1f1345ee /src/components/atoms/lists/list/list.tsx | |
| parent | 9128c224c65f8f2a172b22a443ccb4573c7acd90 (diff) | |
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
Diffstat (limited to 'src/components/atoms/lists/list/list.tsx')
| -rw-r--r-- | src/components/atoms/lists/list/list.tsx | 150 | 
1 files changed, 150 insertions, 0 deletions
| 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<OlHTMLAttributes<HTMLOListElement>, 'children'>; + +type UnorderedListProps = Omit<HTMLAttributes<HTMLUListElement>, 'children'>; + +type BaseListProps<O extends boolean, H extends boolean> = O extends true +  ? OrderedListProps +  : H extends true +  ? OrderedListProps +  : UnorderedListProps; + +type AdditionalProps<O extends boolean, H extends boolean> = { +  /** +   * 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<O extends boolean, H extends boolean> = Pick< +  BaseListProps<O, H>, +  'className' +> & +  Pick< +    AdditionalProps<O, H>, +    'hideMarker' | 'isHierarchical' | 'isInline' | 'isOrdered' +  >; + +const buildClassName = <O extends boolean, H extends boolean>({ +  className = '', +  hideMarker, +  isHierarchical, +  isInline, +  isOrdered, +}: BuildClassNameConfig<O, H>) => { +  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<O extends boolean, H extends boolean> = BaseListProps< +  O, +  H +> & +  AdditionalProps<O, H>; + +const ListWithRef = <O extends boolean, H extends boolean>( +  { +    className, +    children, +    hideMarker = false, +    isHierarchical, +    isInline = false, +    isOrdered, +    spacing = null, +    style, +    ...props +  }: ListProps<O, H>, +  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 ? ( +    <ol +      {...props} +      className={listClass} +      ref={ref as ForwardedRef<HTMLOListElement>} +      style={listStyles} +    > +      {children} +    </ol> +  ) : ( +    <ul {...props} className={listClass} ref={ref} style={listStyles}> +      {children} +    </ul> +  ); +}; + +/** + * List component + * + * Render either an ordered or an unordered list. + */ +export const List = forwardRef(ListWithRef); | 
