@brycks/core-front 0.3.5 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/data/List/List.cjs +1 -1
- package/dist/components/data/List/List.cjs.map +1 -1
- package/dist/components/data/List/List.js +1 -1
- package/dist/components/data/List/List.js.map +1 -1
- package/dist/components/data/Table/Table.cjs +1 -1
- package/dist/components/data/Table/Table.cjs.map +1 -1
- package/dist/components/data/Table/Table.js +196 -166
- package/dist/components/data/Table/Table.js.map +1 -1
- package/dist/components/data/TreeView/TreeView.cjs +1 -1
- package/dist/components/data/TreeView/TreeView.cjs.map +1 -1
- package/dist/components/data/TreeView/TreeView.js +1 -1
- package/dist/components/data/TreeView/TreeView.js.map +1 -1
- package/dist/components/data.cjs +1 -1
- package/dist/components/data.js +23 -21
- package/dist/components/feedback/Alert/Alert.cjs +1 -1
- package/dist/components/feedback/Alert/Alert.cjs.map +1 -1
- package/dist/components/feedback/Alert/Alert.js +20 -19
- package/dist/components/feedback/Alert/Alert.js.map +1 -1
- package/dist/components/feedback/Banner/Banner.cjs +2 -0
- package/dist/components/feedback/Banner/Banner.cjs.map +1 -0
- package/dist/components/feedback/Banner/Banner.js +77 -0
- package/dist/components/feedback/Banner/Banner.js.map +1 -0
- package/dist/components/feedback/ConfirmDialog/ConfirmDialog.cjs +2 -0
- package/dist/components/feedback/ConfirmDialog/ConfirmDialog.cjs.map +1 -0
- package/dist/components/feedback/ConfirmDialog/ConfirmDialog.js +68 -0
- package/dist/components/feedback/ConfirmDialog/ConfirmDialog.js.map +1 -0
- package/dist/components/feedback/Drawer/Drawer.cjs +2 -2
- package/dist/components/feedback/Drawer/Drawer.cjs.map +1 -1
- package/dist/components/feedback/Drawer/Drawer.js +58 -57
- package/dist/components/feedback/Drawer/Drawer.js.map +1 -1
- package/dist/components/feedback/Modal/Modal.cjs +1 -1
- package/dist/components/feedback/Modal/Modal.cjs.map +1 -1
- package/dist/components/feedback/Modal/Modal.js +101 -91
- package/dist/components/feedback/Modal/Modal.js.map +1 -1
- package/dist/components/feedback/Popover/Popover.cjs +2 -2
- package/dist/components/feedback/Popover/Popover.cjs.map +1 -1
- package/dist/components/feedback/Popover/Popover.js +93 -85
- package/dist/components/feedback/Popover/Popover.js.map +1 -1
- package/dist/components/feedback/Toast/Toast.cjs +2 -2
- package/dist/components/feedback/Toast/Toast.cjs.map +1 -1
- package/dist/components/feedback/Toast/Toast.js +102 -93
- package/dist/components/feedback/Toast/Toast.js.map +1 -1
- package/dist/components/feedback.cjs +1 -1
- package/dist/components/feedback.js +17 -13
- package/dist/components/feedback.js.map +1 -1
- package/dist/components/form/Combobox/Combobox.cjs +2 -2
- package/dist/components/form/Combobox/Combobox.cjs.map +1 -1
- package/dist/components/form/Combobox/Combobox.js +187 -167
- package/dist/components/form/Combobox/Combobox.js.map +1 -1
- package/dist/components/form/DateInput/DateInput.cjs +1 -1
- package/dist/components/form/DateInput/DateInput.cjs.map +1 -1
- package/dist/components/form/DateInput/DateInput.js +2 -2
- package/dist/components/form/DateInput/DateInput.js.map +1 -1
- package/dist/components/form/DateRangePicker/DateRangePicker.cjs +1 -1
- package/dist/components/form/DateRangePicker/DateRangePicker.cjs.map +1 -1
- package/dist/components/form/DateRangePicker/DateRangePicker.js +34 -34
- package/dist/components/form/DateRangePicker/DateRangePicker.js.map +1 -1
- package/dist/components/form/DropZone/DropZone.cjs +1 -1
- package/dist/components/form/DropZone/DropZone.cjs.map +1 -1
- package/dist/components/form/DropZone/DropZone.js +10 -10
- package/dist/components/form/DropZone/DropZone.js.map +1 -1
- package/dist/components/form/FileInput/FileInput.cjs +1 -1
- package/dist/components/form/FileInput/FileInput.cjs.map +1 -1
- package/dist/components/form/FileInput/FileInput.js +1 -1
- package/dist/components/form/FileInput/FileInput.js.map +1 -1
- package/dist/components/form/FormCard/FormCard.cjs +1 -1
- package/dist/components/form/FormCard/FormCard.cjs.map +1 -1
- package/dist/components/form/FormCard/FormCard.js +9 -9
- package/dist/components/form/FormCard/FormCard.js.map +1 -1
- package/dist/components/form/FormField/FormField.cjs +1 -1
- package/dist/components/form/FormField/FormField.cjs.map +1 -1
- package/dist/components/form/FormField/FormField.js +67 -55
- package/dist/components/form/FormField/FormField.js.map +1 -1
- package/dist/components/form/MultiSelect/MultiSelect.cjs +1 -1
- package/dist/components/form/MultiSelect/MultiSelect.cjs.map +1 -1
- package/dist/components/form/MultiSelect/MultiSelect.js +2 -3
- package/dist/components/form/MultiSelect/MultiSelect.js.map +1 -1
- package/dist/components/form/Radio/Radio.cjs +1 -1
- package/dist/components/form/Radio/Radio.cjs.map +1 -1
- package/dist/components/form/Radio/Radio.js +20 -20
- package/dist/components/form/Radio/Radio.js.map +1 -1
- package/dist/components/form/SearchInput/SearchInput.cjs +2 -0
- package/dist/components/form/SearchInput/SearchInput.cjs.map +1 -0
- package/dist/components/form/SearchInput/SearchInput.js +68 -0
- package/dist/components/form/SearchInput/SearchInput.js.map +1 -0
- package/dist/components/form/TagInput/TagInput.cjs +1 -1
- package/dist/components/form/TagInput/TagInput.cjs.map +1 -1
- package/dist/components/form/TagInput/TagInput.js +104 -102
- package/dist/components/form/TagInput/TagInput.js.map +1 -1
- package/dist/components/form/TimePicker/TimePicker.cjs +1 -1
- package/dist/components/form/TimePicker/TimePicker.cjs.map +1 -1
- package/dist/components/form/TimePicker/TimePicker.js +29 -30
- package/dist/components/form/TimePicker/TimePicker.js.map +1 -1
- package/dist/components/form.cjs +1 -1
- package/dist/components/form.js +15 -13
- package/dist/components/form.js.map +1 -1
- package/dist/components/layout/Card/Card.cjs +1 -1
- package/dist/components/layout/Card/Card.cjs.map +1 -1
- package/dist/components/layout/Card/Card.js +7 -5
- package/dist/components/layout/Card/Card.js.map +1 -1
- package/dist/components/layout/Collapse/Collapse.cjs.map +1 -1
- package/dist/components/layout/Collapse/Collapse.js.map +1 -1
- package/dist/components/media/Image/Image.cjs +1 -1
- package/dist/components/media/Image/Image.cjs.map +1 -1
- package/dist/components/media/Image/Image.js +9 -9
- package/dist/components/media/Image/Image.js.map +1 -1
- package/dist/components/media/ImageThumbnail/ImageThumbnail.cjs +1 -1
- package/dist/components/media/ImageThumbnail/ImageThumbnail.cjs.map +1 -1
- package/dist/components/media/ImageThumbnail/ImageThumbnail.js +6 -6
- package/dist/components/media/ImageThumbnail/ImageThumbnail.js.map +1 -1
- package/dist/components/navigation/Accordion/Accordion.cjs +1 -1
- package/dist/components/navigation/Accordion/Accordion.cjs.map +1 -1
- package/dist/components/navigation/Accordion/Accordion.js +0 -1
- package/dist/components/navigation/Accordion/Accordion.js.map +1 -1
- package/dist/components/navigation/Menu/Menu.cjs +1 -1
- package/dist/components/navigation/Menu/Menu.cjs.map +1 -1
- package/dist/components/navigation/Menu/Menu.js +87 -86
- package/dist/components/navigation/Menu/Menu.js.map +1 -1
- package/dist/components/navigation/Pagination/Pagination.cjs +1 -1
- package/dist/components/navigation/Pagination/Pagination.cjs.map +1 -1
- package/dist/components/navigation/Pagination/Pagination.js +107 -99
- package/dist/components/navigation/Pagination/Pagination.js.map +1 -1
- package/dist/components/navigation/SegmentedControl/SegmentedControl.cjs +2 -0
- package/dist/components/navigation/SegmentedControl/SegmentedControl.cjs.map +1 -0
- package/dist/components/navigation/SegmentedControl/SegmentedControl.js +90 -0
- package/dist/components/navigation/SegmentedControl/SegmentedControl.js.map +1 -0
- package/dist/components/navigation.cjs +1 -1
- package/dist/components/navigation.js +25 -23
- package/dist/components/navigation.js.map +1 -1
- package/dist/components/utility/Avatar/Avatar.cjs +1 -1
- package/dist/components/utility/Avatar/Avatar.cjs.map +1 -1
- package/dist/components/utility/Avatar/Avatar.js +18 -18
- package/dist/components/utility/Avatar/Avatar.js.map +1 -1
- package/dist/components/utility/Badge/Badge.cjs +1 -1
- package/dist/components/utility/Badge/Badge.cjs.map +1 -1
- package/dist/components/utility/Badge/Badge.js +31 -23
- package/dist/components/utility/Badge/Badge.js.map +1 -1
- package/dist/components/utility/Kbd/Kbd.cjs +2 -0
- package/dist/components/utility/Kbd/Kbd.cjs.map +1 -0
- package/dist/components/utility/Kbd/Kbd.js +20 -0
- package/dist/components/utility/Kbd/Kbd.js.map +1 -0
- package/dist/components/utility/Logo/Logo.cjs +1 -1
- package/dist/components/utility/Logo/Logo.cjs.map +1 -1
- package/dist/components/utility/Logo/Logo.js +21 -21
- package/dist/components/utility/Logo/Logo.js.map +1 -1
- package/dist/components/utility.cjs +1 -1
- package/dist/components/utility.js +9 -7
- package/dist/components/utility.js.map +1 -1
- package/dist/data.d.ts +34 -1
- package/dist/design-system/themes/ThemeProvider.cjs +1 -1
- package/dist/design-system/themes/ThemeProvider.cjs.map +1 -1
- package/dist/design-system/themes/ThemeProvider.js +46 -46
- package/dist/design-system/themes/ThemeProvider.js.map +1 -1
- package/dist/design-system/tokens/zIndex.cjs +1 -1
- package/dist/design-system/tokens/zIndex.cjs.map +1 -1
- package/dist/design-system/tokens/zIndex.js +2 -0
- package/dist/design-system/tokens/zIndex.js.map +1 -1
- package/dist/feedback.d.ts +75 -2
- package/dist/form.d.ts +41 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +213 -3
- package/dist/index.js +177 -165
- package/dist/index.js.map +1 -1
- package/dist/navigation.d.ts +58 -0
- package/dist/styles.css +1 -1
- package/dist/tokens.d.ts +2 -0
- package/dist/utility.d.ts +19 -0
- package/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Accordion.js","sources":["../../../../src/components/navigation/Accordion/Accordion.tsx"],"sourcesContent":["/**\n * Accordion Component\n *\n * Collapsible content panels for organizing information.\n * Supports single or multiple expanded panels.\n */\n\nimport {\n forwardRef,\n useState,\n useCallback,\n createContext,\n useContext,\n type CSSProperties,\n type ReactNode,\n type HTMLAttributes,\n} from 'react'\nimport { cx } from '../../../utils/styles'\n\nexport type AccordionVariant = 'default' | 'bordered' | 'separated'\n\ninterface AccordionContextValue {\n expandedItems: string[]\n toggleItem: (value: string) => void\n allowMultiple: boolean\n variant: AccordionVariant\n}\n\nconst AccordionContext = createContext<AccordionContextValue | null>(null)\n\nfunction useAccordionContext() {\n const context = useContext(AccordionContext)\n if (!context) {\n throw new Error('AccordionItem must be used within an Accordion')\n }\n return context\n}\n\nexport interface AccordionProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {\n /** Expanded items (controlled) */\n value?: string[]\n /** Default expanded items (uncontrolled) */\n defaultValue?: string[]\n /** Callback when items change */\n onChange?: (value: string[]) => void\n /** Allow multiple items to be expanded */\n allowMultiple?: boolean\n /** Accordion variant */\n variant?: AccordionVariant\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nexport const Accordion = forwardRef<HTMLDivElement, AccordionProps>(function Accordion(\n {\n value: controlledValue,\n defaultValue = [],\n onChange,\n allowMultiple = false,\n variant = 'default',\n className,\n style,\n children,\n testId,\n ...props\n },\n ref\n) {\n const [internalValue, setInternalValue] = useState(defaultValue)\n const expandedItems = controlledValue ?? internalValue\n\n const toggleItem = useCallback(\n (itemValue: string) => {\n const isExpanded = expandedItems.includes(itemValue)\n let newValue: string[]\n\n if (isExpanded) {\n newValue = expandedItems.filter((v) => v !== itemValue)\n } else if (allowMultiple) {\n newValue = [...expandedItems, itemValue]\n } else {\n newValue = [itemValue]\n }\n\n if (controlledValue === undefined) {\n setInternalValue(newValue)\n }\n onChange?.(newValue)\n },\n [expandedItems, allowMultiple, controlledValue, onChange]\n )\n\n const containerStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: variant === 'separated' ? 8 : 0,\n ...style,\n }\n\n return (\n <AccordionContext.Provider value={{ expandedItems, toggleItem, allowMultiple, variant }}>\n <div\n ref={ref}\n className={cx('brycks-accordion', `brycks-accordion--${variant}`, className)}\n style={containerStyle}\n data-testid={testId}\n {...props}\n >\n {children}\n </div>\n </AccordionContext.Provider>\n )\n})\n\nAccordion.displayName = 'Accordion'\n\n// AccordionItem\nexport interface AccordionItemProps extends HTMLAttributes<HTMLDivElement> {\n /** Item value (unique identifier) */\n value: string\n /** Whether the item is disabled */\n disabled?: boolean\n /** Custom class name */\n className?: string\n}\n\ninterface AccordionItemContextValue {\n value: string\n isExpanded: boolean\n disabled: boolean\n}\n\nconst AccordionItemContext = createContext<AccordionItemContextValue | null>(null)\n\nfunction useAccordionItemContext() {\n const context = useContext(AccordionItemContext)\n if (!context) {\n throw new Error('AccordionTrigger/AccordionContent must be used within an AccordionItem')\n }\n return context\n}\n\nexport const AccordionItem = forwardRef<HTMLDivElement, AccordionItemProps>(function AccordionItem(\n { value, disabled = false, className, style, children, ...props },\n ref\n) {\n const { expandedItems, variant } = useAccordionContext()\n const isExpanded = expandedItems.includes(value)\n\n const itemStyle: CSSProperties = {\n borderBottom: variant === 'default' ? '1px solid var(--brycks-border-muted)' : undefined,\n border: variant === 'bordered' || variant === 'separated' ? '1px solid var(--brycks-border-default)' : undefined,\n borderRadius: variant === 'separated' ? 'var(--brycks-radius-lg)' : undefined,\n overflow: 'hidden',\n ...style,\n }\n\n // Remove bottom border for separated variant (each item has full border)\n if (variant === 'bordered' && !isExpanded) {\n itemStyle.borderBottom = '1px solid var(--brycks-border-default)'\n }\n\n return (\n <AccordionItemContext.Provider value={{ value, isExpanded, disabled }}>\n <div\n ref={ref}\n className={cx('brycks-accordion-item', isExpanded && 'brycks-accordion-item--expanded', className)}\n style={itemStyle}\n {...props}\n >\n {children}\n </div>\n </AccordionItemContext.Provider>\n )\n})\n\nAccordionItem.displayName = 'AccordionItem'\n\n// AccordionTrigger\nexport interface AccordionTriggerProps extends HTMLAttributes<HTMLButtonElement> {\n /** Icon to show (replaces default chevron) */\n icon?: ReactNode\n /** Custom class name */\n className?: string\n}\n\nfunction ChevronIcon({ isExpanded }: { isExpanded: boolean }) {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n style={{\n transform: isExpanded ? 'rotate(180deg)' : 'rotate(0deg)',\n transition: 'transform 200ms ease-out',\n }}\n >\n <path\n d=\"M4 6l4 4 4-4\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n )\n}\n\nexport const AccordionTrigger = forwardRef<HTMLButtonElement, AccordionTriggerProps>(function AccordionTrigger(\n { icon, className, style, children, ...props },\n ref\n) {\n const { toggleItem } = useAccordionContext()\n const { value, isExpanded, disabled } = useAccordionItemContext()\n\n const handleClick = () => {\n if (!disabled) {\n toggleItem(value)\n }\n }\n\n const triggerStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n width: '100%',\n padding: '16px',\n fontSize: 15,\n fontWeight: 500,\n color: disabled ? 'var(--brycks-foreground-muted)' : 'var(--brycks-foreground-default)',\n backgroundColor: 'transparent',\n border: 'none',\n cursor: disabled ? 'not-allowed' : 'pointer',\n textAlign: 'left',\n transition: 'background-color 150ms ease-out',\n outline: 'none',\n ...style,\n }\n\n return (\n <button\n ref={ref}\n type=\"button\"\n aria-expanded={isExpanded}\n aria-disabled={disabled}\n disabled={disabled}\n className={cx('brycks-accordion-trigger', className)}\n style={triggerStyle}\n onClick={handleClick}\n onMouseEnter={(e) => {\n if (!disabled) {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n }\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n }}\n {...props}\n >\n {children}\n <span style={{ color: 'var(--brycks-foreground-muted)', flexShrink: 0, marginLeft: 12 }}>\n {icon ?? <ChevronIcon isExpanded={isExpanded} />}\n </span>\n </button>\n )\n})\n\nAccordionTrigger.displayName = 'AccordionTrigger'\n\n// AccordionContent\nexport interface AccordionContentProps extends HTMLAttributes<HTMLDivElement> {\n /** Custom class name */\n className?: string\n}\n\nexport const AccordionContent = forwardRef<HTMLDivElement, AccordionContentProps>(function AccordionContent(\n { className, style, children, ...props },\n ref\n) {\n const { isExpanded } = useAccordionItemContext()\n\n const contentStyle: CSSProperties = {\n overflow: 'hidden',\n transition: 'max-height 200ms ease-out, opacity 200ms ease-out',\n maxHeight: isExpanded ? '1000px' : '0',\n opacity: isExpanded ? 1 : 0,\n }\n\n const innerStyle: CSSProperties = {\n padding: '0 16px 16px 16px',\n color: 'var(--brycks-foreground-muted)',\n fontSize: 14,\n lineHeight: 1.6,\n ...style,\n }\n\n return (\n <div\n ref={ref}\n role=\"region\"\n aria-hidden={!isExpanded}\n className={cx('brycks-accordion-content', className)}\n style={contentStyle}\n {...props}\n >\n <div style={innerStyle}>{children}</div>\n </div>\n )\n})\n\nAccordionContent.displayName = 'AccordionContent'\n"],"names":["AccordionContext","createContext","useAccordionContext","context","useContext","Accordion","forwardRef","controlledValue","defaultValue","onChange","allowMultiple","variant","className","style","children","testId","props","ref","internalValue","setInternalValue","useState","expandedItems","toggleItem","useCallback","itemValue","isExpanded","newValue","v","containerStyle","jsx","cx","AccordionItemContext","useAccordionItemContext","AccordionItem","value","disabled","itemStyle","ChevronIcon","AccordionTrigger","icon","handleClick","triggerStyle","jsxs","e","AccordionContent","contentStyle","innerStyle"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Accordion.js","sources":["../../../../src/components/navigation/Accordion/Accordion.tsx"],"sourcesContent":["/**\n * Accordion Component\n *\n * Collapsible content panels for organizing information.\n * Supports single or multiple expanded panels.\n */\n\nimport {\n forwardRef,\n useState,\n useCallback,\n createContext,\n useContext,\n type CSSProperties,\n type ReactNode,\n type HTMLAttributes,\n} from 'react'\nimport { cx } from '../../../utils/styles'\n\nexport type AccordionVariant = 'default' | 'bordered' | 'separated'\n\ninterface AccordionContextValue {\n expandedItems: string[]\n toggleItem: (value: string) => void\n allowMultiple: boolean\n variant: AccordionVariant\n}\n\nconst AccordionContext = createContext<AccordionContextValue | null>(null)\n\nfunction useAccordionContext() {\n const context = useContext(AccordionContext)\n if (!context) {\n throw new Error('AccordionItem must be used within an Accordion')\n }\n return context\n}\n\nexport interface AccordionProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {\n /** Expanded items (controlled) */\n value?: string[]\n /** Default expanded items (uncontrolled) */\n defaultValue?: string[]\n /** Callback when items change */\n onChange?: (value: string[]) => void\n /** Allow multiple items to be expanded */\n allowMultiple?: boolean\n /** Accordion variant */\n variant?: AccordionVariant\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nexport const Accordion = forwardRef<HTMLDivElement, AccordionProps>(function Accordion(\n {\n value: controlledValue,\n defaultValue = [],\n onChange,\n allowMultiple = false,\n variant = 'default',\n className,\n style,\n children,\n testId,\n ...props\n },\n ref\n) {\n const [internalValue, setInternalValue] = useState(defaultValue)\n const expandedItems = controlledValue ?? internalValue\n\n const toggleItem = useCallback(\n (itemValue: string) => {\n const isExpanded = expandedItems.includes(itemValue)\n let newValue: string[]\n\n if (isExpanded) {\n newValue = expandedItems.filter((v) => v !== itemValue)\n } else if (allowMultiple) {\n newValue = [...expandedItems, itemValue]\n } else {\n newValue = [itemValue]\n }\n\n if (controlledValue === undefined) {\n setInternalValue(newValue)\n }\n onChange?.(newValue)\n },\n [expandedItems, allowMultiple, controlledValue, onChange]\n )\n\n const containerStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: variant === 'separated' ? 8 : 0,\n ...style,\n }\n\n return (\n <AccordionContext.Provider value={{ expandedItems, toggleItem, allowMultiple, variant }}>\n <div\n ref={ref}\n className={cx('brycks-accordion', `brycks-accordion--${variant}`, className)}\n style={containerStyle}\n data-testid={testId}\n {...props}\n >\n {children}\n </div>\n </AccordionContext.Provider>\n )\n})\n\nAccordion.displayName = 'Accordion'\n\n// AccordionItem\nexport interface AccordionItemProps extends HTMLAttributes<HTMLDivElement> {\n /** Item value (unique identifier) */\n value: string\n /** Whether the item is disabled */\n disabled?: boolean\n /** Custom class name */\n className?: string\n}\n\ninterface AccordionItemContextValue {\n value: string\n isExpanded: boolean\n disabled: boolean\n}\n\nconst AccordionItemContext = createContext<AccordionItemContextValue | null>(null)\n\nfunction useAccordionItemContext() {\n const context = useContext(AccordionItemContext)\n if (!context) {\n throw new Error('AccordionTrigger/AccordionContent must be used within an AccordionItem')\n }\n return context\n}\n\nexport const AccordionItem = forwardRef<HTMLDivElement, AccordionItemProps>(function AccordionItem(\n { value, disabled = false, className, style, children, ...props },\n ref\n) {\n const { expandedItems, variant } = useAccordionContext()\n const isExpanded = expandedItems.includes(value)\n\n const itemStyle: CSSProperties = {\n borderBottom: variant === 'default' ? '1px solid var(--brycks-border-muted)' : undefined,\n border: variant === 'bordered' || variant === 'separated' ? '1px solid var(--brycks-border-default)' : undefined,\n borderRadius: variant === 'separated' ? 'var(--brycks-radius-lg)' : undefined,\n overflow: 'hidden',\n ...style,\n }\n\n // Remove bottom border for separated variant (each item has full border)\n if (variant === 'bordered' && !isExpanded) {\n itemStyle.borderBottom = '1px solid var(--brycks-border-default)'\n }\n\n return (\n <AccordionItemContext.Provider value={{ value, isExpanded, disabled }}>\n <div\n ref={ref}\n className={cx('brycks-accordion-item', isExpanded && 'brycks-accordion-item--expanded', className)}\n style={itemStyle}\n {...props}\n >\n {children}\n </div>\n </AccordionItemContext.Provider>\n )\n})\n\nAccordionItem.displayName = 'AccordionItem'\n\n// AccordionTrigger\nexport interface AccordionTriggerProps extends HTMLAttributes<HTMLButtonElement> {\n /** Icon to show (replaces default chevron) */\n icon?: ReactNode\n /** Custom class name */\n className?: string\n}\n\nfunction ChevronIcon({ isExpanded }: { isExpanded: boolean }) {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n style={{\n transform: isExpanded ? 'rotate(180deg)' : 'rotate(0deg)',\n transition: 'transform 200ms ease-out',\n }}\n >\n <path\n d=\"M4 6l4 4 4-4\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n )\n}\n\nexport const AccordionTrigger = forwardRef<HTMLButtonElement, AccordionTriggerProps>(function AccordionTrigger(\n { icon, className, style, children, ...props },\n ref\n) {\n const { toggleItem } = useAccordionContext()\n const { value, isExpanded, disabled } = useAccordionItemContext()\n\n const handleClick = () => {\n if (!disabled) {\n toggleItem(value)\n }\n }\n\n const triggerStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n width: '100%',\n padding: '16px',\n fontSize: 15,\n fontWeight: 500,\n color: disabled ? 'var(--brycks-foreground-muted)' : 'var(--brycks-foreground-default)',\n backgroundColor: 'transparent',\n border: 'none',\n cursor: disabled ? 'not-allowed' : 'pointer',\n textAlign: 'left',\n transition: 'background-color 150ms ease-out',\n ...style,\n }\n\n return (\n <button\n ref={ref}\n type=\"button\"\n aria-expanded={isExpanded}\n aria-disabled={disabled}\n disabled={disabled}\n className={cx('brycks-accordion-trigger', className)}\n style={triggerStyle}\n onClick={handleClick}\n onMouseEnter={(e) => {\n if (!disabled) {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n }\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n }}\n {...props}\n >\n {children}\n <span style={{ color: 'var(--brycks-foreground-muted)', flexShrink: 0, marginLeft: 12 }}>\n {icon ?? <ChevronIcon isExpanded={isExpanded} />}\n </span>\n </button>\n )\n})\n\nAccordionTrigger.displayName = 'AccordionTrigger'\n\n// AccordionContent\nexport interface AccordionContentProps extends HTMLAttributes<HTMLDivElement> {\n /** Custom class name */\n className?: string\n}\n\nexport const AccordionContent = forwardRef<HTMLDivElement, AccordionContentProps>(function AccordionContent(\n { className, style, children, ...props },\n ref\n) {\n const { isExpanded } = useAccordionItemContext()\n\n const contentStyle: CSSProperties = {\n overflow: 'hidden',\n transition: 'max-height 200ms ease-out, opacity 200ms ease-out',\n maxHeight: isExpanded ? '1000px' : '0',\n opacity: isExpanded ? 1 : 0,\n }\n\n const innerStyle: CSSProperties = {\n padding: '0 16px 16px 16px',\n color: 'var(--brycks-foreground-muted)',\n fontSize: 14,\n lineHeight: 1.6,\n ...style,\n }\n\n return (\n <div\n ref={ref}\n role=\"region\"\n aria-hidden={!isExpanded}\n className={cx('brycks-accordion-content', className)}\n style={contentStyle}\n {...props}\n >\n <div style={innerStyle}>{children}</div>\n </div>\n )\n})\n\nAccordionContent.displayName = 'AccordionContent'\n"],"names":["AccordionContext","createContext","useAccordionContext","context","useContext","Accordion","forwardRef","controlledValue","defaultValue","onChange","allowMultiple","variant","className","style","children","testId","props","ref","internalValue","setInternalValue","useState","expandedItems","toggleItem","useCallback","itemValue","isExpanded","newValue","v","containerStyle","jsx","cx","AccordionItemContext","useAccordionItemContext","AccordionItem","value","disabled","itemStyle","ChevronIcon","AccordionTrigger","icon","handleClick","triggerStyle","jsxs","e","AccordionContent","contentStyle","innerStyle"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("react/jsx-runtime"),r=require("react"),j=require("../../../utils/styles.cjs"),i=require("../../../design-system/primitives/sizing.cjs"),m=require("../../../design-system/tokens/spacing.cjs"),N=require("../../../design-system/tokens/motion.cjs"),C=require("../../../design-system/tokens/typography.cjs"),A=r.createContext(null);function G(){const g=r.useContext(A);if(!g)throw new Error("Menu components must be used within a Menu");return g}const H=r.forwardRef(function({activeItem:t,onChange:l,size:c="md",collapsed:s=!1,className:p,style:S,children:v,testId:y,...k},x){const[D,M]=r.useState(null),[h,o]=r.useState(-1),b=r.useRef([]),z=r.useRef(null),R=t!==void 0?t:D,a=e=>{t===void 0&&M(e),l?.(e)},d=r.useCallback(e=>{const n=b.current.indexOf(e);return n===-1?(b.current.push(e),b.current.length-1):n},[]),I=r.useCallback(e=>{const n=z.current?.querySelectorAll('[role="menuitem"]:not(:disabled)');if(!n?.length)return;const w=h>=0?h:0;switch(e.key){case"ArrowDown":{e.preventDefault();const f=(w+1)%n.length;o(f),n[f]?.focus();break}case"ArrowUp":{e.preventDefault();const f=(w-1+n.length)%n.length;o(f),n[f]?.focus();break}case"Home":e.preventDefault(),o(0),n[0]?.focus();break;case"End":e.preventDefault(),o(n.length-1),n[n.length-1]?.focus();break}},[h]),q={display:"flex",flexDirection:"column",gap:m.spacing[.5],padding:m.spacing[2],...S};return u.jsx(A.Provider,{value:{size:c,activeItem:R,setActiveItem:a,collapsed:s,focusedIndex:h,setFocusedIndex:o,registerItem:d},children:u.jsx("nav",{ref:e=>{z.current=e,typeof x=="function"?x(e):x&&(x.current=e)},role:"menu",className:j.cx("brycks-menu",`brycks-menu--${c}`,s&&"brycks-menu--collapsed",p),style:q,onKeyDown:I,"data-testid":y,...k,children:v})})});H.displayName="Menu";const O={sm:{height:i.componentHeights.sm,fontSize:C.fontSizes.base-1,iconSize:i.iconSizes.sm,padding:i.componentGap.lg},md:{height:i.componentHeights.md,fontSize:C.fontSizes.base,iconSize:i.iconSizes.md-2,padding:i.componentPaddingX.sm},lg:{height:i.componentHeights.lg,fontSize:C.fontSizes.md,iconSize:i.iconSizes.md,padding:m.spacing[3.5]}},T=r.forwardRef(function({value:t,icon:l,badge:c,disabled:s=!1,onClick:p,className:S,style:v,children:y,...k},x){const{size:D,activeItem:M,setActiveItem:h,collapsed:o,focusedIndex:b,setFocusedIndex:z,registerItem:R}=G(),a=M===t,d=O[D],I=R(t),q=()=>{s||(h(t),p?.(t))},e=()=>{z(I)},n={display:"flex",alignItems:"center",gap:i.componentGap.lg,width:"100%",minHeight:d.height,padding:o?d.padding:`0 ${d.padding}px`,fontSize:d.fontSize,fontWeight:a?600:500,color:s?"var(--brycks-foreground-disabled)":a?"var(--brycks-primary-default)":"var(--brycks-foreground-default)",backgroundColor:a?"var(--brycks-primary-muted)":"transparent",border:"none",borderRadius:"var(--brycks-radius-md)",cursor:s?"not-allowed":"pointer",transition:`all ${N.durations.quick}ms ${N.easings.easeOut}`,textAlign:"left",justifyContent:o?"center":"flex-start",...v},w={width:d.iconSize,height:d.iconSize,flexShrink:0,display:"flex",alignItems:"center",justifyContent:"center"},f={marginLeft:"auto"},E=b>=0?b===I:M?a:I===0;return u.jsxs("button",{ref:x,type:"button",role:"menuitem",disabled:s,tabIndex:E?0:-1,className:j.cx("brycks-menu-item",a&&"brycks-menu-item--active",S),style:n,onClick:q,onFocus:e,onMouseEnter:$=>{!s&&!a&&($.currentTarget.style.backgroundColor="var(--brycks-background-muted)")},onMouseLeave:$=>{a||($.currentTarget.style.backgroundColor="transparent")},title:o?String(y):void 0,...k,children:[l&&u.jsx("span",{style:w,children:l}),!o&&u.jsx("span",{style:{flex:1},children:y}),!o&&c&&u.jsx("span",{style:f,children:c})]})});T.displayName="MenuItem";const F=r.forwardRef(function({label:t,className:l,style:c,children:s,...p},S){const{collapsed:v}=G(),y={display:"flex",flexDirection:"column",gap:m.spacing[.5],...c},k={padding:`${m.spacing[3]}px ${m.spacing[3]}px ${i.componentPaddingY.sm}px ${m.spacing[3]}px`,fontSize:C.fontSizes.xs,fontWeight:600,color:"var(--brycks-foreground-muted)",textTransform:"uppercase",letterSpacing:"0.05em"};return u.jsxs("div",{ref:S,className:j.cx("brycks-menu-group",l),style:y,...p,children:[t&&!v&&u.jsx("div",{style:k,children:t}),s]})});F.displayName="MenuGroup";const P=r.forwardRef(function({className:t,style:l,...c},s){const p={height:1,backgroundColor:"var(--brycks-border-muted)",margin:`${m.spacing[2]}px 0`,...l};return u.jsx("div",{ref:s,role:"separator",className:j.cx("brycks-menu-divider",t),style:p,...c})});P.displayName="MenuDivider";exports.Menu=H;exports.MenuDivider=P;exports.MenuGroup=F;exports.MenuItem=T;
|
|
2
2
|
//# sourceMappingURL=Menu.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Menu.cjs","sources":["../../../../src/components/navigation/Menu/Menu.tsx"],"sourcesContent":["/**\n * Menu Component\n *\n * A vertical navigation menu with support for nested items.\n * Suitable for sidebars and navigation panels.\n */\n\nimport {\n forwardRef,\n useState,\n useRef,\n useCallback,\n createContext,\n useContext,\n type CSSProperties,\n type ReactNode,\n type HTMLAttributes,\n type KeyboardEvent,\n} from 'react'\nimport { cx } from '../../../utils/styles'\nimport { spacing, fontSizes, durations, easings } from '../../../design-system'\nimport { componentHeights, componentGap, componentPaddingX, componentPaddingY, iconSizes } from '../../../design-system/primitives'\n\nexport type MenuSize = 'sm' | 'md' | 'lg'\n\ninterface MenuContextValue {\n size: MenuSize\n activeItem: string | null\n setActiveItem: (value: string | null) => void\n collapsed: boolean\n focusedIndex: number\n setFocusedIndex: (index: number) => void\n registerItem: (value: string) => number\n}\n\nconst MenuContext = createContext<MenuContextValue | null>(null)\n\nfunction useMenuContext() {\n const context = useContext(MenuContext)\n if (!context) {\n throw new Error('Menu components must be used within a Menu')\n }\n return context\n}\n\nexport interface MenuProps extends Omit<HTMLAttributes<HTMLElement>, 'onChange'> {\n /** Currently active item */\n activeItem?: string | null\n /** Callback when active item changes */\n onChange?: (value: string | null) => void\n /** Menu size */\n size?: MenuSize\n /** Whether the menu is collapsed (icons only) */\n collapsed?: boolean\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nexport const Menu = forwardRef<HTMLElement, MenuProps>(function Menu(\n {\n activeItem: controlledActiveItem,\n onChange,\n size = 'md',\n collapsed = false,\n className,\n style,\n children,\n testId,\n ...props\n },\n ref\n) {\n const [internalActiveItem, setInternalActiveItem] = useState<string | null>(null)\n const [focusedIndex, setFocusedIndex] = useState(-1)\n const itemsRef = useRef<string[]>([])\n const menuRef = useRef<HTMLElement>(null)\n const activeItem = controlledActiveItem !== undefined ? controlledActiveItem : internalActiveItem\n\n const setActiveItem = (value: string | null) => {\n if (controlledActiveItem === undefined) {\n setInternalActiveItem(value)\n }\n onChange?.(value)\n }\n\n // Register items for keyboard navigation\n const registerItem = useCallback((value: string) => {\n const index = itemsRef.current.indexOf(value)\n if (index === -1) {\n itemsRef.current.push(value)\n return itemsRef.current.length - 1\n }\n return index\n }, [])\n\n // Keyboard navigation\n const handleKeyDown = useCallback((e: KeyboardEvent<HTMLElement>) => {\n const items = menuRef.current?.querySelectorAll<HTMLButtonElement>('[role=\"menuitem\"]:not(:disabled)')\n if (!items?.length) return\n\n const currentIndex = focusedIndex >= 0 ? focusedIndex : 0\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault()\n const nextIndex = (currentIndex + 1) % items.length\n setFocusedIndex(nextIndex)\n items[nextIndex]?.focus()\n break\n case 'ArrowUp':\n e.preventDefault()\n const prevIndex = (currentIndex - 1 + items.length) % items.length\n setFocusedIndex(prevIndex)\n items[prevIndex]?.focus()\n break\n case 'Home':\n e.preventDefault()\n setFocusedIndex(0)\n items[0]?.focus()\n break\n case 'End':\n e.preventDefault()\n setFocusedIndex(items.length - 1)\n items[items.length - 1]?.focus()\n break\n }\n }, [focusedIndex])\n\n const menuStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: spacing[0.5],\n padding: spacing[2],\n ...style,\n }\n\n return (\n <MenuContext.Provider value={{ size, activeItem, setActiveItem, collapsed, focusedIndex, setFocusedIndex, registerItem }}>\n <nav\n ref={(node) => {\n (menuRef as React.MutableRefObject<HTMLElement | null>).current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) (ref as React.MutableRefObject<HTMLElement | null>).current = node\n }}\n role=\"menu\"\n className={cx('brycks-menu', `brycks-menu--${size}`, collapsed && 'brycks-menu--collapsed', className)}\n style={menuStyle}\n onKeyDown={handleKeyDown}\n data-testid={testId}\n {...props}\n >\n {children}\n </nav>\n </MenuContext.Provider>\n )\n})\n\nMenu.displayName = 'Menu'\n\n// MenuItem\nexport interface MenuItemProps extends Omit<HTMLAttributes<HTMLButtonElement>, 'onClick'> {\n /** Item value (unique identifier) */\n value: string\n /** Icon before the label */\n icon?: ReactNode\n /** Badge/count after the label */\n badge?: ReactNode\n /** Whether the item is disabled */\n disabled?: boolean\n /** Callback when clicked */\n onClick?: (value: string) => void\n /** Custom class name */\n className?: string\n}\n\nconst sizeConfig: Record<MenuSize, { height: number; fontSize: number; iconSize: number; padding: number }> = {\n sm: { height: componentHeights.sm, fontSize: fontSizes.base - 1, iconSize: iconSizes.sm, padding: componentGap.lg },\n md: { height: componentHeights.md, fontSize: fontSizes.base, iconSize: iconSizes.md - 2, padding: componentPaddingX.sm },\n lg: { height: componentHeights.lg, fontSize: fontSizes.md, iconSize: iconSizes.md, padding: spacing[3.5] },\n}\n\nexport const MenuItem = forwardRef<HTMLButtonElement, MenuItemProps>(function MenuItem(\n { value, icon, badge, disabled = false, onClick, className, style, children, ...props },\n ref\n) {\n const { size, activeItem, setActiveItem, collapsed, focusedIndex, setFocusedIndex, registerItem } = useMenuContext()\n const isActive = activeItem === value\n const config = sizeConfig[size]\n const itemIndex = registerItem(value)\n\n const handleClick = () => {\n if (disabled) return\n setActiveItem(value)\n onClick?.(value)\n }\n\n const handleFocus = () => {\n setFocusedIndex(itemIndex)\n }\n\n const itemStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: componentGap.lg,\n width: '100%',\n minHeight: config.height,\n padding: collapsed ? config.padding : `0 ${config.padding}px`,\n fontSize: config.fontSize,\n fontWeight: isActive ? 600 : 500,\n color: disabled\n ? 'var(--brycks-foreground-disabled)'\n : isActive\n ? 'var(--brycks-primary-default)'\n : 'var(--brycks-foreground-default)',\n backgroundColor: isActive ? 'var(--brycks-primary-50)' : 'transparent',\n border: 'none',\n borderRadius: 'var(--brycks-radius-md)',\n cursor: disabled ? 'not-allowed' : 'pointer',\n transition: `all ${durations.quick}ms ${easings.easeOut}`,\n textAlign: 'left',\n outline: 'none',\n justifyContent: collapsed ? 'center' : 'flex-start',\n ...style,\n }\n\n const iconStyle: CSSProperties = {\n width: config.iconSize,\n height: config.iconSize,\n flexShrink: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }\n\n const badgeStyle: CSSProperties = {\n marginLeft: 'auto',\n }\n\n return (\n <button\n ref={ref}\n type=\"button\"\n role=\"menuitem\"\n disabled={disabled}\n tabIndex={focusedIndex === itemIndex ? 0 : -1}\n className={cx('brycks-menu-item', isActive && 'brycks-menu-item--active', className)}\n style={itemStyle}\n onClick={handleClick}\n onFocus={handleFocus}\n onMouseEnter={(e) => {\n if (!disabled && !isActive) {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n }\n }}\n onMouseLeave={(e) => {\n if (!isActive) {\n e.currentTarget.style.backgroundColor = 'transparent'\n }\n }}\n title={collapsed ? String(children) : undefined}\n {...props}\n >\n {icon && <span style={iconStyle}>{icon}</span>}\n {!collapsed && <span style={{ flex: 1 }}>{children}</span>}\n {!collapsed && badge && <span style={badgeStyle}>{badge}</span>}\n </button>\n )\n})\n\nMenuItem.displayName = 'MenuItem'\n\n// MenuGroup\nexport interface MenuGroupProps extends HTMLAttributes<HTMLDivElement> {\n /** Group label */\n label?: string\n /** Custom class name */\n className?: string\n}\n\nexport const MenuGroup = forwardRef<HTMLDivElement, MenuGroupProps>(function MenuGroup(\n { label, className, style, children, ...props },\n ref\n) {\n const { collapsed } = useMenuContext()\n\n const groupStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: spacing[0.5],\n ...style,\n }\n\n const labelStyle: CSSProperties = {\n padding: `${spacing[3]}px ${spacing[3]}px ${componentPaddingY.sm}px ${spacing[3]}px`,\n fontSize: fontSizes.xs,\n fontWeight: 600,\n color: 'var(--brycks-foreground-muted)',\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n }\n\n return (\n <div ref={ref} className={cx('brycks-menu-group', className)} style={groupStyle} {...props}>\n {label && !collapsed && <div style={labelStyle}>{label}</div>}\n {children}\n </div>\n )\n})\n\nMenuGroup.displayName = 'MenuGroup'\n\n// MenuDivider\nexport interface MenuDividerProps extends HTMLAttributes<HTMLDivElement> {\n /** Custom class name */\n className?: string\n}\n\nexport const MenuDivider = forwardRef<HTMLDivElement, MenuDividerProps>(function MenuDivider(\n { className, style, ...props },\n ref\n) {\n const dividerStyle: CSSProperties = {\n height: 1,\n backgroundColor: 'var(--brycks-border-muted)',\n margin: `${spacing[2]}px 0`,\n ...style,\n }\n\n return (\n <div\n ref={ref}\n role=\"separator\"\n className={cx('brycks-menu-divider', className)}\n style={dividerStyle}\n {...props}\n />\n )\n})\n\nMenuDivider.displayName = 'MenuDivider'\n"],"names":["MenuContext","createContext","useMenuContext","context","useContext","Menu","forwardRef","controlledActiveItem","onChange","size","collapsed","className","style","children","testId","props","ref","internalActiveItem","setInternalActiveItem","useState","focusedIndex","setFocusedIndex","itemsRef","useRef","menuRef","activeItem","setActiveItem","value","registerItem","useCallback","index","handleKeyDown","items","currentIndex","nextIndex","prevIndex","menuStyle","spacing","jsx","node","cx","sizeConfig","componentHeights","fontSizes","iconSizes","componentGap","componentPaddingX","MenuItem","icon","badge","disabled","onClick","isActive","config","itemIndex","handleClick","handleFocus","itemStyle","durations","easings","iconStyle","badgeStyle","jsxs","e","MenuGroup","label","groupStyle","labelStyle","componentPaddingY","MenuDivider","dividerStyle"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Menu.cjs","sources":["../../../../src/components/navigation/Menu/Menu.tsx"],"sourcesContent":["/**\n * Menu Component\n *\n * A vertical navigation menu with support for nested items.\n * Suitable for sidebars and navigation panels.\n */\n\nimport {\n forwardRef,\n useState,\n useRef,\n useCallback,\n createContext,\n useContext,\n type CSSProperties,\n type ReactNode,\n type HTMLAttributes,\n type KeyboardEvent,\n} from 'react'\nimport { cx } from '../../../utils/styles'\nimport { spacing, fontSizes, durations, easings } from '../../../design-system'\nimport { componentHeights, componentGap, componentPaddingX, componentPaddingY, iconSizes } from '../../../design-system/primitives'\n\nexport type MenuSize = 'sm' | 'md' | 'lg'\n\ninterface MenuContextValue {\n size: MenuSize\n activeItem: string | null\n setActiveItem: (value: string | null) => void\n collapsed: boolean\n focusedIndex: number\n setFocusedIndex: (index: number) => void\n registerItem: (value: string) => number\n}\n\nconst MenuContext = createContext<MenuContextValue | null>(null)\n\nfunction useMenuContext() {\n const context = useContext(MenuContext)\n if (!context) {\n throw new Error('Menu components must be used within a Menu')\n }\n return context\n}\n\nexport interface MenuProps extends Omit<HTMLAttributes<HTMLElement>, 'onChange'> {\n /** Currently active item */\n activeItem?: string | null\n /** Callback when active item changes */\n onChange?: (value: string | null) => void\n /** Menu size */\n size?: MenuSize\n /** Whether the menu is collapsed (icons only) */\n collapsed?: boolean\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nexport const Menu = forwardRef<HTMLElement, MenuProps>(function Menu(\n {\n activeItem: controlledActiveItem,\n onChange,\n size = 'md',\n collapsed = false,\n className,\n style,\n children,\n testId,\n ...props\n },\n ref\n) {\n const [internalActiveItem, setInternalActiveItem] = useState<string | null>(null)\n const [focusedIndex, setFocusedIndex] = useState(-1)\n const itemsRef = useRef<string[]>([])\n const menuRef = useRef<HTMLElement>(null)\n const activeItem = controlledActiveItem !== undefined ? controlledActiveItem : internalActiveItem\n\n const setActiveItem = (value: string | null) => {\n if (controlledActiveItem === undefined) {\n setInternalActiveItem(value)\n }\n onChange?.(value)\n }\n\n // Register items for keyboard navigation\n const registerItem = useCallback((value: string) => {\n const index = itemsRef.current.indexOf(value)\n if (index === -1) {\n itemsRef.current.push(value)\n return itemsRef.current.length - 1\n }\n return index\n }, [])\n\n // Keyboard navigation\n const handleKeyDown = useCallback((e: KeyboardEvent<HTMLElement>) => {\n const items = menuRef.current?.querySelectorAll<HTMLButtonElement>('[role=\"menuitem\"]:not(:disabled)')\n if (!items?.length) return\n\n const currentIndex = focusedIndex >= 0 ? focusedIndex : 0\n\n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault()\n const nextIndex = (currentIndex + 1) % items.length\n setFocusedIndex(nextIndex)\n items[nextIndex]?.focus()\n break\n }\n case 'ArrowUp': {\n e.preventDefault()\n const prevIndex = (currentIndex - 1 + items.length) % items.length\n setFocusedIndex(prevIndex)\n items[prevIndex]?.focus()\n break\n }\n case 'Home':\n e.preventDefault()\n setFocusedIndex(0)\n items[0]?.focus()\n break\n case 'End':\n e.preventDefault()\n setFocusedIndex(items.length - 1)\n items[items.length - 1]?.focus()\n break\n }\n }, [focusedIndex])\n\n const menuStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: spacing[0.5],\n padding: spacing[2],\n ...style,\n }\n\n return (\n <MenuContext.Provider value={{ size, activeItem, setActiveItem, collapsed, focusedIndex, setFocusedIndex, registerItem }}>\n <nav\n ref={(node) => {\n (menuRef as React.MutableRefObject<HTMLElement | null>).current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) (ref as React.MutableRefObject<HTMLElement | null>).current = node\n }}\n role=\"menu\"\n className={cx('brycks-menu', `brycks-menu--${size}`, collapsed && 'brycks-menu--collapsed', className)}\n style={menuStyle}\n onKeyDown={handleKeyDown}\n data-testid={testId}\n {...props}\n >\n {children}\n </nav>\n </MenuContext.Provider>\n )\n})\n\nMenu.displayName = 'Menu'\n\n// MenuItem\nexport interface MenuItemProps extends Omit<HTMLAttributes<HTMLButtonElement>, 'onClick'> {\n /** Item value (unique identifier) */\n value: string\n /** Icon before the label */\n icon?: ReactNode\n /** Badge/count after the label */\n badge?: ReactNode\n /** Whether the item is disabled */\n disabled?: boolean\n /** Callback when clicked */\n onClick?: (value: string) => void\n /** Custom class name */\n className?: string\n}\n\nconst sizeConfig: Record<MenuSize, { height: number; fontSize: number; iconSize: number; padding: number }> = {\n sm: { height: componentHeights.sm, fontSize: fontSizes.base - 1, iconSize: iconSizes.sm, padding: componentGap.lg },\n md: { height: componentHeights.md, fontSize: fontSizes.base, iconSize: iconSizes.md - 2, padding: componentPaddingX.sm },\n lg: { height: componentHeights.lg, fontSize: fontSizes.md, iconSize: iconSizes.md, padding: spacing[3.5] },\n}\n\nexport const MenuItem = forwardRef<HTMLButtonElement, MenuItemProps>(function MenuItem(\n { value, icon, badge, disabled = false, onClick, className, style, children, ...props },\n ref\n) {\n const { size, activeItem, setActiveItem, collapsed, focusedIndex, setFocusedIndex, registerItem } = useMenuContext()\n const isActive = activeItem === value\n const config = sizeConfig[size]\n const itemIndex = registerItem(value)\n\n const handleClick = () => {\n if (disabled) return\n setActiveItem(value)\n onClick?.(value)\n }\n\n const handleFocus = () => {\n setFocusedIndex(itemIndex)\n }\n\n const itemStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: componentGap.lg,\n width: '100%',\n minHeight: config.height,\n padding: collapsed ? config.padding : `0 ${config.padding}px`,\n fontSize: config.fontSize,\n fontWeight: isActive ? 600 : 500,\n color: disabled\n ? 'var(--brycks-foreground-disabled)'\n : isActive\n ? 'var(--brycks-primary-default)'\n : 'var(--brycks-foreground-default)',\n backgroundColor: isActive ? 'var(--brycks-primary-muted)' : 'transparent',\n border: 'none',\n borderRadius: 'var(--brycks-radius-md)',\n cursor: disabled ? 'not-allowed' : 'pointer',\n transition: `all ${durations.quick}ms ${easings.easeOut}`,\n textAlign: 'left',\n justifyContent: collapsed ? 'center' : 'flex-start',\n ...style,\n }\n\n const iconStyle: CSSProperties = {\n width: config.iconSize,\n height: config.iconSize,\n flexShrink: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }\n\n const badgeStyle: CSSProperties = {\n marginLeft: 'auto',\n }\n\n // Roving tabindex: before any item is focused (focusedIndex -1), Tab must\n // still be able to enter the menu through the active item or the first one.\n const isTabStop =\n focusedIndex >= 0 ? focusedIndex === itemIndex : activeItem ? isActive : itemIndex === 0\n\n return (\n <button\n ref={ref}\n type=\"button\"\n role=\"menuitem\"\n disabled={disabled}\n tabIndex={isTabStop ? 0 : -1}\n className={cx('brycks-menu-item', isActive && 'brycks-menu-item--active', className)}\n style={itemStyle}\n onClick={handleClick}\n onFocus={handleFocus}\n onMouseEnter={(e) => {\n if (!disabled && !isActive) {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n }\n }}\n onMouseLeave={(e) => {\n if (!isActive) {\n e.currentTarget.style.backgroundColor = 'transparent'\n }\n }}\n title={collapsed ? String(children) : undefined}\n {...props}\n >\n {icon && <span style={iconStyle}>{icon}</span>}\n {!collapsed && <span style={{ flex: 1 }}>{children}</span>}\n {!collapsed && badge && <span style={badgeStyle}>{badge}</span>}\n </button>\n )\n})\n\nMenuItem.displayName = 'MenuItem'\n\n// MenuGroup\nexport interface MenuGroupProps extends HTMLAttributes<HTMLDivElement> {\n /** Group label */\n label?: string\n /** Custom class name */\n className?: string\n}\n\nexport const MenuGroup = forwardRef<HTMLDivElement, MenuGroupProps>(function MenuGroup(\n { label, className, style, children, ...props },\n ref\n) {\n const { collapsed } = useMenuContext()\n\n const groupStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: spacing[0.5],\n ...style,\n }\n\n const labelStyle: CSSProperties = {\n padding: `${spacing[3]}px ${spacing[3]}px ${componentPaddingY.sm}px ${spacing[3]}px`,\n fontSize: fontSizes.xs,\n fontWeight: 600,\n color: 'var(--brycks-foreground-muted)',\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n }\n\n return (\n <div ref={ref} className={cx('brycks-menu-group', className)} style={groupStyle} {...props}>\n {label && !collapsed && <div style={labelStyle}>{label}</div>}\n {children}\n </div>\n )\n})\n\nMenuGroup.displayName = 'MenuGroup'\n\n// MenuDivider\nexport interface MenuDividerProps extends HTMLAttributes<HTMLDivElement> {\n /** Custom class name */\n className?: string\n}\n\nexport const MenuDivider = forwardRef<HTMLDivElement, MenuDividerProps>(function MenuDivider(\n { className, style, ...props },\n ref\n) {\n const dividerStyle: CSSProperties = {\n height: 1,\n backgroundColor: 'var(--brycks-border-muted)',\n margin: `${spacing[2]}px 0`,\n ...style,\n }\n\n return (\n <div\n ref={ref}\n role=\"separator\"\n className={cx('brycks-menu-divider', className)}\n style={dividerStyle}\n {...props}\n />\n )\n})\n\nMenuDivider.displayName = 'MenuDivider'\n"],"names":["MenuContext","createContext","useMenuContext","context","useContext","Menu","forwardRef","controlledActiveItem","onChange","size","collapsed","className","style","children","testId","props","ref","internalActiveItem","setInternalActiveItem","useState","focusedIndex","setFocusedIndex","itemsRef","useRef","menuRef","activeItem","setActiveItem","value","registerItem","useCallback","index","handleKeyDown","items","currentIndex","nextIndex","prevIndex","menuStyle","spacing","jsx","node","cx","sizeConfig","componentHeights","fontSizes","iconSizes","componentGap","componentPaddingX","MenuItem","icon","badge","disabled","onClick","isActive","config","itemIndex","handleClick","handleFocus","itemStyle","durations","easings","iconStyle","badgeStyle","isTabStop","jsxs","e","MenuGroup","label","groupStyle","labelStyle","componentPaddingY","MenuDivider","dividerStyle"],"mappings":"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"}
|
|
@@ -1,48 +1,50 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { forwardRef as C, useState as F, useRef as G, useCallback as
|
|
1
|
+
import { jsx as m, jsxs as E } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef as C, useState as F, useRef as G, useCallback as T, createContext as L, useContext as O } from "react";
|
|
3
3
|
import { cx as w } from "../../../utils/styles.js";
|
|
4
|
-
import { iconSizes as R, componentHeights as j, componentPaddingX as
|
|
4
|
+
import { iconSizes as R, componentHeights as j, componentPaddingX as W, componentGap as H, componentPaddingY as U } from "../../../design-system/primitives/sizing.js";
|
|
5
5
|
import { spacing as u } from "../../../design-system/tokens/spacing.js";
|
|
6
|
-
import { durations as
|
|
6
|
+
import { durations as X, easings as Y } from "../../../design-system/tokens/motion.js";
|
|
7
7
|
import { fontSizes as z } from "../../../design-system/tokens/typography.js";
|
|
8
|
-
const
|
|
8
|
+
const P = L(null);
|
|
9
9
|
function q() {
|
|
10
|
-
const
|
|
11
|
-
if (!
|
|
10
|
+
const f = O(P);
|
|
11
|
+
if (!f)
|
|
12
12
|
throw new Error("Menu components must be used within a Menu");
|
|
13
|
-
return
|
|
13
|
+
return f;
|
|
14
14
|
}
|
|
15
|
-
const
|
|
15
|
+
const B = C(function({
|
|
16
16
|
activeItem: n,
|
|
17
|
-
onChange:
|
|
17
|
+
onChange: c,
|
|
18
18
|
size: s = "md",
|
|
19
19
|
collapsed: r = !1,
|
|
20
20
|
className: l,
|
|
21
|
-
style:
|
|
22
|
-
children:
|
|
23
|
-
testId:
|
|
24
|
-
...
|
|
25
|
-
},
|
|
26
|
-
const [D,
|
|
27
|
-
n === void 0 &&
|
|
28
|
-
}, a =
|
|
29
|
-
const t =
|
|
30
|
-
return t === -1 ? (
|
|
31
|
-
}, []),
|
|
32
|
-
const t =
|
|
21
|
+
style: h,
|
|
22
|
+
children: b,
|
|
23
|
+
testId: p,
|
|
24
|
+
...k
|
|
25
|
+
}, g) {
|
|
26
|
+
const [D, S] = F(null), [y, o] = F(-1), x = G([]), I = G(null), $ = n !== void 0 ? n : D, i = (e) => {
|
|
27
|
+
n === void 0 && S(e), c?.(e);
|
|
28
|
+
}, a = T((e) => {
|
|
29
|
+
const t = x.current.indexOf(e);
|
|
30
|
+
return t === -1 ? (x.current.push(e), x.current.length - 1) : t;
|
|
31
|
+
}, []), v = T((e) => {
|
|
32
|
+
const t = I.current?.querySelectorAll('[role="menuitem"]:not(:disabled)');
|
|
33
33
|
if (!t?.length) return;
|
|
34
|
-
const
|
|
34
|
+
const M = y >= 0 ? y : 0;
|
|
35
35
|
switch (e.key) {
|
|
36
|
-
case "ArrowDown":
|
|
36
|
+
case "ArrowDown": {
|
|
37
37
|
e.preventDefault();
|
|
38
|
-
const
|
|
39
|
-
o(
|
|
38
|
+
const d = (M + 1) % t.length;
|
|
39
|
+
o(d), t[d]?.focus();
|
|
40
40
|
break;
|
|
41
|
-
|
|
41
|
+
}
|
|
42
|
+
case "ArrowUp": {
|
|
42
43
|
e.preventDefault();
|
|
43
|
-
const
|
|
44
|
-
o(
|
|
44
|
+
const d = (M - 1 + t.length) % t.length;
|
|
45
|
+
o(d), t[d]?.focus();
|
|
45
46
|
break;
|
|
47
|
+
}
|
|
46
48
|
case "Home":
|
|
47
49
|
e.preventDefault(), o(0), t[0]?.focus();
|
|
48
50
|
break;
|
|
@@ -50,125 +52,124 @@ const Y = C(function({
|
|
|
50
52
|
e.preventDefault(), o(t.length - 1), t[t.length - 1]?.focus();
|
|
51
53
|
break;
|
|
52
54
|
}
|
|
53
|
-
}, [
|
|
55
|
+
}, [y]), N = {
|
|
54
56
|
display: "flex",
|
|
55
57
|
flexDirection: "column",
|
|
56
58
|
gap: u[0.5],
|
|
57
59
|
padding: u[2],
|
|
58
|
-
...
|
|
60
|
+
...h
|
|
59
61
|
};
|
|
60
|
-
return /* @__PURE__ */
|
|
62
|
+
return /* @__PURE__ */ m(P.Provider, { value: { size: s, activeItem: $, setActiveItem: i, collapsed: r, focusedIndex: y, setFocusedIndex: o, registerItem: a }, children: /* @__PURE__ */ m(
|
|
61
63
|
"nav",
|
|
62
64
|
{
|
|
63
65
|
ref: (e) => {
|
|
64
|
-
|
|
66
|
+
I.current = e, typeof g == "function" ? g(e) : g && (g.current = e);
|
|
65
67
|
},
|
|
66
68
|
role: "menu",
|
|
67
69
|
className: w("brycks-menu", `brycks-menu--${s}`, r && "brycks-menu--collapsed", l),
|
|
68
|
-
style:
|
|
69
|
-
onKeyDown:
|
|
70
|
-
"data-testid":
|
|
71
|
-
...
|
|
72
|
-
children:
|
|
70
|
+
style: N,
|
|
71
|
+
onKeyDown: v,
|
|
72
|
+
"data-testid": p,
|
|
73
|
+
...k,
|
|
74
|
+
children: b
|
|
73
75
|
}
|
|
74
76
|
) });
|
|
75
77
|
});
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
sm: { height: j.sm, fontSize: z.base - 1, iconSize: R.sm, padding:
|
|
79
|
-
md: { height: j.md, fontSize: z.base, iconSize: R.md - 2, padding:
|
|
78
|
+
B.displayName = "Menu";
|
|
79
|
+
const J = {
|
|
80
|
+
sm: { height: j.sm, fontSize: z.base - 1, iconSize: R.sm, padding: H.lg },
|
|
81
|
+
md: { height: j.md, fontSize: z.base, iconSize: R.md - 2, padding: W.sm },
|
|
80
82
|
lg: { height: j.lg, fontSize: z.md, iconSize: R.md, padding: u[3.5] }
|
|
81
|
-
},
|
|
82
|
-
const { size: D, activeItem:
|
|
83
|
-
r || (
|
|
83
|
+
}, Q = C(function({ value: n, icon: c, badge: s, disabled: r = !1, onClick: l, className: h, style: b, children: p, ...k }, g) {
|
|
84
|
+
const { size: D, activeItem: S, setActiveItem: y, collapsed: o, focusedIndex: x, setFocusedIndex: I, registerItem: $ } = q(), i = S === n, a = J[D], v = $(n), N = () => {
|
|
85
|
+
r || (y(n), l?.(n));
|
|
84
86
|
}, e = () => {
|
|
85
|
-
v
|
|
87
|
+
I(v);
|
|
86
88
|
}, t = {
|
|
87
89
|
display: "flex",
|
|
88
90
|
alignItems: "center",
|
|
89
|
-
gap:
|
|
91
|
+
gap: H.lg,
|
|
90
92
|
width: "100%",
|
|
91
93
|
minHeight: a.height,
|
|
92
94
|
padding: o ? a.padding : `0 ${a.padding}px`,
|
|
93
95
|
fontSize: a.fontSize,
|
|
94
|
-
fontWeight:
|
|
95
|
-
color: r ? "var(--brycks-foreground-disabled)" :
|
|
96
|
-
backgroundColor:
|
|
96
|
+
fontWeight: i ? 600 : 500,
|
|
97
|
+
color: r ? "var(--brycks-foreground-disabled)" : i ? "var(--brycks-primary-default)" : "var(--brycks-foreground-default)",
|
|
98
|
+
backgroundColor: i ? "var(--brycks-primary-muted)" : "transparent",
|
|
97
99
|
border: "none",
|
|
98
100
|
borderRadius: "var(--brycks-radius-md)",
|
|
99
101
|
cursor: r ? "not-allowed" : "pointer",
|
|
100
|
-
transition: `all ${
|
|
102
|
+
transition: `all ${X.quick}ms ${Y.easeOut}`,
|
|
101
103
|
textAlign: "left",
|
|
102
|
-
outline: "none",
|
|
103
104
|
justifyContent: o ? "center" : "flex-start",
|
|
104
|
-
...
|
|
105
|
-
},
|
|
105
|
+
...b
|
|
106
|
+
}, M = {
|
|
106
107
|
width: a.iconSize,
|
|
107
108
|
height: a.iconSize,
|
|
108
109
|
flexShrink: 0,
|
|
109
110
|
display: "flex",
|
|
110
111
|
alignItems: "center",
|
|
111
112
|
justifyContent: "center"
|
|
112
|
-
},
|
|
113
|
+
}, d = {
|
|
113
114
|
marginLeft: "auto"
|
|
114
|
-
};
|
|
115
|
-
return /* @__PURE__ */
|
|
115
|
+
}, K = x >= 0 ? x === v : S ? i : v === 0;
|
|
116
|
+
return /* @__PURE__ */ E(
|
|
116
117
|
"button",
|
|
117
118
|
{
|
|
118
|
-
ref:
|
|
119
|
+
ref: g,
|
|
119
120
|
type: "button",
|
|
120
121
|
role: "menuitem",
|
|
121
122
|
disabled: r,
|
|
122
|
-
tabIndex:
|
|
123
|
-
className: w("brycks-menu-item",
|
|
123
|
+
tabIndex: K ? 0 : -1,
|
|
124
|
+
className: w("brycks-menu-item", i && "brycks-menu-item--active", h),
|
|
124
125
|
style: t,
|
|
125
|
-
onClick:
|
|
126
|
+
onClick: N,
|
|
126
127
|
onFocus: e,
|
|
127
|
-
onMouseEnter: (
|
|
128
|
-
!r && !
|
|
128
|
+
onMouseEnter: (A) => {
|
|
129
|
+
!r && !i && (A.currentTarget.style.backgroundColor = "var(--brycks-background-muted)");
|
|
129
130
|
},
|
|
130
|
-
onMouseLeave: (
|
|
131
|
-
|
|
131
|
+
onMouseLeave: (A) => {
|
|
132
|
+
i || (A.currentTarget.style.backgroundColor = "transparent");
|
|
132
133
|
},
|
|
133
|
-
title: o ? String(
|
|
134
|
-
...
|
|
134
|
+
title: o ? String(p) : void 0,
|
|
135
|
+
...k,
|
|
135
136
|
children: [
|
|
136
|
-
|
|
137
|
-
!o && /* @__PURE__ */
|
|
138
|
-
!o && s && /* @__PURE__ */
|
|
137
|
+
c && /* @__PURE__ */ m("span", { style: M, children: c }),
|
|
138
|
+
!o && /* @__PURE__ */ m("span", { style: { flex: 1 }, children: p }),
|
|
139
|
+
!o && s && /* @__PURE__ */ m("span", { style: d, children: s })
|
|
139
140
|
]
|
|
140
141
|
}
|
|
141
142
|
);
|
|
142
143
|
});
|
|
143
|
-
|
|
144
|
-
const
|
|
145
|
-
const { collapsed:
|
|
144
|
+
Q.displayName = "MenuItem";
|
|
145
|
+
const V = C(function({ label: n, className: c, style: s, children: r, ...l }, h) {
|
|
146
|
+
const { collapsed: b } = q(), p = {
|
|
146
147
|
display: "flex",
|
|
147
148
|
flexDirection: "column",
|
|
148
149
|
gap: u[0.5],
|
|
149
150
|
...s
|
|
150
|
-
},
|
|
151
|
-
padding: `${u[3]}px ${u[3]}px ${
|
|
151
|
+
}, k = {
|
|
152
|
+
padding: `${u[3]}px ${u[3]}px ${U.sm}px ${u[3]}px`,
|
|
152
153
|
fontSize: z.xs,
|
|
153
154
|
fontWeight: 600,
|
|
154
155
|
color: "var(--brycks-foreground-muted)",
|
|
155
156
|
textTransform: "uppercase",
|
|
156
157
|
letterSpacing: "0.05em"
|
|
157
158
|
};
|
|
158
|
-
return /* @__PURE__ */
|
|
159
|
-
n && !
|
|
159
|
+
return /* @__PURE__ */ E("div", { ref: h, className: w("brycks-menu-group", c), style: p, ...l, children: [
|
|
160
|
+
n && !b && /* @__PURE__ */ m("div", { style: k, children: n }),
|
|
160
161
|
r
|
|
161
162
|
] });
|
|
162
163
|
});
|
|
163
|
-
|
|
164
|
-
const
|
|
164
|
+
V.displayName = "MenuGroup";
|
|
165
|
+
const Z = C(function({ className: n, style: c, ...s }, r) {
|
|
165
166
|
const l = {
|
|
166
167
|
height: 1,
|
|
167
168
|
backgroundColor: "var(--brycks-border-muted)",
|
|
168
169
|
margin: `${u[2]}px 0`,
|
|
169
|
-
...
|
|
170
|
+
...c
|
|
170
171
|
};
|
|
171
|
-
return /* @__PURE__ */
|
|
172
|
+
return /* @__PURE__ */ m(
|
|
172
173
|
"div",
|
|
173
174
|
{
|
|
174
175
|
ref: r,
|
|
@@ -179,11 +180,11 @@ const V = C(function({ className: n, style: i, ...s }, r) {
|
|
|
179
180
|
}
|
|
180
181
|
);
|
|
181
182
|
});
|
|
182
|
-
|
|
183
|
+
Z.displayName = "MenuDivider";
|
|
183
184
|
export {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
185
|
+
B as Menu,
|
|
186
|
+
Z as MenuDivider,
|
|
187
|
+
V as MenuGroup,
|
|
188
|
+
Q as MenuItem
|
|
188
189
|
};
|
|
189
190
|
//# sourceMappingURL=Menu.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Menu.js","sources":["../../../../src/components/navigation/Menu/Menu.tsx"],"sourcesContent":["/**\n * Menu Component\n *\n * A vertical navigation menu with support for nested items.\n * Suitable for sidebars and navigation panels.\n */\n\nimport {\n forwardRef,\n useState,\n useRef,\n useCallback,\n createContext,\n useContext,\n type CSSProperties,\n type ReactNode,\n type HTMLAttributes,\n type KeyboardEvent,\n} from 'react'\nimport { cx } from '../../../utils/styles'\nimport { spacing, fontSizes, durations, easings } from '../../../design-system'\nimport { componentHeights, componentGap, componentPaddingX, componentPaddingY, iconSizes } from '../../../design-system/primitives'\n\nexport type MenuSize = 'sm' | 'md' | 'lg'\n\ninterface MenuContextValue {\n size: MenuSize\n activeItem: string | null\n setActiveItem: (value: string | null) => void\n collapsed: boolean\n focusedIndex: number\n setFocusedIndex: (index: number) => void\n registerItem: (value: string) => number\n}\n\nconst MenuContext = createContext<MenuContextValue | null>(null)\n\nfunction useMenuContext() {\n const context = useContext(MenuContext)\n if (!context) {\n throw new Error('Menu components must be used within a Menu')\n }\n return context\n}\n\nexport interface MenuProps extends Omit<HTMLAttributes<HTMLElement>, 'onChange'> {\n /** Currently active item */\n activeItem?: string | null\n /** Callback when active item changes */\n onChange?: (value: string | null) => void\n /** Menu size */\n size?: MenuSize\n /** Whether the menu is collapsed (icons only) */\n collapsed?: boolean\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nexport const Menu = forwardRef<HTMLElement, MenuProps>(function Menu(\n {\n activeItem: controlledActiveItem,\n onChange,\n size = 'md',\n collapsed = false,\n className,\n style,\n children,\n testId,\n ...props\n },\n ref\n) {\n const [internalActiveItem, setInternalActiveItem] = useState<string | null>(null)\n const [focusedIndex, setFocusedIndex] = useState(-1)\n const itemsRef = useRef<string[]>([])\n const menuRef = useRef<HTMLElement>(null)\n const activeItem = controlledActiveItem !== undefined ? controlledActiveItem : internalActiveItem\n\n const setActiveItem = (value: string | null) => {\n if (controlledActiveItem === undefined) {\n setInternalActiveItem(value)\n }\n onChange?.(value)\n }\n\n // Register items for keyboard navigation\n const registerItem = useCallback((value: string) => {\n const index = itemsRef.current.indexOf(value)\n if (index === -1) {\n itemsRef.current.push(value)\n return itemsRef.current.length - 1\n }\n return index\n }, [])\n\n // Keyboard navigation\n const handleKeyDown = useCallback((e: KeyboardEvent<HTMLElement>) => {\n const items = menuRef.current?.querySelectorAll<HTMLButtonElement>('[role=\"menuitem\"]:not(:disabled)')\n if (!items?.length) return\n\n const currentIndex = focusedIndex >= 0 ? focusedIndex : 0\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault()\n const nextIndex = (currentIndex + 1) % items.length\n setFocusedIndex(nextIndex)\n items[nextIndex]?.focus()\n break\n case 'ArrowUp':\n e.preventDefault()\n const prevIndex = (currentIndex - 1 + items.length) % items.length\n setFocusedIndex(prevIndex)\n items[prevIndex]?.focus()\n break\n case 'Home':\n e.preventDefault()\n setFocusedIndex(0)\n items[0]?.focus()\n break\n case 'End':\n e.preventDefault()\n setFocusedIndex(items.length - 1)\n items[items.length - 1]?.focus()\n break\n }\n }, [focusedIndex])\n\n const menuStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: spacing[0.5],\n padding: spacing[2],\n ...style,\n }\n\n return (\n <MenuContext.Provider value={{ size, activeItem, setActiveItem, collapsed, focusedIndex, setFocusedIndex, registerItem }}>\n <nav\n ref={(node) => {\n (menuRef as React.MutableRefObject<HTMLElement | null>).current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) (ref as React.MutableRefObject<HTMLElement | null>).current = node\n }}\n role=\"menu\"\n className={cx('brycks-menu', `brycks-menu--${size}`, collapsed && 'brycks-menu--collapsed', className)}\n style={menuStyle}\n onKeyDown={handleKeyDown}\n data-testid={testId}\n {...props}\n >\n {children}\n </nav>\n </MenuContext.Provider>\n )\n})\n\nMenu.displayName = 'Menu'\n\n// MenuItem\nexport interface MenuItemProps extends Omit<HTMLAttributes<HTMLButtonElement>, 'onClick'> {\n /** Item value (unique identifier) */\n value: string\n /** Icon before the label */\n icon?: ReactNode\n /** Badge/count after the label */\n badge?: ReactNode\n /** Whether the item is disabled */\n disabled?: boolean\n /** Callback when clicked */\n onClick?: (value: string) => void\n /** Custom class name */\n className?: string\n}\n\nconst sizeConfig: Record<MenuSize, { height: number; fontSize: number; iconSize: number; padding: number }> = {\n sm: { height: componentHeights.sm, fontSize: fontSizes.base - 1, iconSize: iconSizes.sm, padding: componentGap.lg },\n md: { height: componentHeights.md, fontSize: fontSizes.base, iconSize: iconSizes.md - 2, padding: componentPaddingX.sm },\n lg: { height: componentHeights.lg, fontSize: fontSizes.md, iconSize: iconSizes.md, padding: spacing[3.5] },\n}\n\nexport const MenuItem = forwardRef<HTMLButtonElement, MenuItemProps>(function MenuItem(\n { value, icon, badge, disabled = false, onClick, className, style, children, ...props },\n ref\n) {\n const { size, activeItem, setActiveItem, collapsed, focusedIndex, setFocusedIndex, registerItem } = useMenuContext()\n const isActive = activeItem === value\n const config = sizeConfig[size]\n const itemIndex = registerItem(value)\n\n const handleClick = () => {\n if (disabled) return\n setActiveItem(value)\n onClick?.(value)\n }\n\n const handleFocus = () => {\n setFocusedIndex(itemIndex)\n }\n\n const itemStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: componentGap.lg,\n width: '100%',\n minHeight: config.height,\n padding: collapsed ? config.padding : `0 ${config.padding}px`,\n fontSize: config.fontSize,\n fontWeight: isActive ? 600 : 500,\n color: disabled\n ? 'var(--brycks-foreground-disabled)'\n : isActive\n ? 'var(--brycks-primary-default)'\n : 'var(--brycks-foreground-default)',\n backgroundColor: isActive ? 'var(--brycks-primary-50)' : 'transparent',\n border: 'none',\n borderRadius: 'var(--brycks-radius-md)',\n cursor: disabled ? 'not-allowed' : 'pointer',\n transition: `all ${durations.quick}ms ${easings.easeOut}`,\n textAlign: 'left',\n outline: 'none',\n justifyContent: collapsed ? 'center' : 'flex-start',\n ...style,\n }\n\n const iconStyle: CSSProperties = {\n width: config.iconSize,\n height: config.iconSize,\n flexShrink: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }\n\n const badgeStyle: CSSProperties = {\n marginLeft: 'auto',\n }\n\n return (\n <button\n ref={ref}\n type=\"button\"\n role=\"menuitem\"\n disabled={disabled}\n tabIndex={focusedIndex === itemIndex ? 0 : -1}\n className={cx('brycks-menu-item', isActive && 'brycks-menu-item--active', className)}\n style={itemStyle}\n onClick={handleClick}\n onFocus={handleFocus}\n onMouseEnter={(e) => {\n if (!disabled && !isActive) {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n }\n }}\n onMouseLeave={(e) => {\n if (!isActive) {\n e.currentTarget.style.backgroundColor = 'transparent'\n }\n }}\n title={collapsed ? String(children) : undefined}\n {...props}\n >\n {icon && <span style={iconStyle}>{icon}</span>}\n {!collapsed && <span style={{ flex: 1 }}>{children}</span>}\n {!collapsed && badge && <span style={badgeStyle}>{badge}</span>}\n </button>\n )\n})\n\nMenuItem.displayName = 'MenuItem'\n\n// MenuGroup\nexport interface MenuGroupProps extends HTMLAttributes<HTMLDivElement> {\n /** Group label */\n label?: string\n /** Custom class name */\n className?: string\n}\n\nexport const MenuGroup = forwardRef<HTMLDivElement, MenuGroupProps>(function MenuGroup(\n { label, className, style, children, ...props },\n ref\n) {\n const { collapsed } = useMenuContext()\n\n const groupStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: spacing[0.5],\n ...style,\n }\n\n const labelStyle: CSSProperties = {\n padding: `${spacing[3]}px ${spacing[3]}px ${componentPaddingY.sm}px ${spacing[3]}px`,\n fontSize: fontSizes.xs,\n fontWeight: 600,\n color: 'var(--brycks-foreground-muted)',\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n }\n\n return (\n <div ref={ref} className={cx('brycks-menu-group', className)} style={groupStyle} {...props}>\n {label && !collapsed && <div style={labelStyle}>{label}</div>}\n {children}\n </div>\n )\n})\n\nMenuGroup.displayName = 'MenuGroup'\n\n// MenuDivider\nexport interface MenuDividerProps extends HTMLAttributes<HTMLDivElement> {\n /** Custom class name */\n className?: string\n}\n\nexport const MenuDivider = forwardRef<HTMLDivElement, MenuDividerProps>(function MenuDivider(\n { className, style, ...props },\n ref\n) {\n const dividerStyle: CSSProperties = {\n height: 1,\n backgroundColor: 'var(--brycks-border-muted)',\n margin: `${spacing[2]}px 0`,\n ...style,\n }\n\n return (\n <div\n ref={ref}\n role=\"separator\"\n className={cx('brycks-menu-divider', className)}\n style={dividerStyle}\n {...props}\n />\n )\n})\n\nMenuDivider.displayName = 'MenuDivider'\n"],"names":["MenuContext","createContext","useMenuContext","context","useContext","Menu","forwardRef","controlledActiveItem","onChange","size","collapsed","className","style","children","testId","props","ref","internalActiveItem","setInternalActiveItem","useState","focusedIndex","setFocusedIndex","itemsRef","useRef","menuRef","activeItem","setActiveItem","value","registerItem","useCallback","index","handleKeyDown","items","currentIndex","nextIndex","prevIndex","menuStyle","spacing","jsx","node","cx","sizeConfig","componentHeights","fontSizes","iconSizes","componentGap","componentPaddingX","MenuItem","icon","badge","disabled","onClick","isActive","config","itemIndex","handleClick","handleFocus","itemStyle","durations","easings","iconStyle","badgeStyle","jsxs","e","MenuGroup","label","groupStyle","labelStyle","componentPaddingY","MenuDivider","dividerStyle"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Menu.js","sources":["../../../../src/components/navigation/Menu/Menu.tsx"],"sourcesContent":["/**\n * Menu Component\n *\n * A vertical navigation menu with support for nested items.\n * Suitable for sidebars and navigation panels.\n */\n\nimport {\n forwardRef,\n useState,\n useRef,\n useCallback,\n createContext,\n useContext,\n type CSSProperties,\n type ReactNode,\n type HTMLAttributes,\n type KeyboardEvent,\n} from 'react'\nimport { cx } from '../../../utils/styles'\nimport { spacing, fontSizes, durations, easings } from '../../../design-system'\nimport { componentHeights, componentGap, componentPaddingX, componentPaddingY, iconSizes } from '../../../design-system/primitives'\n\nexport type MenuSize = 'sm' | 'md' | 'lg'\n\ninterface MenuContextValue {\n size: MenuSize\n activeItem: string | null\n setActiveItem: (value: string | null) => void\n collapsed: boolean\n focusedIndex: number\n setFocusedIndex: (index: number) => void\n registerItem: (value: string) => number\n}\n\nconst MenuContext = createContext<MenuContextValue | null>(null)\n\nfunction useMenuContext() {\n const context = useContext(MenuContext)\n if (!context) {\n throw new Error('Menu components must be used within a Menu')\n }\n return context\n}\n\nexport interface MenuProps extends Omit<HTMLAttributes<HTMLElement>, 'onChange'> {\n /** Currently active item */\n activeItem?: string | null\n /** Callback when active item changes */\n onChange?: (value: string | null) => void\n /** Menu size */\n size?: MenuSize\n /** Whether the menu is collapsed (icons only) */\n collapsed?: boolean\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nexport const Menu = forwardRef<HTMLElement, MenuProps>(function Menu(\n {\n activeItem: controlledActiveItem,\n onChange,\n size = 'md',\n collapsed = false,\n className,\n style,\n children,\n testId,\n ...props\n },\n ref\n) {\n const [internalActiveItem, setInternalActiveItem] = useState<string | null>(null)\n const [focusedIndex, setFocusedIndex] = useState(-1)\n const itemsRef = useRef<string[]>([])\n const menuRef = useRef<HTMLElement>(null)\n const activeItem = controlledActiveItem !== undefined ? controlledActiveItem : internalActiveItem\n\n const setActiveItem = (value: string | null) => {\n if (controlledActiveItem === undefined) {\n setInternalActiveItem(value)\n }\n onChange?.(value)\n }\n\n // Register items for keyboard navigation\n const registerItem = useCallback((value: string) => {\n const index = itemsRef.current.indexOf(value)\n if (index === -1) {\n itemsRef.current.push(value)\n return itemsRef.current.length - 1\n }\n return index\n }, [])\n\n // Keyboard navigation\n const handleKeyDown = useCallback((e: KeyboardEvent<HTMLElement>) => {\n const items = menuRef.current?.querySelectorAll<HTMLButtonElement>('[role=\"menuitem\"]:not(:disabled)')\n if (!items?.length) return\n\n const currentIndex = focusedIndex >= 0 ? focusedIndex : 0\n\n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault()\n const nextIndex = (currentIndex + 1) % items.length\n setFocusedIndex(nextIndex)\n items[nextIndex]?.focus()\n break\n }\n case 'ArrowUp': {\n e.preventDefault()\n const prevIndex = (currentIndex - 1 + items.length) % items.length\n setFocusedIndex(prevIndex)\n items[prevIndex]?.focus()\n break\n }\n case 'Home':\n e.preventDefault()\n setFocusedIndex(0)\n items[0]?.focus()\n break\n case 'End':\n e.preventDefault()\n setFocusedIndex(items.length - 1)\n items[items.length - 1]?.focus()\n break\n }\n }, [focusedIndex])\n\n const menuStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: spacing[0.5],\n padding: spacing[2],\n ...style,\n }\n\n return (\n <MenuContext.Provider value={{ size, activeItem, setActiveItem, collapsed, focusedIndex, setFocusedIndex, registerItem }}>\n <nav\n ref={(node) => {\n (menuRef as React.MutableRefObject<HTMLElement | null>).current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) (ref as React.MutableRefObject<HTMLElement | null>).current = node\n }}\n role=\"menu\"\n className={cx('brycks-menu', `brycks-menu--${size}`, collapsed && 'brycks-menu--collapsed', className)}\n style={menuStyle}\n onKeyDown={handleKeyDown}\n data-testid={testId}\n {...props}\n >\n {children}\n </nav>\n </MenuContext.Provider>\n )\n})\n\nMenu.displayName = 'Menu'\n\n// MenuItem\nexport interface MenuItemProps extends Omit<HTMLAttributes<HTMLButtonElement>, 'onClick'> {\n /** Item value (unique identifier) */\n value: string\n /** Icon before the label */\n icon?: ReactNode\n /** Badge/count after the label */\n badge?: ReactNode\n /** Whether the item is disabled */\n disabled?: boolean\n /** Callback when clicked */\n onClick?: (value: string) => void\n /** Custom class name */\n className?: string\n}\n\nconst sizeConfig: Record<MenuSize, { height: number; fontSize: number; iconSize: number; padding: number }> = {\n sm: { height: componentHeights.sm, fontSize: fontSizes.base - 1, iconSize: iconSizes.sm, padding: componentGap.lg },\n md: { height: componentHeights.md, fontSize: fontSizes.base, iconSize: iconSizes.md - 2, padding: componentPaddingX.sm },\n lg: { height: componentHeights.lg, fontSize: fontSizes.md, iconSize: iconSizes.md, padding: spacing[3.5] },\n}\n\nexport const MenuItem = forwardRef<HTMLButtonElement, MenuItemProps>(function MenuItem(\n { value, icon, badge, disabled = false, onClick, className, style, children, ...props },\n ref\n) {\n const { size, activeItem, setActiveItem, collapsed, focusedIndex, setFocusedIndex, registerItem } = useMenuContext()\n const isActive = activeItem === value\n const config = sizeConfig[size]\n const itemIndex = registerItem(value)\n\n const handleClick = () => {\n if (disabled) return\n setActiveItem(value)\n onClick?.(value)\n }\n\n const handleFocus = () => {\n setFocusedIndex(itemIndex)\n }\n\n const itemStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: componentGap.lg,\n width: '100%',\n minHeight: config.height,\n padding: collapsed ? config.padding : `0 ${config.padding}px`,\n fontSize: config.fontSize,\n fontWeight: isActive ? 600 : 500,\n color: disabled\n ? 'var(--brycks-foreground-disabled)'\n : isActive\n ? 'var(--brycks-primary-default)'\n : 'var(--brycks-foreground-default)',\n backgroundColor: isActive ? 'var(--brycks-primary-muted)' : 'transparent',\n border: 'none',\n borderRadius: 'var(--brycks-radius-md)',\n cursor: disabled ? 'not-allowed' : 'pointer',\n transition: `all ${durations.quick}ms ${easings.easeOut}`,\n textAlign: 'left',\n justifyContent: collapsed ? 'center' : 'flex-start',\n ...style,\n }\n\n const iconStyle: CSSProperties = {\n width: config.iconSize,\n height: config.iconSize,\n flexShrink: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }\n\n const badgeStyle: CSSProperties = {\n marginLeft: 'auto',\n }\n\n // Roving tabindex: before any item is focused (focusedIndex -1), Tab must\n // still be able to enter the menu through the active item or the first one.\n const isTabStop =\n focusedIndex >= 0 ? focusedIndex === itemIndex : activeItem ? isActive : itemIndex === 0\n\n return (\n <button\n ref={ref}\n type=\"button\"\n role=\"menuitem\"\n disabled={disabled}\n tabIndex={isTabStop ? 0 : -1}\n className={cx('brycks-menu-item', isActive && 'brycks-menu-item--active', className)}\n style={itemStyle}\n onClick={handleClick}\n onFocus={handleFocus}\n onMouseEnter={(e) => {\n if (!disabled && !isActive) {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n }\n }}\n onMouseLeave={(e) => {\n if (!isActive) {\n e.currentTarget.style.backgroundColor = 'transparent'\n }\n }}\n title={collapsed ? String(children) : undefined}\n {...props}\n >\n {icon && <span style={iconStyle}>{icon}</span>}\n {!collapsed && <span style={{ flex: 1 }}>{children}</span>}\n {!collapsed && badge && <span style={badgeStyle}>{badge}</span>}\n </button>\n )\n})\n\nMenuItem.displayName = 'MenuItem'\n\n// MenuGroup\nexport interface MenuGroupProps extends HTMLAttributes<HTMLDivElement> {\n /** Group label */\n label?: string\n /** Custom class name */\n className?: string\n}\n\nexport const MenuGroup = forwardRef<HTMLDivElement, MenuGroupProps>(function MenuGroup(\n { label, className, style, children, ...props },\n ref\n) {\n const { collapsed } = useMenuContext()\n\n const groupStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: spacing[0.5],\n ...style,\n }\n\n const labelStyle: CSSProperties = {\n padding: `${spacing[3]}px ${spacing[3]}px ${componentPaddingY.sm}px ${spacing[3]}px`,\n fontSize: fontSizes.xs,\n fontWeight: 600,\n color: 'var(--brycks-foreground-muted)',\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n }\n\n return (\n <div ref={ref} className={cx('brycks-menu-group', className)} style={groupStyle} {...props}>\n {label && !collapsed && <div style={labelStyle}>{label}</div>}\n {children}\n </div>\n )\n})\n\nMenuGroup.displayName = 'MenuGroup'\n\n// MenuDivider\nexport interface MenuDividerProps extends HTMLAttributes<HTMLDivElement> {\n /** Custom class name */\n className?: string\n}\n\nexport const MenuDivider = forwardRef<HTMLDivElement, MenuDividerProps>(function MenuDivider(\n { className, style, ...props },\n ref\n) {\n const dividerStyle: CSSProperties = {\n height: 1,\n backgroundColor: 'var(--brycks-border-muted)',\n margin: `${spacing[2]}px 0`,\n ...style,\n }\n\n return (\n <div\n ref={ref}\n role=\"separator\"\n className={cx('brycks-menu-divider', className)}\n style={dividerStyle}\n {...props}\n />\n )\n})\n\nMenuDivider.displayName = 'MenuDivider'\n"],"names":["MenuContext","createContext","useMenuContext","context","useContext","Menu","forwardRef","controlledActiveItem","onChange","size","collapsed","className","style","children","testId","props","ref","internalActiveItem","setInternalActiveItem","useState","focusedIndex","setFocusedIndex","itemsRef","useRef","menuRef","activeItem","setActiveItem","value","registerItem","useCallback","index","handleKeyDown","items","currentIndex","nextIndex","prevIndex","menuStyle","spacing","jsx","node","cx","sizeConfig","componentHeights","fontSizes","iconSizes","componentGap","componentPaddingX","MenuItem","icon","badge","disabled","onClick","isActive","config","itemIndex","handleClick","handleFocus","itemStyle","durations","easings","iconStyle","badgeStyle","isTabStop","jsxs","e","MenuGroup","label","groupStyle","labelStyle","componentPaddingY","MenuDivider","dividerStyle"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("react/jsx-runtime"),j=require("react"),B=require("../../../utils/styles.cjs"),u=require("../../../design-system/tokens/spacing.cjs"),y=require("../../../design-system/tokens/typography.cjs"),N=require("../../../design-system/primitives/sizing.cjs"),v=require("../../../design-system/tokens/motion.cjs"),O={navigation:"Pagination",first:"First page",previous:"Previous page",next:"Next page",last:"Last page",page:p=>`Page ${p}`,of:(p,e)=>`Page ${p} of ${e}`},G={sm:{height:u.spacing[7],fontSize:y.fontSizes.sm,gap:u.spacing[1],minWidth:u.spacing[7]},md:{height:u.spacing[9],fontSize:y.fontSizes.base,gap:N.componentGap.sm,minWidth:u.spacing[9]},lg:{height:u.spacing[11],fontSize:y.fontSizes.md,gap:u.spacing[2],minWidth:u.spacing[11]}};function F(){return r.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",children:r.jsx("path",{d:"M10 12L6 8l4-4",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})}function H(){return r.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",children:r.jsx("path",{d:"M6 4l4 4-4 4",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})}function J(){return r.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",children:r.jsx("path",{d:"M11 12l-4-4 4-4M7 12l-4-4 4-4",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})}function K(){return r.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",children:r.jsx("path",{d:"M5 4l4 4-4 4M9 4l4 4-4 4",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})}const C=j.forwardRef(function({page:e,totalPages:t,onChange:M,size:k="md",variant:c="default",siblingCount:d=1,showFirstLast:m=!0,showPrevNext:b=!0,disabled:o=!1,labels:S,className:L,style:z,testId:W,...$},T){const s=G[k],l={...O,...S},q=j.useMemo(()=>{if(c==="minimal"||c==="simple")return[];const n=[],a=Math.max(e-d,1),h=Math.min(e+d,t),R=a>2,x=h<t-1;if(R)if(x){n.push(1),n.push("ellipsis");for(let i=a;i<=h;i++)n.push(i);n.push("ellipsis"),n.push(t)}else{n.push(1),n.push("ellipsis");for(let i=Math.max(t-2-d,1);i<=t;i++)n.push(i)}else{for(let i=1;i<=Math.min(3+d,t);i++)n.push(i);x&&(n.push("ellipsis"),n.push(t))}return n},[e,t,d,c]),w={display:"flex",alignItems:"center",gap:s.gap,...z},f=(n,a)=>({display:"inline-flex",alignItems:"center",justifyContent:"center",minWidth:s.minWidth,height:s.height,padding:`0 ${s.gap+4}px`,fontSize:s.fontSize,fontWeight:n?600:500,color:a?"var(--brycks-foreground-disabled)":n?"var(--brycks-primary-default)":"var(--brycks-foreground-default)",backgroundColor:n?"var(--brycks-primary-muted)":"transparent",border:"none",borderRadius:"var(--brycks-radius-md)",cursor:a?"not-allowed":"pointer",transition:`all ${v.durations.quick}ms ${v.easings.easeOut}`}),E={display:"flex",alignItems:"center",justifyContent:"center",minWidth:s.minWidth,height:s.height,color:"var(--brycks-foreground-muted)",fontSize:s.fontSize},I={fontSize:s.fontSize,color:"var(--brycks-foreground-muted)",padding:`0 ${s.gap}px`},g=n=>{n>=1&&n<=t&&n!==e&&!o&&M(n)};return r.jsxs("nav",{ref:T,role:"navigation","aria-label":l.navigation,className:B.cx("brycks-pagination",`brycks-pagination--${k}`,`brycks-pagination--${c}`,L),style:w,"data-testid":W,...$,children:[m&&c!=="minimal"&&r.jsx("button",{type:"button","aria-label":l.first,disabled:e===1||o,style:f(!1,e===1||o),onClick:()=>g(1),onMouseEnter:n=>{e!==1&&!o&&(n.currentTarget.style.backgroundColor="var(--brycks-background-muted)")},onMouseLeave:n=>{n.currentTarget.style.backgroundColor="transparent"},children:r.jsx(J,{})}),b&&r.jsx("button",{type:"button","aria-label":l.previous,disabled:e===1||o,style:f(!1,e===1||o),onClick:()=>g(e-1),onMouseEnter:n=>{e!==1&&!o&&(n.currentTarget.style.backgroundColor="var(--brycks-background-muted)")},onMouseLeave:n=>{n.currentTarget.style.backgroundColor="transparent"},children:r.jsx(F,{})}),c==="simple"||c==="minimal"?r.jsx("span",{style:I,children:c==="minimal"?`${e} / ${t}`:l.of(e,t)}):q.map((n,a)=>n==="ellipsis"?r.jsx("span",{style:E,children:"..."},`ellipsis-${a}`):r.jsx("button",{type:"button","aria-label":l.page(n),"aria-current":n===e?"page":void 0,disabled:o,style:f(n===e,o),onClick:()=>g(n),onMouseEnter:h=>{n!==e&&!o&&(h.currentTarget.style.backgroundColor="var(--brycks-background-muted)")},onMouseLeave:h=>{n!==e&&(h.currentTarget.style.backgroundColor="transparent")},children:n},n)),b&&r.jsx("button",{type:"button","aria-label":l.next,disabled:e===t||o,style:f(!1,e===t||o),onClick:()=>g(e+1),onMouseEnter:n=>{e!==t&&!o&&(n.currentTarget.style.backgroundColor="var(--brycks-background-muted)")},onMouseLeave:n=>{n.currentTarget.style.backgroundColor="transparent"},children:r.jsx(H,{})}),m&&c!=="minimal"&&r.jsx("button",{type:"button","aria-label":l.last,disabled:e===t||o,style:f(!1,e===t||o),onClick:()=>g(t),onMouseEnter:n=>{e!==t&&!o&&(n.currentTarget.style.backgroundColor="var(--brycks-background-muted)")},onMouseLeave:n=>{n.currentTarget.style.backgroundColor="transparent"},children:r.jsx(K,{})})]})});C.displayName="Pagination";exports.Pagination=C;
|
|
2
2
|
//# sourceMappingURL=Pagination.cjs.map
|