import { ReactElement, ReactNode, useId } from 'react'; import classnames from 'classnames'; import getChildrenToArray from './getChildrenToArray'; import ListText from './Message'; import Box from '../Box'; import { ListProvider, useList } from '../contexts/ListProvider'; import { NestingProvider, useNesting } from '../contexts/NestingProvider'; import Flex from '../Flex'; import styles from '../List.css'; import Text from '../Text'; const UNORDERED_SEQUENCE = ['desc', 'circle']; const ORDERED_SEQUENCE = ['decimal', 'upper-latin', 'lower-latin']; // These sequences are used to style list-style-type in List.Item. Lists can mix ordered and unordered list. These sequences support carrying the item style independently from the nested level. const STYLE_SEQUENCE_UNORDERED = Object.freeze([ ...UNORDERED_SEQUENCE, ...UNORDERED_SEQUENCE, ...UNORDERED_SEQUENCE, ]); const STYLE_SEQUENCE_ORDERED = Object.freeze([...ORDERED_SEQUENCE, ...ORDERED_SEQUENCE]); type ListType = 'bare' | 'ordered' | 'unordered'; type Size = '100' | '200' | '300' | '400' | '500' | '600'; type Props = { /** * Use List.Item to build lists. See [subcomponents](https://gestalt.pinterest.systems/web/list#List.Item). */ children: ReactNode; /** * The label for the list. Be sure to localize the text. See the [label variant](https://gestalt.pinterest.systems/web/list#Text-and-label) for guidance. */ label?: string | ReactElement; /** * Whether the label should be visible or not. If `hidden`, the label is still available for screen reader users, but does not appear visually. See the [accessibility section](https://gestalt.pinterest.systems/web/list#Accessibility) and the [label variant](https://gestalt.pinterest.systems/web/list#Text-and-label) for guidance. */ labelDisplay?: 'visible' | 'hidden'; /** * The spacing used in List. See the [spacing variant](https://gestalt.pinterest.systems/web/list#Spacing) for guidance. */ spacing?: 'regular' | 'condensed'; /** *The sizes are based on our [font-size design tokens](https://gestalt.pinterest.systems/foundations/design_tokens/overview#Font-size). See the [sizes variant](https://gestalt.pinterest.systems/web/text#Sizes) for more details. */ size?: Size; /** * Determines the style of the list. See the [type variant](https://gestalt.pinterest.systems/web/list#Type) to learn more. */ type?: ListType; }; function InternalList({ label, labelDisplay = 'visible', spacing = 'regular', size = '300', type, children, }: Props) { const id = useId(); const { nestedLevel } = useNesting(); const { type: inheritedType, style: inheritedStyle, size: inheritedFontSize } = useList(); const listType = type ?? inheritedType ?? 'unordered'; const hiddenLabel = labelDisplay === 'hidden'; const ListElement = listType === 'ordered' ? 'ol' : 'ul'; const listChildren = getChildrenToArray({ children, filterLevel: 'List' }); const isListParent = nestedLevel === 0; const className = classnames(styles.list, { [styles.bulletList]: type !== 'bare', [styles.parentList]: isListParent, }); // Check if a nested List get a type value different from the inherited one. const didTypeChanged = !!type && inheritedType !== type; // If a nested List type didn't change, slice the inherited style sequence for the type selected so the children have access to the correct sequence left. const newInheritedStyleOl = !didTypeChanged && listType === 'ordered' ? inheritedStyle?.ol.slice(1) : inheritedStyle?.ol; const newInheritedStyleUl = !didTypeChanged && listType === 'unordered' ? inheritedStyle?.ul.slice(1) : inheritedStyle?.ul; const formattedListElement = isListParent ? ( {listChildren} ) : ( {listChildren} ); return ( {label && isListParent ? ( {formattedListElement} ) : ( formattedListElement )} ); } InternalList.displayName = 'InternalList'; export default InternalList;