aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/atoms/lists/list/list.tsx
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2023-09-29 21:29:45 +0200
committerArmand Philippot <git@armandphilippot.com>2023-11-11 18:14:41 +0100
commit4f768afe543bbf9e1857c41d03804f8e37ab3512 (patch)
treed751219a147688b5665c51db3c8dbdca1f1345ee /src/components/atoms/lists/list/list.tsx
parent9128c224c65f8f2a172b22a443ccb4573c7acd90 (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.tsx150
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);