@tribepad/themis 1.0.1 → 1.0.2
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/elements/Accordion/index.js +1 -335
- package/dist/elements/Accordion/index.js.map +1 -1
- package/dist/elements/Accordion/index.mjs +1 -317
- package/dist/elements/Accordion/index.mjs.map +1 -1
- package/dist/elements/AlertDialog/AlertDialog.d.ts +43 -0
- package/dist/elements/AlertDialog/AlertDialog.d.ts.map +1 -0
- package/dist/elements/AlertDialog/AlertDialog.styles.d.ts +15 -0
- package/dist/elements/AlertDialog/AlertDialog.styles.d.ts.map +1 -0
- package/dist/elements/AlertDialog/AlertDialog.types.d.ts +72 -0
- package/dist/elements/AlertDialog/AlertDialog.types.d.ts.map +1 -0
- package/dist/elements/AlertDialog/index.d.ts +25 -0
- package/dist/elements/AlertDialog/index.d.ts.map +1 -0
- package/dist/elements/AlertDialog/index.js +3 -0
- package/dist/elements/AlertDialog/index.js.map +1 -0
- package/dist/elements/AlertDialog/index.mjs +3 -0
- package/dist/elements/AlertDialog/index.mjs.map +1 -0
- package/dist/elements/Avatar/index.js +1 -468
- package/dist/elements/Avatar/index.js.map +1 -1
- package/dist/elements/Avatar/index.mjs +1 -456
- package/dist/elements/Avatar/index.mjs.map +1 -1
- package/dist/elements/Badge/index.js +1 -243
- package/dist/elements/Badge/index.js.map +1 -1
- package/dist/elements/Badge/index.mjs +1 -234
- package/dist/elements/Badge/index.mjs.map +1 -1
- package/dist/elements/Breadcrumbs/index.js +1 -821
- package/dist/elements/Breadcrumbs/index.js.map +1 -1
- package/dist/elements/Breadcrumbs/index.mjs +1 -810
- package/dist/elements/Breadcrumbs/index.mjs.map +1 -1
- package/dist/elements/Button/Button.d.ts +26 -81
- package/dist/elements/Button/Button.d.ts.map +1 -1
- package/dist/elements/Button/Button.styles.d.ts +35 -0
- package/dist/elements/Button/Button.styles.d.ts.map +1 -0
- package/dist/elements/Button/Button.types.d.ts +20 -8
- package/dist/elements/Button/Button.types.d.ts.map +1 -1
- package/dist/elements/Button/index.js +1 -288
- package/dist/elements/Button/index.js.map +1 -1
- package/dist/elements/Button/index.mjs +1 -283
- package/dist/elements/Button/index.mjs.map +1 -1
- package/dist/elements/ButtonGroup/index.js +1 -237
- package/dist/elements/ButtonGroup/index.js.map +1 -1
- package/dist/elements/ButtonGroup/index.mjs +1 -222
- package/dist/elements/ButtonGroup/index.mjs.map +1 -1
- package/dist/elements/Card/index.js +1 -579
- package/dist/elements/Card/index.js.map +1 -1
- package/dist/elements/Card/index.mjs +1 -560
- package/dist/elements/Card/index.mjs.map +1 -1
- package/dist/elements/Carousel/Carousel.d.ts +1 -11
- package/dist/elements/Carousel/Carousel.d.ts.map +1 -1
- package/dist/elements/Carousel/LazyCarousel.d.ts +1 -1
- package/dist/elements/Carousel/LazyCarousel.d.ts.map +1 -1
- package/dist/elements/Carousel/index.js +1 -789
- package/dist/elements/Carousel/index.js.map +1 -1
- package/dist/elements/Carousel/index.mjs +1 -786
- package/dist/elements/Carousel/index.mjs.map +1 -1
- package/dist/elements/Chart/ChartContext.d.ts.map +1 -1
- package/dist/elements/Chart/index.js +1 -1842
- package/dist/elements/Chart/index.js.map +1 -1
- package/dist/elements/Chart/index.mjs +1 -1832
- package/dist/elements/Chart/index.mjs.map +1 -1
- package/dist/elements/Checkbox/index.js +1 -316
- package/dist/elements/Checkbox/index.js.map +1 -1
- package/dist/elements/Checkbox/index.mjs +1 -306
- package/dist/elements/Checkbox/index.mjs.map +1 -1
- package/dist/elements/CheckboxGroup/index.js +1 -455
- package/dist/elements/CheckboxGroup/index.js.map +1 -1
- package/dist/elements/CheckboxGroup/index.mjs +1 -439
- package/dist/elements/CheckboxGroup/index.mjs.map +1 -1
- package/dist/elements/Combobox/Combobox.d.ts +56 -0
- package/dist/elements/Combobox/Combobox.d.ts.map +1 -0
- package/dist/elements/Combobox/Combobox.styles.d.ts +29 -0
- package/dist/elements/Combobox/Combobox.styles.d.ts.map +1 -0
- package/dist/elements/Combobox/Combobox.types.d.ts +67 -0
- package/dist/elements/Combobox/Combobox.types.d.ts.map +1 -0
- package/dist/elements/Combobox/index.d.ts +20 -0
- package/dist/elements/Combobox/index.d.ts.map +1 -0
- package/dist/elements/Combobox/index.js +3 -0
- package/dist/elements/Combobox/index.js.map +1 -0
- package/dist/elements/Combobox/index.mjs +3 -0
- package/dist/elements/Combobox/index.mjs.map +1 -0
- package/dist/elements/DatePicker/DatePicker.d.ts +1 -1
- package/dist/elements/DatePicker/DatePicker.d.ts.map +1 -1
- package/dist/elements/DatePicker/index.js +1 -903
- package/dist/elements/DatePicker/index.js.map +1 -1
- package/dist/elements/DatePicker/index.mjs +1 -853
- package/dist/elements/DatePicker/index.mjs.map +1 -1
- package/dist/elements/Dropdown/Dropdown.d.ts +7 -15
- package/dist/elements/Dropdown/Dropdown.d.ts.map +1 -1
- package/dist/elements/Dropdown/Dropdown.styles.d.ts +22 -0
- package/dist/elements/Dropdown/Dropdown.styles.d.ts.map +1 -0
- package/dist/elements/Dropdown/index.d.ts +1 -0
- package/dist/elements/Dropdown/index.d.ts.map +1 -1
- package/dist/elements/Dropdown/index.js +1 -193
- package/dist/elements/Dropdown/index.js.map +1 -1
- package/dist/elements/Dropdown/index.mjs +1 -184
- package/dist/elements/Dropdown/index.mjs.map +1 -1
- package/dist/elements/FileField/index.js +1 -1539
- package/dist/elements/FileField/index.js.map +1 -1
- package/dist/elements/FileField/index.mjs +1 -1507
- package/dist/elements/FileField/index.mjs.map +1 -1
- package/dist/elements/FormLayout/index.js +1 -170
- package/dist/elements/FormLayout/index.js.map +1 -1
- package/dist/elements/FormLayout/index.mjs +1 -167
- package/dist/elements/FormLayout/index.mjs.map +1 -1
- package/dist/elements/Modal/Modal.d.ts +9 -14
- package/dist/elements/Modal/Modal.d.ts.map +1 -1
- package/dist/elements/Modal/Modal.styles.d.ts +29 -0
- package/dist/elements/Modal/Modal.styles.d.ts.map +1 -0
- package/dist/elements/Modal/index.d.ts +1 -0
- package/dist/elements/Modal/index.d.ts.map +1 -1
- package/dist/elements/Modal/index.js +1 -232
- package/dist/elements/Modal/index.js.map +1 -1
- package/dist/elements/Modal/index.mjs +1 -220
- package/dist/elements/Modal/index.mjs.map +1 -1
- package/dist/elements/NumberField/index.js +1 -666
- package/dist/elements/NumberField/index.js.map +1 -1
- package/dist/elements/NumberField/index.mjs +1 -654
- package/dist/elements/NumberField/index.mjs.map +1 -1
- package/dist/elements/OTPInput/OTPInput.d.ts.map +1 -1
- package/dist/elements/OTPInput/index.js +1 -734
- package/dist/elements/OTPInput/index.js.map +1 -1
- package/dist/elements/OTPInput/index.mjs +1 -732
- package/dist/elements/OTPInput/index.mjs.map +1 -1
- package/dist/elements/Pagination/Pagination.d.ts +45 -0
- package/dist/elements/Pagination/Pagination.d.ts.map +1 -0
- package/dist/elements/Pagination/Pagination.styles.d.ts +10 -0
- package/dist/elements/Pagination/Pagination.styles.d.ts.map +1 -0
- package/dist/elements/Pagination/Pagination.types.d.ts +55 -0
- package/dist/elements/Pagination/Pagination.types.d.ts.map +1 -0
- package/dist/elements/Pagination/index.d.ts +21 -0
- package/dist/elements/Pagination/index.d.ts.map +1 -0
- package/dist/elements/Pagination/index.js +3 -0
- package/dist/elements/Pagination/index.js.map +1 -0
- package/dist/elements/Pagination/index.mjs +3 -0
- package/dist/elements/Pagination/index.mjs.map +1 -0
- package/dist/elements/Panel/index.js +1 -330
- package/dist/elements/Panel/index.js.map +1 -1
- package/dist/elements/Panel/index.mjs +1 -323
- package/dist/elements/Panel/index.mjs.map +1 -1
- package/dist/elements/PasswordField/PasswordField.d.ts +27 -0
- package/dist/elements/PasswordField/PasswordField.d.ts.map +1 -0
- package/dist/elements/PasswordField/PasswordField.styles.d.ts +32 -0
- package/dist/elements/PasswordField/PasswordField.styles.d.ts.map +1 -0
- package/dist/elements/PasswordField/PasswordField.types.d.ts +100 -0
- package/dist/elements/PasswordField/PasswordField.types.d.ts.map +1 -0
- package/dist/elements/PasswordField/index.css +2 -0
- package/dist/elements/PasswordField/index.css.map +1 -0
- package/dist/elements/PasswordField/index.d.ts +20 -0
- package/dist/elements/PasswordField/index.d.ts.map +1 -0
- package/dist/elements/PasswordField/index.js +3 -0
- package/dist/elements/PasswordField/index.js.map +1 -0
- package/dist/elements/PasswordField/index.mjs +3 -0
- package/dist/elements/PasswordField/index.mjs.map +1 -0
- package/dist/elements/Progress/index.js +1 -187
- package/dist/elements/Progress/index.js.map +1 -1
- package/dist/elements/Progress/index.mjs +1 -181
- package/dist/elements/Progress/index.mjs.map +1 -1
- package/dist/elements/RadioGroup/index.js +1 -369
- package/dist/elements/RadioGroup/index.js.map +1 -1
- package/dist/elements/RadioGroup/index.mjs +1 -359
- package/dist/elements/RadioGroup/index.mjs.map +1 -1
- package/dist/elements/Resizable/index.js +1 -1580
- package/dist/elements/Resizable/index.js.map +1 -1
- package/dist/elements/Resizable/index.mjs +1 -1566
- package/dist/elements/Resizable/index.mjs.map +1 -1
- package/dist/elements/SearchField/SearchField.d.ts +27 -0
- package/dist/elements/SearchField/SearchField.d.ts.map +1 -0
- package/dist/elements/SearchField/SearchField.styles.d.ts +32 -0
- package/dist/elements/SearchField/SearchField.styles.d.ts.map +1 -0
- package/dist/elements/SearchField/SearchField.types.d.ts +45 -0
- package/dist/elements/SearchField/SearchField.types.d.ts.map +1 -0
- package/dist/elements/SearchField/index.css +2 -0
- package/dist/elements/SearchField/index.css.map +1 -0
- package/dist/elements/SearchField/index.d.ts +21 -0
- package/dist/elements/SearchField/index.d.ts.map +1 -0
- package/dist/elements/SearchField/index.js +3 -0
- package/dist/elements/SearchField/index.js.map +1 -0
- package/dist/elements/SearchField/index.mjs +3 -0
- package/dist/elements/SearchField/index.mjs.map +1 -0
- package/dist/elements/Select/Select.d.ts +19 -48
- package/dist/elements/Select/Select.d.ts.map +1 -1
- package/dist/elements/Select/Select.styles.d.ts +55 -0
- package/dist/elements/Select/Select.styles.d.ts.map +1 -0
- package/dist/elements/Select/index.js +1 -589
- package/dist/elements/Select/index.js.map +1 -1
- package/dist/elements/Select/index.mjs +1 -582
- package/dist/elements/Select/index.mjs.map +1 -1
- package/dist/elements/Skeleton/index.js +1 -82
- package/dist/elements/Skeleton/index.js.map +1 -1
- package/dist/elements/Skeleton/index.mjs +1 -78
- package/dist/elements/Skeleton/index.mjs.map +1 -1
- package/dist/elements/Switch/index.js +1 -179
- package/dist/elements/Switch/index.js.map +1 -1
- package/dist/elements/Switch/index.mjs +1 -173
- package/dist/elements/Switch/index.mjs.map +1 -1
- package/dist/elements/Table/Table.d.ts +3 -24
- package/dist/elements/Table/Table.d.ts.map +1 -1
- package/dist/elements/Table/Table.styles.d.ts +24 -0
- package/dist/elements/Table/Table.styles.d.ts.map +1 -0
- package/dist/elements/Table/index.js +1 -595
- package/dist/elements/Table/index.js.map +1 -1
- package/dist/elements/Table/index.mjs +1 -578
- package/dist/elements/Table/index.mjs.map +1 -1
- package/dist/elements/Tabs/index.js +1 -337
- package/dist/elements/Tabs/index.js.map +1 -1
- package/dist/elements/Tabs/index.mjs +1 -320
- package/dist/elements/Tabs/index.mjs.map +1 -1
- package/dist/elements/TextField/TextField.d.ts +6 -42
- package/dist/elements/TextField/TextField.d.ts.map +1 -1
- package/dist/elements/TextField/TextField.hooks.d.ts +63 -0
- package/dist/elements/TextField/TextField.hooks.d.ts.map +1 -0
- package/dist/elements/TextField/TextField.icons.d.ts +19 -0
- package/dist/elements/TextField/TextField.icons.d.ts.map +1 -0
- package/dist/elements/TextField/TextField.styles.d.ts +37 -0
- package/dist/elements/TextField/TextField.styles.d.ts.map +1 -0
- package/dist/elements/TextField/TextField.types.d.ts +3 -0
- package/dist/elements/TextField/TextField.types.d.ts.map +1 -1
- package/dist/elements/TextField/index.css +1 -22
- package/dist/elements/TextField/index.css.map +1 -1
- package/dist/elements/TextField/index.js +1 -902
- package/dist/elements/TextField/index.js.map +1 -1
- package/dist/elements/TextField/index.mjs +1 -882
- package/dist/elements/TextField/index.mjs.map +1 -1
- package/dist/elements/TimeField/index.js +1 -254
- package/dist/elements/TimeField/index.js.map +1 -1
- package/dist/elements/TimeField/index.mjs +1 -238
- package/dist/elements/TimeField/index.mjs.map +1 -1
- package/dist/elements/Toast/Toast.d.ts +0 -22
- package/dist/elements/Toast/Toast.d.ts.map +1 -1
- package/dist/elements/Toast/index.js +1 -737
- package/dist/elements/Toast/index.js.map +1 -1
- package/dist/elements/Toast/index.mjs +1 -724
- package/dist/elements/Toast/index.mjs.map +1 -1
- package/dist/elements/Tooltip/index.js +1 -323
- package/dist/elements/Tooltip/index.js.map +1 -1
- package/dist/elements/Tooltip/index.mjs +1 -310
- package/dist/elements/Tooltip/index.mjs.map +1 -1
- package/dist/elements/index.css +1 -22
- package/dist/elements/index.css.map +1 -1
- package/dist/elements/index.d.ts +13 -1
- package/dist/elements/index.d.ts.map +1 -1
- package/dist/elements/index.js +1 -12455
- package/dist/elements/index.js.map +1 -1
- package/dist/elements/index.mjs +1 -12233
- package/dist/elements/index.mjs.map +1 -1
- package/dist/index.css +1 -22
- package/dist/index.css.map +1 -1
- package/dist/index.js +2 -12490
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -12262
- package/dist/index.mjs.map +1 -1
- package/dist/schemas/index.js +2 -54
- package/dist/schemas/index.js.map +1 -1
- package/dist/schemas/index.mjs +2 -48
- package/dist/schemas/index.mjs.map +1 -1
- package/dist/styles/defaults.css +151 -0
- package/dist/styles/index.js +1 -166
- package/dist/styles/index.js.map +1 -1
- package/dist/styles/index.mjs +1 -129
- package/dist/styles/index.mjs.map +1 -1
- package/dist/utils/index.js +1 -12
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/index.mjs +1 -10
- package/dist/utils/index.mjs.map +1 -1
- package/package.json +9 -7
- package/src/elements/Accordion/Accordion.stories.tsx +1 -1
- package/src/elements/AlertDialog/AlertDialog.stories.tsx +124 -0
- package/src/elements/Avatar/Avatar.stories.tsx +1 -1
- package/src/elements/Badge/Badge.stories.tsx +1 -1
- package/src/elements/Breadcrumbs/Breadcrumbs.stories.tsx +1 -1
- package/src/elements/Button/Button.stories.tsx +1 -1
- package/src/elements/ButtonGroup/ButtonGroup.stories.tsx +1 -1
- package/src/elements/Card/Card.stories.tsx +1 -1
- package/src/elements/Carousel/Carousel.stories.tsx +1 -1
- package/src/elements/Chart/Chart.stories.tsx +1 -1
- package/src/elements/Checkbox/Checkbox.stories.tsx +1 -1
- package/src/elements/CheckboxGroup/CheckboxGroup.stories.tsx +1 -1
- package/src/elements/Combobox/Combobox.stories.tsx +133 -0
- package/src/elements/DatePicker/DatePicker.stories.tsx +1 -1
- package/src/elements/Dropdown/Dropdown.stories.tsx +1 -1
- package/src/elements/FileField/FileField.stories.tsx +1 -1
- package/src/elements/FileField/FileProgress.stories.tsx +1 -1
- package/src/elements/FormLayout/FormLayout.stories.tsx +1 -1
- package/src/elements/Modal/Modal.stories.tsx +1 -1
- package/src/elements/NumberField/NumberField.stories.tsx +1 -1
- package/src/elements/OTPInput/OTPInput.stories.tsx +1 -1
- package/src/elements/Pagination/Pagination.stories.tsx +203 -0
- package/src/elements/Panel/Panel.stories.tsx +1 -1
- package/src/elements/PasswordField/PasswordField.stories.tsx +167 -0
- package/src/elements/Progress/Progress.stories.tsx +1 -1
- package/src/elements/RadioGroup/RadioGroup.stories.tsx +1 -1
- package/src/elements/Resizable/Resizable.stories.tsx +1 -1
- package/src/elements/SearchField/SearchField.stories.tsx +146 -0
- package/src/elements/Select/Select.stories.tsx +1 -1
- package/src/elements/Skeleton/Skeleton.stories.tsx +1 -1
- package/src/elements/Switch/Switch.stories.tsx +1 -1
- package/src/elements/Table/Table.stories.tsx +1 -1
- package/src/elements/Tabs/Tabs.stories.tsx +1 -1
- package/src/elements/TextField/TextField.stories.tsx +1 -1
- package/src/elements/TimeField/TimeField.stories.tsx +1 -1
- package/src/elements/Toast/Toast.stories.tsx +1 -1
- package/src/elements/Tooltip/Tooltip.stories.tsx +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/utils/cn.ts","../../../src/elements/Button/Button.styles.ts","../../../src/styles/interaction-states.ts","../../../src/elements/ButtonGroup/ButtonGroupContext.tsx","../../../src/elements/ButtonGroup/ButtonGroup.variants.ts","../../../src/elements/Button/Button.tsx","../../../src/elements/Pagination/Pagination.styles.ts","../../../src/elements/Pagination/Pagination.tsx","../../../src/schemas/BaseComponentProps.ts","../../../src/elements/Pagination/Pagination.types.ts"],"names":["cn","inputs","twMerge","clsx","buttonOuterVariants","cva","buttonVisualVariants","PRESSED_STYLES","HOVER_STYLES","HIGH_CONTRAST_HOVER","HIGH_CONTRAST_PRESSED","ButtonGroupContext","createContext","useButtonGroupContext","useContext","ButtonGroupItemContext","useButtonGroupItemContext","buttonGroupItemVariants","Button","memo","forwardRef","className","buttonVisualClassName","variant","size","visualSize","fullWidth","loading","loadingText","shortcut","children","isDisabled","paywall","paywallRedirect","paywallDescription","onPress","props","ref","paywallDescriptionId","useId","groupContext","itemContext","effectiveVariant","effectiveSize","effectiveIsDisabled","isInVerticalGroup","effectiveFullWidth","positionClassName","effectiveVisualSize","jsx","AriaButton","e","renderProps","jsxs","Fragment","Loader2","Zap","paginationNavVariants","paginationEllipsisVariants","paginationInfoVariants","generatePageNumbers","currentPage","totalPages","siblingCount","totalPageNumbers","_","i","leftSiblingIndex","rightSiblingIndex","showLeftEllipsis","showRightEllipsis","leftRange","pages","rightRange","getButtonVariant","paginationVariant","isActive","getButtonVisualSize","Pagination","page","onPageChange","showFirstLast","showPrevNext","labels","ariaLabel","dataTestId","resolvedLabels","useMemo","handlePageChange","useCallback","newPage","isFirstPage","isLastPage","ChevronsLeft","ChevronLeft","pageItem","ChevronRight","ChevronsRight","BaseComponentPropsSchema","z","PaginationSizeSchema","PaginationVariantSchema","PaginationPropsSchema"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Class Name Utility\n * Merges Tailwind CSS classes with conflict resolution\n *\n * Combines clsx for conditional classes and tailwind-merge for deduplication\n *\n * @example\n * cn('px-2 py-1', 'px-4') // => 'py-1 px-4' (px-4 overrides px-2)\n * cn('text-red-500', condition && 'text-blue-500') // => conditional application\n */\n\nimport { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","import { cva } from 'class-variance-authority';\n\n/**\n * Layer 1: Transparent outer touch target (44x44px minimum)\n * Handles WCAG 2.2 AAA touch target requirement\n * Always transparent, centers the visual button inside\n * IMPORTANT: Focus ring stays on Layer 1 for AAA compliance (2.4.13)\n *\n * In vertical ButtonGroups, uses items-stretch so the visual layer (Layer 2)\n * can fill the full touch target height, eliminating gaps between buttons.\n */\nexport const buttonOuterVariants = cva(\n \"inline-flex justify-center min-h-[44px] min-w-[44px] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50\",\n {\n variants: {\n fullWidth: {\n true: \"w-full\",\n false: \"\",\n },\n inVerticalGroup: {\n true: \"items-stretch\",\n false: \"items-center\",\n },\n },\n defaultVariants: {\n fullWidth: false,\n inVerticalGroup: false,\n },\n }\n);\n\n/**\n * Layer 2: Visual button appearance (adjustable size)\n * Provides the visual appearance with configurable size\n * Can be smaller than touch target for use cases like carousel dots\n * NOTE: NO focus-visible styles here - focus ring is on Layer 1\n */\nexport const buttonVisualVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 relative cursor-pointer\",\n {\n variants: {\n variant: {\n default:\n \"bg-[var(--primary-action)] text-[var(--primary-action-foreground)] shadow-md hover:bg-[var(--primary-action-hover)] data-[pressed]:bg-[var(--primary-action)]/80\",\n destructive:\n \"bg-[var(--destructive-background)] text-[var(--destructive-foreground)] shadow-md hover:bg-[var(--destructive-background)]/90 data-[pressed]:bg-[var(--destructive-background)]/80\",\n outline:\n \"border border-[var(--input-border)] bg-[var(--page-background)] hover:bg-[var(--input-border)] data-[pressed]:bg-[var(--input-border)]\",\n secondary:\n \"bg-[var(--secondary)] text-[var(--secondary-foreground)] shadow-md hover:bg-[var(--secondary)]/80 data-[pressed]:bg-[var(--secondary)]/70\",\n ghost:\n \"hover:bg-[var(--accent)] hover:text-[var(--accent-foreground)] data-[pressed]:bg-[var(--accent)]\",\n link: \"text-[var(--text-link)] underline-offset-4 hover:underline data-[pressed]:text-[var(--text-link-hover)]\",\n },\n fullWidth: {\n true: \"w-full\",\n false: \"\",\n },\n visualSize: {\n default: \"h-10 px-4 py-2\",\n sm: \"h-9 rounded-md px-3 text-xs\",\n lg: \"h-11 rounded-md px-8\",\n icon: \"h-10 w-10\",\n dot: \"h-5 w-5 rounded-full p-0 min-h-0 min-w-0\",\n },\n paywall: {\n true: \"!bg-[var(--paywall)] !text-[var(--paywall-foreground)] !shadow-md hover:!bg-[var(--paywall)]/90 !cursor-not-allowed !border-transparent\",\n false: \"\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n visualSize: \"default\",\n paywall: false,\n },\n }\n);\n\n/**\n * @deprecated Use buttonVisualVariants instead. This alias is kept for backward compatibility.\n */\nexport const buttonVariants = buttonVisualVariants;\n","/**\n * Global Interaction State Styles\n *\n * Consistent interaction patterns across all Themis components.\n * These styles ensure WCAG 2.2 AAA compliance and predictable UX.\n *\n * @see spec.md FR-010 (Visible focus ring for keyboard navigation)\n * @see spec.md FR-031 (Pressed state feedback)\n * @see spec.md FR-012 (High contrast mode support)\n * @see constitution.md Principle IV (Accessibility First)\n */\n\n/**\n * Focus state styles (FR-010)\n * Visible focus ring for keyboard navigation - WCAG 2.2 Level AAA\n *\n * Components can override by extending these styles:\n * @example\n * cn(FOCUS_STYLES, \"ring-4\") // Increases ring width to 4px\n */\nexport const FOCUS_STYLES = \"data-[focus-visible]:ring-2 data-[focus-visible]:ring-[var(--themis-ring)] data-[focus-visible]:ring-offset-2\";\n\n/**\n * Pressed/Active state styles (FR-031)\n * Visual feedback for press interactions\n *\n * Components can override the scale amount:\n * @example\n * cn(PRESSED_STYLES_BASE, \"data-[pressed]:scale-[0.95]\") // More pronounced scale\n */\nexport const PRESSED_STYLES = \"data-[pressed]:scale-[0.97]\";\n\n/**\n * Base pressed styles without scale (for components that need different feedback)\n */\nexport const PRESSED_STYLES_BASE = \"data-[pressed]:transition-transform data-[pressed]:duration-100\";\n\n/**\n * Hover state styles\n * Elevation change on hover for better affordance\n *\n * Components can override shadow depth:\n * @example\n * cn(HOVER_STYLES_BASE, \"data-[hovered]:shadow-lg\") // Larger shadow\n */\nexport const HOVER_STYLES = \"data-[hovered]:shadow-md\";\n\n/**\n * Base hover styles without shadow (for components that use different hover effects)\n */\nexport const HOVER_STYLES_BASE = \"data-[hovered]:transition-shadow data-[hovered]:duration-200\";\n\n/**\n * High contrast mode focus (FR-012)\n * Enhanced outlines for users requiring high contrast\n *\n * Uses 'hc:' prefix for prefers-contrast: more media query\n */\nexport const HIGH_CONTRAST_FOCUS = \"hc:data-[focus-visible]:outline hc:data-[focus-visible]:outline-4 hc:data-[focus-visible]:outline-offset-2 hc:data-[focus-visible]:outline-foreground\";\n\n/**\n * High contrast mode hover (FR-012)\n * Enhanced outlines for hover in high contrast mode\n */\nexport const HIGH_CONTRAST_HOVER = \"hc:data-[hovered]:outline hc:data-[hovered]:outline-2 hc:data-[hovered]:outline-foreground\";\n\n/**\n * High contrast mode pressed state\n * Enhanced outlines for pressed state in high contrast mode\n */\nexport const HIGH_CONTRAST_PRESSED = \"hc:data-[pressed]:outline hc:data-[pressed]:outline-2 hc:data-[pressed]:outline-offset-1 hc:data-[pressed]:outline-foreground\";\n\n/**\n * Combined high contrast styles\n * Use this for components that need all high contrast interaction states\n */\nexport const HIGH_CONTRAST_INTERACTIONS = `${HIGH_CONTRAST_FOCUS} ${HIGH_CONTRAST_HOVER} ${HIGH_CONTRAST_PRESSED}`;\n\n/**\n * Disabled state styles\n * Consistent disabled appearance across all components\n */\nexport const DISABLED_STYLES = \"disabled:pointer-events-none disabled:opacity-50\";\n\n/**\n * Default interaction bundle\n * Most common combination for interactive components\n *\n * Includes: focus ring, pressed scale, hover shadow, high contrast enhancements\n *\n * @example\n * <button className={cn(DEFAULT_INTERACTIONS, \"bg-primary\")}>Click</button>\n */\nexport const DEFAULT_INTERACTIONS = `${FOCUS_STYLES} ${PRESSED_STYLES} ${HOVER_STYLES} ${HIGH_CONTRAST_FOCUS} ${HIGH_CONTRAST_HOVER} ${HIGH_CONTRAST_PRESSED}`;\n\n/**\n * Subtle interaction bundle\n * For components that need less pronounced feedback\n *\n * Includes: focus ring and high contrast, but no hover shadow or pressed scale\n */\nexport const SUBTLE_INTERACTIONS = `${FOCUS_STYLES} ${HIGH_CONTRAST_FOCUS}`;\n\n/**\n * Non-interactive element styles\n * For elements that should indicate they are not interactive\n */\nexport const NON_INTERACTIVE = \"cursor-default select-none\";\n","\"use client\";\n\nimport { createContext, useContext } from 'react';\nimport type {\n ButtonGroupContextValue,\n ButtonGroupItemContextValue,\n} from './ButtonGroup.types';\n\n/**\n * ButtonGroup Context System (Two-Level)\n *\n * Provides a two-level context pattern for ButtonGroup:\n *\n * 1. ButtonGroupContext (group-level):\n * - Provides: orientation, variant, size, isDisabled\n * - Consumed by: Button (for prop inheritance), Separator (for orientation)\n *\n * 2. ButtonGroupItemContext (item-level):\n * - Provides: position ('first' | 'middle' | 'last' | 'only')\n * - Consumed by: Button (for border-radius styling)\n *\n * Both contexts return null when not in a provider, allowing Button\n * to work standalone without any group context.\n *\n * @see plan.md for architecture details\n * @see ButtonGroup.tsx for Provider implementation\n */\n\n// =============================================================================\n// Group-Level Context\n// =============================================================================\n\n/**\n * Context for group-level props (orientation, variant, size, isDisabled)\n * Default value is null to indicate \"not in a group\"\n */\nconst ButtonGroupContext = createContext<ButtonGroupContextValue | null>(null);\n\nButtonGroupContext.displayName = 'ButtonGroupContext';\n\n/**\n * Hook to access group-level context\n * @returns ButtonGroupContextValue if inside a ButtonGroup, null otherwise\n */\nexport function useButtonGroupContext(): ButtonGroupContextValue | null {\n return useContext(ButtonGroupContext);\n}\n\n// =============================================================================\n// Item-Level Context\n// =============================================================================\n\n/**\n * Context for per-button position information\n * Default value is null to indicate \"not wrapped with position context\"\n */\nconst ButtonGroupItemContext =\n createContext<ButtonGroupItemContextValue | null>(null);\n\nButtonGroupItemContext.displayName = 'ButtonGroupItemContext';\n\n/**\n * Hook to access item-level context (position)\n * @returns ButtonGroupItemContextValue if wrapped with position context, null otherwise\n */\nexport function useButtonGroupItemContext(): ButtonGroupItemContextValue | null {\n return useContext(ButtonGroupItemContext);\n}\n\n// =============================================================================\n// Exports\n// =============================================================================\n\nexport { ButtonGroupContext, ButtonGroupItemContext };\n","import { cva } from 'class-variance-authority';\n\n/**\n * ButtonGroup CVA Variants\n *\n * Defines Class Variance Authority (CVA) variants for:\n * - ButtonGroup container (orientation-based layout)\n * - ButtonGroupItem (position-based border-radius)\n * - ButtonGroupSeparator (orientation-based styling)\n *\n * @see plan.md Phase 1: Design & Contracts - CVA Variants\n * @see constitution.md Principle V (Component Quality Standards)\n */\n\n// =============================================================================\n// Container Variants\n// =============================================================================\n\n/**\n * ButtonGroup container variants\n * Controls the layout direction based on orientation\n * Uses gap-0 to ensure buttons are connected (share borders)\n */\nexport const buttonGroupVariants = cva('inline-flex items-center gap-0', {\n variants: {\n orientation: {\n horizontal: 'flex-row',\n vertical: 'flex-col w-full',\n },\n },\n defaultVariants: {\n orientation: 'horizontal',\n },\n});\n\n// =============================================================================\n// Item Position Variants\n// =============================================================================\n\n/**\n * ButtonGroupItem position variants\n * Applied to Button's visual layer (Layer 2) for position-aware border-radius\n *\n * Compound variants handle both orientation and position combinations:\n * - Horizontal: left/right borders and radii\n * - Vertical: top/bottom borders and radii\n */\nexport const buttonGroupItemVariants = cva('', {\n variants: {\n orientation: {\n // min-w-[44px] ensures visual layer fills touch target width (for icon buttons)\n horizontal: 'min-w-[44px]',\n // flex (overrides inline-flex) + min-h-[44px] makes visual layer fill touch target,\n // eliminating gaps between stacked buttons in vertical orientation\n vertical: 'flex min-h-[44px]',\n },\n position: {\n first: '',\n middle: '',\n last: '',\n only: '', // Single button - no modifications needed\n },\n },\n compoundVariants: [\n // ==========================================================================\n // Horizontal Orientation\n // ==========================================================================\n {\n orientation: 'horizontal',\n position: 'first',\n className: 'rounded-r-none border-r-0',\n },\n {\n orientation: 'horizontal',\n position: 'middle',\n className: 'rounded-none border-r-0',\n },\n {\n orientation: 'horizontal',\n position: 'last',\n className: 'rounded-l-none',\n },\n // ==========================================================================\n // Vertical Orientation\n // Note: w-full is handled by Button's effectiveFullWidth for both layers\n // ==========================================================================\n {\n orientation: 'vertical',\n position: 'first',\n className: 'rounded-b-none border-b-0',\n },\n {\n orientation: 'vertical',\n position: 'middle',\n className: 'rounded-none border-b-0',\n },\n {\n orientation: 'vertical',\n position: 'last',\n className: 'rounded-t-none',\n },\n ],\n defaultVariants: {\n orientation: 'horizontal',\n position: 'only',\n },\n});\n\n// =============================================================================\n// Separator Variants\n// =============================================================================\n\n/**\n * ButtonGroupSeparator variants\n * Orientation-aware visual divider between button groups\n */\nexport const buttonGroupSeparatorVariants = cva('bg-[var(--border)]', {\n variants: {\n orientation: {\n horizontal: 'w-px h-6 mx-1',\n vertical: 'h-px w-full my-1',\n },\n },\n defaultVariants: {\n orientation: 'horizontal',\n },\n});\n","\"use client\";\n\n/**\n * Button Component - 3-Layer Architecture\n * Accessible button with React Aria primitives and CVA styling\n *\n * Architecture:\n * - Layer 1: Touch Target (44x44px WCAG AAA compliant, transparent)\n * - Layer 2: Visual Button (configurable size, colors, borders)\n * - Layer 3: Content & Effects (text, icons, ripple, loading spinner)\n *\n * @see 3layer-plan.md for architecture details\n * @see spec.md FR-029 to FR-037 (Button Component Requirements)\n * @see spec.md FR-009 (WCAG 2.2 AAA - 7:1 contrast ratio)\n * @see spec.md FR-014 (44x44px minimum touch targets)\n * @see constitution.md Principle IV (Accessibility First)\n */\n\nimport { forwardRef, memo, useId } from 'react';\nimport {\n Button as AriaButton,\n type ButtonProps as AriaButtonProps,\n} from 'react-aria-components';\nimport { Loader2, Zap } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport type { ButtonProps } from './Button.types';\nimport { buttonOuterVariants, buttonVisualVariants, buttonVariants } from './Button.styles';\nimport { PRESSED_STYLES, HOVER_STYLES, HIGH_CONTRAST_HOVER, HIGH_CONTRAST_PRESSED } from '../../styles/interaction-states';\nimport {\n useButtonGroupContext,\n useButtonGroupItemContext,\n} from '../ButtonGroup/ButtonGroupContext';\nimport { buttonGroupItemVariants } from '../ButtonGroup/ButtonGroup.variants';\n\n/**\n * Button Component - 3-Layer Architecture\n * Fully accessible button with React Aria and themed styling\n *\n * Layer 1: Touch Target (AriaButton) - 44x44px WCAG AAA compliant\n * Layer 2: Visual Button (span) - configurable appearance\n * Layer 3: Content (children) - text, icons, effects\n */\nconst Button = memo(forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n className,\n buttonVisualClassName,\n variant,\n size,\n visualSize,\n fullWidth,\n loading = false,\n loadingText = \"Loading...\",\n shortcut,\n children,\n isDisabled,\n paywall = false,\n paywallRedirect,\n paywallDescription,\n onPress,\n ...props\n },\n ref\n ) => {\n const paywallDescriptionId = useId();\n\n // ==========================================================================\n // ButtonGroup Context Integration\n // ==========================================================================\n\n // Consume group-level context (variant, size, isDisabled, orientation)\n const groupContext = useButtonGroupContext();\n\n // Consume item-level context (position for border-radius styling)\n const itemContext = useButtonGroupItemContext();\n\n // Merge context values with props (props take precedence)\n const effectiveVariant = variant ?? groupContext?.variant ?? 'default';\n const effectiveSize = size ?? groupContext?.size;\n const effectiveIsDisabled = isDisabled ?? groupContext?.isDisabled ?? false;\n\n // In vertical groups, buttons should be full width automatically\n const isInVerticalGroup = groupContext?.orientation === 'vertical';\n const effectiveFullWidth = fullWidth || isInVerticalGroup;\n\n // Position styling for ButtonGroup (only applied when in a group)\n const positionClassName = itemContext\n ? buttonGroupItemVariants({\n orientation: groupContext?.orientation ?? 'horizontal',\n position: itemContext.position,\n })\n : '';\n\n // Default visualSize to size for backward compatibility\n const effectiveVisualSize = visualSize ?? effectiveSize ?? 'default';\n\n // AAA Accessibility: Warn in dev/test if icon/dot variant lacks accessible name\n if (process.env.NODE_ENV !== 'production') {\n if (\n (effectiveVisualSize === 'dot' || effectiveVisualSize === 'icon') &&\n !props['aria-label'] &&\n !children\n ) {\n console.warn(\n '[Button] visualSize=\"dot\" or \"icon\" requires aria-label when no visible text is provided (WCAG 1.1.1)'\n );\n }\n }\n\n /**\n * Handle button press - intercepts action when paywalled\n * If paywalled with redirect URL, opens in new tab\n * Otherwise, calls the normal onPress handler\n */\n const handlePress = (e: Parameters<NonNullable<AriaButtonProps['onPress']>>[0]): void => {\n if (paywall) {\n if (paywallRedirect) {\n window.open(paywallRedirect, '_blank', 'noopener,noreferrer');\n }\n // Don't call onPress when paywalled\n return;\n }\n onPress?.(e);\n };\n\n // Only set isDisabled when we have a reason to disable\n // Otherwise, let slot system control disabled state (e.g., in NumberField)\n const computedIsDisabled = effectiveIsDisabled || loading || undefined;\n\n return (\n <AriaButton\n ref={ref}\n isDisabled={computedIsDisabled}\n aria-disabled={paywall ? true : undefined}\n aria-describedby={paywall ? paywallDescriptionId : undefined}\n onPress={handlePress}\n className={cn(buttonOuterVariants({ fullWidth: effectiveFullWidth, inVerticalGroup: isInVerticalGroup }), className)}\n {...props}\n >\n {(renderProps) => (\n /* Layer 2: Visual Button */\n <span\n className={cn(\n buttonVisualVariants({\n variant: effectiveVariant,\n visualSize: effectiveVisualSize,\n paywall,\n fullWidth: effectiveFullWidth,\n }),\n // Position styling from ButtonGroup context (border-radius adjustments)\n positionClassName,\n buttonVisualClassName,\n // Layer 2 interaction styles (no focus - focus ring is on Layer 1)\n PRESSED_STYLES,\n HOVER_STYLES,\n HIGH_CONTRAST_HOVER,\n HIGH_CONTRAST_PRESSED\n )}\n data-pressed={renderProps.isPressed || undefined}\n >\n {/* Layer 3: Content & Effects */}\n\n {/* FR-033: Loading spinner with screen reader announcement */}\n {/* Uses motion-safe: for WCAG 2.3.3 AAA (Animation from Interactions) */}\n {loading && (\n <>\n <Loader2 className=\"motion-safe:animate-spin\" aria-hidden=\"true\" />\n <span className=\"sr-only\" aria-live=\"polite\">\n {loadingText}\n </span>\n </>\n )}\n\n {/* Hide children during loading */}\n {!loading && children}\n\n {/* Paywall: Lightning bolt icon */}\n {paywall && (\n <Zap\n data-testid=\"zap-icon\"\n aria-hidden=\"true\"\n className=\"ml-1\"\n />\n )}\n\n {/* Paywall: Screen reader description */}\n {paywall && (\n <span id={paywallDescriptionId} className=\"sr-only\">\n Premium feature: {paywallDescription || \"Upgrade required to access this feature\"}\n </span>\n )}\n\n {/* FR-034: Keyboard shortcut display on focus */}\n {renderProps.isFocusVisible && shortcut && (\n <kbd className=\"ml-auto hidden text-xs opacity-60 lg:inline\">\n {shortcut}\n </kbd>\n )}\n\n {/* Touch/press ripple effect - FR-031: Pressed state feedback */}\n {/* Uses motion-safe: for WCAG 2.3.3 AAA (Animation from Interactions) */}\n {renderProps.isPressed && (\n <span\n className=\"absolute inset-0 rounded-[inherit] bg-current opacity-10 motion-safe:animate-in motion-safe:zoom-in-95\"\n aria-hidden=\"true\"\n />\n )}\n </span>\n )}\n </AriaButton>\n );\n }\n));\n\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants, buttonOuterVariants, buttonVisualVariants };\nexport type { ButtonProps } from './Button.types';\n","import { cva } from 'class-variance-authority';\n\nexport const paginationNavVariants = cva(\n 'flex items-center justify-center',\n {\n variants: {\n size: {\n sm: 'gap-0.5',\n default: 'gap-1',\n lg: 'gap-1.5',\n },\n },\n defaultVariants: { size: 'default' },\n }\n);\n\nexport const paginationEllipsisVariants = cva(\n 'inline-flex items-center justify-center text-[var(--muted-foreground)]',\n {\n variants: {\n size: {\n sm: 'h-8 w-8 text-xs',\n default: 'h-10 w-10 text-sm',\n lg: 'h-12 w-12 text-base',\n },\n },\n defaultVariants: { size: 'default' },\n }\n);\n\nexport const paginationInfoVariants = cva(\n 'text-[var(--muted-foreground)]',\n {\n variants: {\n size: {\n sm: 'text-xs',\n default: 'text-sm',\n lg: 'text-base',\n },\n },\n defaultVariants: { size: 'default' },\n }\n);\n","'use client';\n\nimport { forwardRef, useMemo, useCallback, type ReactElement } from 'react';\nimport { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport { Button } from '../Button/Button';\nimport type { ButtonProps } from '../Button/Button.types';\nimport type { PaginationProps, PaginationVariant } from './Pagination.types';\nimport {\n paginationNavVariants,\n paginationEllipsisVariants,\n} from './Pagination.styles';\n\n/**\n * Generate page numbers array with ellipsis\n * Returns array like: [1, '...', 4, 5, 6, '...', 10]\n */\nfunction generatePageNumbers(\n currentPage: number,\n totalPages: number,\n siblingCount: number\n): (number | 'ellipsis-start' | 'ellipsis-end')[] {\n // Total page numbers to show (first + last + current + siblings + 2 ellipses)\n const totalPageNumbers = siblingCount * 2 + 5;\n\n // If total pages is less than the numbers we want to show, show all\n if (totalPages <= totalPageNumbers) {\n return Array.from({ length: totalPages }, (_, i) => i + 1);\n }\n\n const leftSiblingIndex = Math.max(currentPage - siblingCount, 1);\n const rightSiblingIndex = Math.min(currentPage + siblingCount, totalPages);\n\n const showLeftEllipsis = leftSiblingIndex > 2;\n const showRightEllipsis = rightSiblingIndex < totalPages - 1;\n\n if (!showLeftEllipsis && showRightEllipsis) {\n const leftRange = siblingCount * 2 + 3;\n const pages: (number | 'ellipsis-start' | 'ellipsis-end')[] = Array.from(\n { length: leftRange },\n (_, i) => i + 1\n );\n pages.push('ellipsis-end');\n pages.push(totalPages);\n return pages;\n }\n\n if (showLeftEllipsis && !showRightEllipsis) {\n const rightRange = siblingCount * 2 + 3;\n const pages: (number | 'ellipsis-start' | 'ellipsis-end')[] = [1, 'ellipsis-start'];\n for (let i = totalPages - rightRange + 1; i <= totalPages; i++) {\n pages.push(i);\n }\n return pages;\n }\n\n // Both ellipses\n const pages: (number | 'ellipsis-start' | 'ellipsis-end')[] = [1, 'ellipsis-start'];\n for (let i = leftSiblingIndex; i <= rightSiblingIndex; i++) {\n pages.push(i);\n }\n pages.push('ellipsis-end');\n pages.push(totalPages);\n return pages;\n}\n\n/**\n * Map Pagination variant to Button variant for inactive buttons\n */\nfunction getButtonVariant(paginationVariant: PaginationVariant, isActive: boolean): ButtonProps['variant'] {\n if (isActive) return 'default';\n if (paginationVariant === 'outline') return 'outline';\n return 'ghost';\n}\n\n/**\n * Map Pagination size to Button visualSize\n */\nfunction getButtonVisualSize(size: 'sm' | 'default' | 'lg'): ButtonProps['visualSize'] {\n if (size === 'sm') return 'sm';\n if (size === 'lg') return 'lg';\n return 'default';\n}\n\n/**\n * Pagination Component\n *\n * Navigation for paginated content with page numbers, prev/next,\n * and first/last buttons. Uses the Themis Button component for\n * three-layer architecture and WCAG 2.2 AAA 44x44px touch targets.\n *\n * @example\n * ```tsx\n * <Pagination\n * page={currentPage}\n * totalPages={10}\n * onPageChange={setCurrentPage}\n * />\n * ```\n */\nexport const Pagination = forwardRef<HTMLElement, PaginationProps>(\n (\n {\n page,\n totalPages,\n onPageChange,\n size = 'default',\n variant = 'default',\n siblingCount = 1,\n showFirstLast = true,\n showPrevNext = true,\n isDisabled = false,\n labels,\n className,\n 'aria-label': ariaLabel,\n 'data-testid': dataTestId,\n ...props\n },\n ref\n ): ReactElement => {\n const resolvedLabels = {\n previous: labels?.previous ?? 'Previous',\n next: labels?.next ?? 'Next',\n first: labels?.first ?? 'First page',\n last: labels?.last ?? 'Last page',\n page: labels?.page ?? 'Page',\n of: labels?.of ?? 'of',\n };\n\n const pages = useMemo(\n () => generatePageNumbers(page, totalPages, siblingCount),\n [page, totalPages, siblingCount]\n );\n\n const handlePageChange = useCallback(\n (newPage: number) => {\n if (newPage >= 1 && newPage <= totalPages && newPage !== page) {\n onPageChange(newPage);\n }\n },\n [page, totalPages, onPageChange]\n );\n\n const isFirstPage = page === 1;\n const isLastPage = page === totalPages;\n const visualSize = getButtonVisualSize(size);\n\n return (\n <nav\n ref={ref as React.Ref<HTMLElement>}\n aria-label={ariaLabel ?? 'Pagination'}\n className={cn(paginationNavVariants({ size }), className)}\n data-testid={dataTestId}\n {...props}\n >\n {/* First Page Button */}\n {showFirstLast && (\n <Button\n variant={getButtonVariant(variant, false)}\n visualSize=\"icon\"\n isDisabled={isDisabled || isFirstPage}\n aria-label={resolvedLabels.first}\n onPress={() => handlePageChange(1)}\n >\n <ChevronsLeft aria-hidden=\"true\" />\n </Button>\n )}\n\n {/* Previous Button */}\n {showPrevNext && (\n <Button\n variant={getButtonVariant(variant, false)}\n visualSize=\"icon\"\n isDisabled={isDisabled || isFirstPage}\n aria-label={resolvedLabels.previous}\n onPress={() => handlePageChange(page - 1)}\n >\n <ChevronLeft aria-hidden=\"true\" />\n </Button>\n )}\n\n {/* Page Numbers */}\n {pages.map((pageItem) => {\n if (pageItem === 'ellipsis-start' || pageItem === 'ellipsis-end') {\n return (\n <span\n key={pageItem}\n className={paginationEllipsisVariants({ size })}\n aria-hidden=\"true\"\n >\n ...\n </span>\n );\n }\n\n const isActive = pageItem === page;\n return (\n <Button\n key={pageItem}\n variant={getButtonVariant(variant, isActive)}\n visualSize={visualSize}\n isDisabled={isDisabled || isActive}\n aria-label={`${resolvedLabels.page} ${pageItem}`}\n aria-current={isActive ? 'page' : undefined}\n onPress={() => handlePageChange(pageItem)}\n >\n {pageItem}\n </Button>\n );\n })}\n\n {/* Next Button */}\n {showPrevNext && (\n <Button\n variant={getButtonVariant(variant, false)}\n visualSize=\"icon\"\n isDisabled={isDisabled || isLastPage}\n aria-label={resolvedLabels.next}\n onPress={() => handlePageChange(page + 1)}\n >\n <ChevronRight aria-hidden=\"true\" />\n </Button>\n )}\n\n {/* Last Page Button */}\n {showFirstLast && (\n <Button\n variant={getButtonVariant(variant, false)}\n visualSize=\"icon\"\n isDisabled={isDisabled || isLastPage}\n aria-label={resolvedLabels.last}\n onPress={() => handlePageChange(totalPages)}\n >\n <ChevronsRight aria-hidden=\"true\" />\n </Button>\n )}\n\n {/* Screen reader announcement for page context */}\n <span className=\"sr-only\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n {resolvedLabels.page} {page} {resolvedLabels.of} {totalPages}\n </span>\n </nav>\n );\n }\n);\n\nPagination.displayName = 'Pagination';\n","import { z } from 'zod';\n\n/**\n * Base props schema for all Themis components\n * Ensures consistent accessibility and styling APIs across the library\n *\n * @see spec.md FR-009 to FR-014 (Accessibility Requirements)\n * @see constitution.md Principle IV (Accessibility First - WCAG 2.2 AA minimum)\n */\nexport const BaseComponentPropsSchema = z.object({\n // Styling\n className: z.string().optional(),\n\n // React\n children: z.any().optional(), // ReactNode not directly supported by Zod\n id: z.string().optional(),\n\n // Accessibility (WCAG 2.2 AA requirements)\n 'aria-label': z.string().optional(),\n 'aria-labelledby': z.string().optional(),\n 'aria-describedby': z.string().optional(),\n 'aria-live': z.enum(['off', 'polite', 'assertive']).optional(),\n 'aria-hidden': z.boolean().optional(),\n\n // Testing & Development\n 'data-testid': z.string().optional(),\n});\n\nexport type BaseComponentProps = z.infer<typeof BaseComponentPropsSchema>;\n","import { z } from 'zod';\nimport { BaseComponentPropsSchema } from '../../schemas/BaseComponentProps';\n\nexport const PaginationSizeSchema = z.enum(['sm', 'default', 'lg']);\nexport type PaginationSize = z.infer<typeof PaginationSizeSchema>;\n\nexport const PaginationVariantSchema = z.enum(['default', 'outline', 'ghost']);\nexport type PaginationVariant = z.infer<typeof PaginationVariantSchema>;\n\nexport const PaginationPropsSchema = BaseComponentPropsSchema.extend({\n /** Current page (1-indexed) */\n page: z.number().min(1),\n\n /** Total number of pages */\n totalPages: z.number().min(1),\n\n /** Callback when page changes */\n onPageChange: z.custom<(page: number) => void>(),\n\n /** Size variant */\n size: PaginationSizeSchema.optional().default('default'),\n\n /** Visual variant */\n variant: PaginationVariantSchema.optional().default('default'),\n\n /** Number of sibling pages to show around current page */\n siblingCount: z.number().min(0).optional().default(1),\n\n /** Whether to show first/last page buttons */\n showFirstLast: z.boolean().optional().default(true),\n\n /** Whether to show previous/next buttons */\n showPrevNext: z.boolean().optional().default(true),\n\n /** Whether the pagination is disabled */\n isDisabled: z.boolean().optional().default(false),\n\n /** Custom labels */\n labels: z.object({\n previous: z.string().optional().default('Previous'),\n next: z.string().optional().default('Next'),\n first: z.string().optional().default('First page'),\n last: z.string().optional().default('Last page'),\n page: z.string().optional().default('Page'),\n of: z.string().optional().default('of'),\n }).optional(),\n});\n\nexport type PaginationProps = z.infer<typeof PaginationPropsSchema>;\n"]}
|
|
@@ -1,332 +1,3 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
var react = require('react');
|
|
5
|
-
var reactAriaComponents = require('react-aria-components');
|
|
6
|
-
var classVarianceAuthority = require('class-variance-authority');
|
|
7
|
-
var lucideReact = require('lucide-react');
|
|
8
|
-
var clsx = require('clsx');
|
|
9
|
-
var tailwindMerge = require('tailwind-merge');
|
|
10
|
-
var jsxRuntime = require('react/jsx-runtime');
|
|
11
|
-
|
|
12
|
-
// src/elements/Panel/Panel.tsx
|
|
13
|
-
function cn(...inputs) {
|
|
14
|
-
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
15
|
-
}
|
|
16
|
-
var panelContentVariants = classVarianceAuthority.cva(
|
|
17
|
-
[
|
|
18
|
-
"flex flex-col h-full",
|
|
19
|
-
"bg-[var(--content-background)] text-[var(--content-foreground)]",
|
|
20
|
-
"shadow-lg"
|
|
21
|
-
],
|
|
22
|
-
{
|
|
23
|
-
variants: {
|
|
24
|
-
position: {
|
|
25
|
-
right: "border-l border-[var(--border)]",
|
|
26
|
-
left: "border-r border-[var(--border)]"
|
|
27
|
-
},
|
|
28
|
-
width: {
|
|
29
|
-
sm: "w-[300px]",
|
|
30
|
-
md: "w-[400px]",
|
|
31
|
-
lg: "w-[500px]",
|
|
32
|
-
xl: "w-[600px]",
|
|
33
|
-
auto: "w-auto"
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
defaultVariants: {
|
|
37
|
-
position: "right",
|
|
38
|
-
width: "md"
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
);
|
|
42
|
-
var panelHeaderVariants = classVarianceAuthority.cva([
|
|
43
|
-
"flex items-start justify-between",
|
|
44
|
-
"px-4 py-4",
|
|
45
|
-
"border-b border-[var(--border)]"
|
|
46
|
-
]);
|
|
47
|
-
var panelBodyVariants = classVarianceAuthority.cva(["flex-1 overflow-y-auto", "px-4 py-4"]);
|
|
48
|
-
var panelFooterVariants = classVarianceAuthority.cva([
|
|
49
|
-
"flex items-center justify-end gap-2",
|
|
50
|
-
"px-4 py-4",
|
|
51
|
-
"border-t border-[var(--border)]"
|
|
52
|
-
]);
|
|
53
|
-
var panelCloseButtonVariants = classVarianceAuthority.cva([
|
|
54
|
-
"inline-flex items-center justify-center rounded-md",
|
|
55
|
-
"min-h-[44px] min-w-[44px]",
|
|
56
|
-
"text-[var(--menu-muted)]",
|
|
57
|
-
"hover:bg-[var(--accent)] hover:text-[var(--accent-foreground)]",
|
|
58
|
-
"focus-visible:outline-none focus-visible:ring-2",
|
|
59
|
-
"focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2"
|
|
60
|
-
// No transition - instant state changes (v1)
|
|
61
|
-
]);
|
|
62
|
-
var PanelContext = react.createContext(null);
|
|
63
|
-
function usePanelContext() {
|
|
64
|
-
const context = react.useContext(PanelContext);
|
|
65
|
-
if (!context) {
|
|
66
|
-
throw new Error("Panel compound components must be used within a Panel");
|
|
67
|
-
}
|
|
68
|
-
return context;
|
|
69
|
-
}
|
|
70
|
-
function useMediaQuery(query) {
|
|
71
|
-
const [matches, setMatches] = react.useState(() => {
|
|
72
|
-
if (typeof window === "undefined") return false;
|
|
73
|
-
return window.matchMedia(query).matches;
|
|
74
|
-
});
|
|
75
|
-
react.useEffect(() => {
|
|
76
|
-
if (typeof window === "undefined") return;
|
|
77
|
-
const media = window.matchMedia(query);
|
|
78
|
-
if (media.matches !== matches) {
|
|
79
|
-
setMatches(media.matches);
|
|
80
|
-
}
|
|
81
|
-
const listener = (e) => setMatches(e.matches);
|
|
82
|
-
media.addEventListener("change", listener);
|
|
83
|
-
return () => media.removeEventListener("change", listener);
|
|
84
|
-
}, [query, matches]);
|
|
85
|
-
return matches;
|
|
86
|
-
}
|
|
87
|
-
function PanelRoot({
|
|
88
|
-
children,
|
|
89
|
-
isOpen: controlledIsOpen,
|
|
90
|
-
defaultOpen = false,
|
|
91
|
-
onOpenChange,
|
|
92
|
-
onClose,
|
|
93
|
-
position = "right",
|
|
94
|
-
width = "md",
|
|
95
|
-
mobileBreakpoint = 768
|
|
96
|
-
}) {
|
|
97
|
-
const [uncontrolledIsOpen, setUncontrolledIsOpen] = react.useState(defaultOpen);
|
|
98
|
-
const isControlled = controlledIsOpen !== void 0;
|
|
99
|
-
const isOpen = isControlled ? controlledIsOpen : uncontrolledIsOpen;
|
|
100
|
-
const triggerRef = react.useRef(null);
|
|
101
|
-
const uniqueId = react.useId();
|
|
102
|
-
const panelId = `panel-${uniqueId}`;
|
|
103
|
-
const titleId = `panel-title-${uniqueId}`;
|
|
104
|
-
const isMobile = useMediaQuery(`(max-width: ${mobileBreakpoint - 1}px)`);
|
|
105
|
-
const setIsOpen = react.useCallback(
|
|
106
|
-
(open) => {
|
|
107
|
-
if (!isControlled) {
|
|
108
|
-
setUncontrolledIsOpen(open);
|
|
109
|
-
}
|
|
110
|
-
onOpenChange?.(open);
|
|
111
|
-
},
|
|
112
|
-
[isControlled, onOpenChange]
|
|
113
|
-
);
|
|
114
|
-
const handleClose = react.useCallback(() => {
|
|
115
|
-
setIsOpen(false);
|
|
116
|
-
onClose?.();
|
|
117
|
-
if (triggerRef.current) {
|
|
118
|
-
window.requestAnimationFrame(() => {
|
|
119
|
-
triggerRef.current?.focus();
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
}, [setIsOpen, onClose]);
|
|
123
|
-
react.useEffect(() => {
|
|
124
|
-
if (!isOpen || isMobile) return;
|
|
125
|
-
const handleKeyDown = (e) => {
|
|
126
|
-
if (e.key === "Escape") {
|
|
127
|
-
e.preventDefault();
|
|
128
|
-
handleClose();
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
document.addEventListener("keydown", handleKeyDown);
|
|
132
|
-
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
133
|
-
}, [isOpen, isMobile, handleClose]);
|
|
134
|
-
const contextValue = {
|
|
135
|
-
isOpen,
|
|
136
|
-
setIsOpen,
|
|
137
|
-
panelId,
|
|
138
|
-
titleId,
|
|
139
|
-
triggerRef,
|
|
140
|
-
position,
|
|
141
|
-
width,
|
|
142
|
-
mobileBreakpoint,
|
|
143
|
-
isMobile,
|
|
144
|
-
handleClose
|
|
145
|
-
};
|
|
146
|
-
return /* @__PURE__ */ jsxRuntime.jsx(PanelContext.Provider, { value: contextValue, children });
|
|
147
|
-
}
|
|
148
|
-
PanelRoot.displayName = "Panel";
|
|
149
|
-
function PanelTrigger({
|
|
150
|
-
children,
|
|
151
|
-
asChild = true
|
|
152
|
-
}) {
|
|
153
|
-
const { isOpen, setIsOpen, panelId, triggerRef } = usePanelContext();
|
|
154
|
-
const setRef = react.useCallback(
|
|
155
|
-
(element) => {
|
|
156
|
-
triggerRef.current = element;
|
|
157
|
-
},
|
|
158
|
-
[triggerRef]
|
|
159
|
-
);
|
|
160
|
-
const handlePress = react.useCallback(() => {
|
|
161
|
-
setIsOpen(!isOpen);
|
|
162
|
-
}, [setIsOpen, isOpen]);
|
|
163
|
-
if (asChild && react.isValidElement(children)) {
|
|
164
|
-
const childProps = children.props;
|
|
165
|
-
return react.cloneElement(children, {
|
|
166
|
-
ref: setRef,
|
|
167
|
-
"aria-expanded": isOpen,
|
|
168
|
-
"aria-controls": isOpen ? panelId : void 0,
|
|
169
|
-
"data-testid": "panel-trigger",
|
|
170
|
-
onPress: handlePress,
|
|
171
|
-
onClick: childProps.onClick ? () => {
|
|
172
|
-
childProps.onClick?.();
|
|
173
|
-
handlePress();
|
|
174
|
-
} : handlePress
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
178
|
-
reactAriaComponents.Button,
|
|
179
|
-
{
|
|
180
|
-
ref: setRef,
|
|
181
|
-
"aria-expanded": isOpen,
|
|
182
|
-
"aria-controls": isOpen ? panelId : void 0,
|
|
183
|
-
onPress: handlePress,
|
|
184
|
-
"data-testid": "panel-trigger",
|
|
185
|
-
children
|
|
186
|
-
}
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
|
-
PanelTrigger.displayName = "PanelTrigger";
|
|
190
|
-
function BuiltInCloseButton() {
|
|
191
|
-
const { handleClose } = usePanelContext();
|
|
192
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
193
|
-
reactAriaComponents.Button,
|
|
194
|
-
{
|
|
195
|
-
"aria-label": "Close panel",
|
|
196
|
-
"data-testid": "panel-close",
|
|
197
|
-
onPress: handleClose,
|
|
198
|
-
className: panelCloseButtonVariants(),
|
|
199
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-5 w-5" })
|
|
200
|
-
}
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
function PanelContent({
|
|
204
|
-
children,
|
|
205
|
-
showClose = true,
|
|
206
|
-
className
|
|
207
|
-
}) {
|
|
208
|
-
const { isOpen, isMobile, panelId, titleId, position, width, handleClose } = usePanelContext();
|
|
209
|
-
if (!isOpen) return null;
|
|
210
|
-
if (isMobile) {
|
|
211
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
212
|
-
reactAriaComponents.ModalOverlay,
|
|
213
|
-
{
|
|
214
|
-
isDismissable: true,
|
|
215
|
-
isOpen,
|
|
216
|
-
onOpenChange: (open) => !open && handleClose(),
|
|
217
|
-
className: "fixed inset-0 z-50 bg-black/50",
|
|
218
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(reactAriaComponents.Modal, { className: "fixed inset-0 z-50 flex items-center justify-center p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
219
|
-
reactAriaComponents.Dialog,
|
|
220
|
-
{
|
|
221
|
-
"aria-labelledby": titleId,
|
|
222
|
-
className: cn(
|
|
223
|
-
"w-full max-w-lg rounded-lg bg-[var(--content-background)] shadow-lg outline-none",
|
|
224
|
-
"flex max-h-[90vh] flex-col",
|
|
225
|
-
className
|
|
226
|
-
),
|
|
227
|
-
children: [
|
|
228
|
-
showClose && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-2 top-2", children: /* @__PURE__ */ jsxRuntime.jsx(BuiltInCloseButton, {}) }),
|
|
229
|
-
children
|
|
230
|
-
]
|
|
231
|
-
}
|
|
232
|
-
) })
|
|
233
|
-
}
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
237
|
-
"aside",
|
|
238
|
-
{
|
|
239
|
-
id: panelId,
|
|
240
|
-
"aria-labelledby": titleId,
|
|
241
|
-
"data-testid": "panel-content",
|
|
242
|
-
className: cn(panelContentVariants({ position, width }), className),
|
|
243
|
-
children: [
|
|
244
|
-
children,
|
|
245
|
-
showClose && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-2 top-2", children: /* @__PURE__ */ jsxRuntime.jsx(BuiltInCloseButton, {}) })
|
|
246
|
-
]
|
|
247
|
-
}
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
PanelContent.displayName = "PanelContent";
|
|
251
|
-
function PanelHeader({
|
|
252
|
-
children,
|
|
253
|
-
className
|
|
254
|
-
}) {
|
|
255
|
-
return /* @__PURE__ */ jsxRuntime.jsx("header", { className: cn(panelHeaderVariants(), className), children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-1", children }) });
|
|
256
|
-
}
|
|
257
|
-
PanelHeader.displayName = "PanelHeader";
|
|
258
|
-
function PanelTitle({
|
|
259
|
-
children,
|
|
260
|
-
as: Tag = "h2",
|
|
261
|
-
className
|
|
262
|
-
}) {
|
|
263
|
-
const { titleId } = usePanelContext();
|
|
264
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
265
|
-
Tag,
|
|
266
|
-
{
|
|
267
|
-
id: titleId,
|
|
268
|
-
"aria-live": "polite",
|
|
269
|
-
className: cn(
|
|
270
|
-
"text-lg font-semibold text-[var(--content-foreground)]",
|
|
271
|
-
className
|
|
272
|
-
),
|
|
273
|
-
children
|
|
274
|
-
}
|
|
275
|
-
);
|
|
276
|
-
}
|
|
277
|
-
PanelTitle.displayName = "PanelTitle";
|
|
278
|
-
function PanelDescription({
|
|
279
|
-
children,
|
|
280
|
-
className
|
|
281
|
-
}) {
|
|
282
|
-
return /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn("text-sm text-[var(--menu-muted)]", className), children });
|
|
283
|
-
}
|
|
284
|
-
PanelDescription.displayName = "PanelDescription";
|
|
285
|
-
function PanelBody({ children, className }) {
|
|
286
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(panelBodyVariants(), className), children });
|
|
287
|
-
}
|
|
288
|
-
PanelBody.displayName = "PanelBody";
|
|
289
|
-
function PanelFooter({
|
|
290
|
-
children,
|
|
291
|
-
className
|
|
292
|
-
}) {
|
|
293
|
-
return /* @__PURE__ */ jsxRuntime.jsx("footer", { className: cn(panelFooterVariants(), className), children });
|
|
294
|
-
}
|
|
295
|
-
PanelFooter.displayName = "PanelFooter";
|
|
296
|
-
function PanelClose({ children }) {
|
|
297
|
-
const { handleClose } = usePanelContext();
|
|
298
|
-
if (!react.isValidElement(children)) {
|
|
299
|
-
return null;
|
|
300
|
-
}
|
|
301
|
-
const childProps = children.props;
|
|
302
|
-
return react.cloneElement(children, {
|
|
303
|
-
onPress: () => {
|
|
304
|
-
childProps.onPress?.();
|
|
305
|
-
handleClose();
|
|
306
|
-
},
|
|
307
|
-
onClick: () => {
|
|
308
|
-
childProps.onClick?.();
|
|
309
|
-
handleClose();
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
PanelClose.displayName = "PanelClose";
|
|
314
|
-
var Panel = Object.assign(PanelRoot, {
|
|
315
|
-
Trigger: PanelTrigger,
|
|
316
|
-
Content: PanelContent,
|
|
317
|
-
Header: PanelHeader,
|
|
318
|
-
Title: PanelTitle,
|
|
319
|
-
Description: PanelDescription,
|
|
320
|
-
Body: PanelBody,
|
|
321
|
-
Footer: PanelFooter,
|
|
322
|
-
Close: PanelClose
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
exports.Panel = Panel;
|
|
326
|
-
exports.panelBodyVariants = panelBodyVariants;
|
|
327
|
-
exports.panelCloseButtonVariants = panelCloseButtonVariants;
|
|
328
|
-
exports.panelContentVariants = panelContentVariants;
|
|
329
|
-
exports.panelFooterVariants = panelFooterVariants;
|
|
330
|
-
exports.panelHeaderVariants = panelHeaderVariants;
|
|
331
|
-
//# sourceMappingURL=index.js.map
|
|
2
|
+
'use strict';var react=require('react'),reactAriaComponents=require('react-aria-components'),classVarianceAuthority=require('class-variance-authority'),lucideReact=require('lucide-react'),clsx=require('clsx'),tailwindMerge=require('tailwind-merge'),jsxRuntime=require('react/jsx-runtime');function d(...e){return tailwindMerge.twMerge(clsx.clsx(e))}var B=classVarianceAuthority.cva(["flex flex-col h-full","bg-[var(--content-background)] text-[var(--content-foreground)]","shadow-lg"],{variants:{position:{right:"border-l border-[var(--border)]",left:"border-r border-[var(--border)]"},width:{sm:"w-[300px]",md:"w-[400px]",lg:"w-[500px]",xl:"w-[600px]",auto:"w-auto"}},defaultVariants:{position:"right",width:"md"}}),O=classVarianceAuthority.cva(["flex items-start justify-between","px-4 py-4","border-b border-[var(--border)]"]),I=classVarianceAuthority.cva(["flex-1 overflow-y-auto","px-4 py-4"]),D=classVarianceAuthority.cva(["flex items-center justify-end gap-2","px-4 py-4","border-t border-[var(--border)]"]),H=classVarianceAuthority.cva(["inline-flex items-center justify-center rounded-md","min-h-[44px] min-w-[44px]","text-[var(--menu-muted)]","hover:bg-[var(--accent)] hover:text-[var(--accent-foreground)]","focus-visible:outline-none focus-visible:ring-2","focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2"]),A=react.createContext(null);function f(){let e=react.useContext(A);if(!e)throw new Error("Panel compound components must be used within a Panel");return e}function re(e){let[n,t]=react.useState(()=>typeof window>"u"?false:window.matchMedia(e).matches);return react.useEffect(()=>{if(typeof window>"u")return;let o=window.matchMedia(e);o.matches!==n&&t(o.matches);let r=s=>t(s.matches);return o.addEventListener("change",r),()=>o.removeEventListener("change",r)},[e,n]),n}function F({children:e,isOpen:n,defaultOpen:t=false,onOpenChange:o,onClose:r,position:s="right",width:p="md",mobileBreakpoint:l=768}){let[i,g]=react.useState(t),c=n!==void 0,x=c?n:i,v=react.useRef(null),C=react.useId(),U=`panel-${C}`,X=`panel-title-${C}`,b=re(`(max-width: ${l-1}px)`),y=react.useCallback(u=>{c||g(u),o?.(u);},[c,o]),h=react.useCallback(()=>{y(false),r?.(),v.current&&window.requestAnimationFrame(()=>{v.current?.focus();});},[y,r]);react.useEffect(()=>{if(!x||b)return;let u=w=>{w.key==="Escape"&&(w.preventDefault(),h());};return document.addEventListener("keydown",u),()=>document.removeEventListener("keydown",u)},[x,b,h]);let q={isOpen:x,setIsOpen:y,panelId:U,titleId:X,triggerRef:v,position:s,width:p,mobileBreakpoint:l,isMobile:b,handleClose:h};return jsxRuntime.jsx(A.Provider,{value:q,children:e})}F.displayName="Panel";function L({children:e,asChild:n=true}){let{isOpen:t,setIsOpen:o,panelId:r,triggerRef:s}=f(),p=react.useCallback(i=>{s.current=i;},[s]),l=react.useCallback(()=>{o(!t);},[o,t]);if(n&&react.isValidElement(e)){let i=e.props;return react.cloneElement(e,{ref:p,"aria-expanded":t,"aria-controls":t?r:void 0,"data-testid":"panel-trigger",onPress:l,onClick:i.onClick?()=>{i.onClick?.(),l();}:l})}return jsxRuntime.jsx(reactAriaComponents.Button,{ref:p,"aria-expanded":t,"aria-controls":t?r:void 0,onPress:l,"data-testid":"panel-trigger",children:e})}L.displayName="PanelTrigger";function E(){let{handleClose:e}=f();return jsxRuntime.jsx(reactAriaComponents.Button,{"aria-label":"Close panel","data-testid":"panel-close",onPress:e,className:H(),children:jsxRuntime.jsx(lucideReact.X,{className:"h-5 w-5"})})}function j({children:e,showClose:n=true,className:t}){let{isOpen:o,isMobile:r,panelId:s,titleId:p,position:l,width:i,handleClose:g}=f();return o?r?jsxRuntime.jsx(reactAriaComponents.ModalOverlay,{isDismissable:true,isOpen:o,onOpenChange:c=>!c&&g(),className:"fixed inset-0 z-50 bg-black/50",children:jsxRuntime.jsx(reactAriaComponents.Modal,{className:"fixed inset-0 z-50 flex items-center justify-center p-4",children:jsxRuntime.jsxs(reactAriaComponents.Dialog,{"aria-labelledby":p,className:d("w-full max-w-lg rounded-lg bg-[var(--content-background)] shadow-lg outline-none","flex max-h-[90vh] flex-col",t),children:[n&&jsxRuntime.jsx("div",{className:"absolute right-2 top-2",children:jsxRuntime.jsx(E,{})}),e]})})}):jsxRuntime.jsxs("aside",{id:s,"aria-labelledby":p,"data-testid":"panel-content",className:d(B({position:l,width:i}),t),children:[e,n&&jsxRuntime.jsx("div",{className:"absolute right-2 top-2",children:jsxRuntime.jsx(E,{})})]}):null}j.displayName="PanelContent";function W({children:e,className:n}){return jsxRuntime.jsx("header",{className:d(O(),n),children:jsxRuntime.jsx("div",{className:"flex flex-col gap-1",children:e})})}W.displayName="PanelHeader";function $({children:e,as:n="h2",className:t}){let{titleId:o}=f();return jsxRuntime.jsx(n,{id:o,"aria-live":"polite",className:d("text-lg font-semibold text-[var(--content-foreground)]",t),children:e})}$.displayName="PanelTitle";function z({children:e,className:n}){return jsxRuntime.jsx("p",{className:d("text-sm text-[var(--menu-muted)]",n),children:e})}z.displayName="PanelDescription";function K({children:e,className:n}){return jsxRuntime.jsx("div",{className:d(I(),n),children:e})}K.displayName="PanelBody";function Q({children:e,className:n}){return jsxRuntime.jsx("footer",{className:d(D(),n),children:e})}Q.displayName="PanelFooter";function S({children:e}){let{handleClose:n}=f();if(!react.isValidElement(e))return null;let t=e.props;return react.cloneElement(e,{onPress:()=>{t.onPress?.(),n();},onClick:()=>{t.onClick?.(),n();}})}S.displayName="PanelClose";var le=Object.assign(F,{Trigger:L,Content:j,Header:W,Title:$,Description:z,Body:K,Footer:Q,Close:S});exports.Panel=le;exports.panelBodyVariants=I;exports.panelCloseButtonVariants=H;exports.panelContentVariants=B;exports.panelFooterVariants=D;exports.panelHeaderVariants=O;//# sourceMappingURL=index.js.map
|
|
332
3
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utils/cn.ts","../../../src/elements/Panel/Panel.tsx"],"names":["twMerge","clsx","cva","createContext","useContext","useState","useEffect","useRef","useId","useCallback","isValidElement","cloneElement","jsx","AriaButton","X","AriaModalOverlay","AriaModal","jsxs","AriaDialog"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Class Name Utility\n * Merges Tailwind CSS classes with conflict resolution\n *\n * Combines clsx for conditional classes and tailwind-merge for deduplication\n *\n * @example\n * cn('px-2 py-1', 'px-4') // => 'py-1 px-4' (px-4 overrides px-2)\n * cn('text-red-500', condition && 'text-blue-500') // => conditional application\n */\n\nimport { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\n/**\n * Panel Component - Accessible Non-Modal Sidebar\n * Disclosure pattern + Complementary region for WCAG 2.2 AAA\n *\n * @see plan.md for architecture details\n * @see panel-prd.md for requirements\n * @see ADR 0007 for accessibility decisions\n */\n\nimport {\n type ReactElement,\n type MutableRefObject,\n createContext,\n useContext,\n useState,\n useId,\n useRef,\n useCallback,\n useEffect,\n cloneElement,\n isValidElement,\n} from \"react\";\nimport {\n Button as AriaButton,\n Modal as AriaModal,\n ModalOverlay as AriaModalOverlay,\n Dialog as AriaDialog,\n} from \"react-aria-components\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { X } from \"lucide-react\";\nimport { cn } from \"../../utils/cn\";\nimport type {\n PanelProps,\n PanelTriggerProps,\n PanelContentProps,\n PanelHeaderProps,\n PanelTitleProps,\n PanelDescriptionProps,\n PanelBodyProps,\n PanelFooterProps,\n PanelCloseProps,\n PanelPosition,\n PanelWidth,\n} from \"./Panel.types\";\n\n// ============================================================================\n// CVA Variants\n// ============================================================================\n\n/**\n * Panel content variants\n * Controls position-based styling and width\n */\nexport const panelContentVariants = cva(\n [\n \"flex flex-col h-full\",\n \"bg-[var(--content-background)] text-[var(--content-foreground)]\",\n \"shadow-lg\",\n ],\n {\n variants: {\n position: {\n right: \"border-l border-[var(--border)]\",\n left: \"border-r border-[var(--border)]\",\n },\n width: {\n sm: \"w-[300px]\",\n md: \"w-[400px]\",\n lg: \"w-[500px]\",\n xl: \"w-[600px]\",\n auto: \"w-auto\",\n },\n },\n defaultVariants: {\n position: \"right\",\n width: \"md\",\n },\n }\n);\n\n/**\n * Panel header variants\n */\nexport const panelHeaderVariants = cva([\n \"flex items-start justify-between\",\n \"px-4 py-4\",\n \"border-b border-[var(--border)]\",\n]);\n\n/**\n * Panel body variants\n */\nexport const panelBodyVariants = cva([\"flex-1 overflow-y-auto\", \"px-4 py-4\"]);\n\n/**\n * Panel footer variants\n */\nexport const panelFooterVariants = cva([\n \"flex items-center justify-end gap-2\",\n \"px-4 py-4\",\n \"border-t border-[var(--border)]\",\n]);\n\n/**\n * Close button variants\n */\nexport const panelCloseButtonVariants = cva([\n \"inline-flex items-center justify-center rounded-md\",\n \"min-h-[44px] min-w-[44px]\",\n \"text-[var(--menu-muted)]\",\n \"hover:bg-[var(--accent)] hover:text-[var(--accent-foreground)]\",\n \"focus-visible:outline-none focus-visible:ring-2\",\n \"focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2\",\n // No transition - instant state changes (v1)\n]);\n\n// ============================================================================\n// Context\n// ============================================================================\n\ninterface PanelContextValue {\n isOpen: boolean;\n setIsOpen: (open: boolean) => void;\n panelId: string;\n titleId: string;\n triggerRef: MutableRefObject<HTMLElement | null>;\n position: PanelPosition;\n width: PanelWidth;\n mobileBreakpoint: number;\n isMobile: boolean;\n handleClose: () => void;\n}\n\nconst PanelContext = createContext<PanelContextValue | null>(null);\n\nfunction usePanelContext(): PanelContextValue {\n const context = useContext(PanelContext);\n if (!context) {\n throw new Error(\"Panel compound components must be used within a Panel\");\n }\n return context;\n}\n\n// ============================================================================\n// Hooks\n// ============================================================================\n\n/**\n * SSR-safe media query hook\n * Defaults to false on server (desktop mode)\n */\nfunction useMediaQuery(query: string): boolean {\n const [matches, setMatches] = useState(() => {\n // Default to false on server (desktop mode)\n if (typeof window === \"undefined\") return false;\n return window.matchMedia(query).matches;\n });\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const media = window.matchMedia(query);\n // Sync state after mount (handles SSR mismatch)\n if (media.matches !== matches) {\n setMatches(media.matches);\n }\n const listener = (e: MediaQueryListEvent) => setMatches(e.matches);\n media.addEventListener(\"change\", listener);\n return () => media.removeEventListener(\"change\", listener);\n }, [query, matches]);\n\n return matches;\n}\n\n// ============================================================================\n// Panel Root\n// ============================================================================\n\nfunction PanelRoot({\n children,\n isOpen: controlledIsOpen,\n defaultOpen = false,\n onOpenChange,\n onClose,\n position = \"right\",\n width = \"md\",\n mobileBreakpoint = 768,\n}: PanelProps): ReactElement {\n // State management (controlled/uncontrolled)\n const [uncontrolledIsOpen, setUncontrolledIsOpen] = useState(defaultOpen);\n const isControlled = controlledIsOpen !== undefined;\n const isOpen = isControlled ? controlledIsOpen : uncontrolledIsOpen;\n\n // Refs\n const triggerRef = useRef<HTMLElement | null>(null);\n\n // IDs for ARIA\n const uniqueId = useId();\n const panelId = `panel-${uniqueId}`;\n const titleId = `panel-title-${uniqueId}`;\n\n // Responsive detection\n const isMobile = useMediaQuery(`(max-width: ${mobileBreakpoint - 1}px)`);\n\n // State setter\n const setIsOpen = useCallback(\n (open: boolean) => {\n if (!isControlled) {\n setUncontrolledIsOpen(open);\n }\n onOpenChange?.(open);\n },\n [isControlled, onOpenChange]\n );\n\n // Close handler with focus restoration\n const handleClose = useCallback(() => {\n setIsOpen(false);\n onClose?.();\n // Return focus to trigger if it still exists in DOM\n if (triggerRef.current) {\n window.requestAnimationFrame(() => {\n triggerRef.current?.focus();\n });\n }\n }, [setIsOpen, onClose]);\n\n // Escape key handler (desktop only - mobile handled by React Aria)\n useEffect(() => {\n if (!isOpen || isMobile) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n e.preventDefault();\n handleClose();\n }\n };\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [isOpen, isMobile, handleClose]);\n\n const contextValue: PanelContextValue = {\n isOpen,\n setIsOpen,\n panelId,\n titleId,\n triggerRef,\n position,\n width,\n mobileBreakpoint,\n isMobile,\n handleClose,\n };\n\n return (\n <PanelContext.Provider value={contextValue}>\n {children}\n </PanelContext.Provider>\n );\n}\nPanelRoot.displayName = \"Panel\";\n\n// ============================================================================\n// Panel.Trigger\n// ============================================================================\n\nfunction PanelTrigger({\n children,\n asChild = true,\n}: PanelTriggerProps): ReactElement | null {\n const { isOpen, setIsOpen, panelId, triggerRef } = usePanelContext();\n\n // Capture ref via callback ref pattern\n const setRef = useCallback(\n (element: HTMLElement | null) => {\n triggerRef.current = element;\n },\n [triggerRef]\n );\n\n const handlePress = useCallback(() => {\n setIsOpen(!isOpen);\n }, [setIsOpen, isOpen]);\n\n // If asChild, clone the child element and add props\n if (asChild && isValidElement(children)) {\n const childProps = children.props as unknown as {\n onPress?: () => void;\n onClick?: () => void;\n };\n return cloneElement(children, {\n ref: setRef,\n \"aria-expanded\": isOpen,\n \"aria-controls\": isOpen ? panelId : undefined,\n \"data-testid\": \"panel-trigger\",\n onPress: handlePress,\n onClick: childProps.onClick\n ? () => {\n childProps.onClick?.();\n handlePress();\n }\n : handlePress,\n } as unknown as Record<string, unknown>);\n }\n\n // Fallback: wrap in AriaButton\n return (\n <AriaButton\n ref={setRef}\n aria-expanded={isOpen}\n aria-controls={isOpen ? panelId : undefined}\n onPress={handlePress}\n data-testid=\"panel-trigger\"\n >\n {children}\n </AriaButton>\n );\n}\nPanelTrigger.displayName = \"PanelTrigger\";\n\n// ============================================================================\n// Built-in Close Button\n// ============================================================================\n\nfunction BuiltInCloseButton(): ReactElement {\n const { handleClose } = usePanelContext();\n\n return (\n <AriaButton\n aria-label=\"Close panel\"\n data-testid=\"panel-close\"\n onPress={handleClose}\n className={panelCloseButtonVariants()}\n >\n <X className=\"h-5 w-5\" />\n </AriaButton>\n );\n}\n\n// ============================================================================\n// Panel.Content\n// ============================================================================\n\nfunction PanelContent({\n children,\n showClose = true,\n className,\n}: PanelContentProps): ReactElement | null {\n const { isOpen, isMobile, panelId, titleId, position, width, handleClose } =\n usePanelContext();\n\n if (!isOpen) return null;\n\n // Mobile: Use React Aria Modal primitives\n if (isMobile) {\n return (\n <AriaModalOverlay\n isDismissable\n isOpen={isOpen}\n onOpenChange={(open) => !open && handleClose()}\n className=\"fixed inset-0 z-50 bg-black/50\"\n >\n <AriaModal className=\"fixed inset-0 z-50 flex items-center justify-center p-4\">\n <AriaDialog\n aria-labelledby={titleId}\n className={cn(\n \"w-full max-w-lg rounded-lg bg-[var(--content-background)] shadow-lg outline-none\",\n \"flex max-h-[90vh] flex-col\",\n className\n )}\n >\n {showClose && (\n <div className=\"absolute right-2 top-2\">\n <BuiltInCloseButton />\n </div>\n )}\n {children}\n </AriaDialog>\n </AriaModal>\n </AriaModalOverlay>\n );\n }\n\n // Desktop: Render aside in-place\n return (\n <aside\n id={panelId}\n aria-labelledby={titleId}\n data-testid=\"panel-content\"\n className={cn(panelContentVariants({ position, width }), className)}\n >\n {children}\n {showClose && (\n <div className=\"absolute right-2 top-2\">\n <BuiltInCloseButton />\n </div>\n )}\n </aside>\n );\n}\nPanelContent.displayName = \"PanelContent\";\n\n// ============================================================================\n// Panel.Header\n// ============================================================================\n\nfunction PanelHeader({\n children,\n className,\n}: PanelHeaderProps): ReactElement | null {\n // Note: We use a <header> element but DON'T add role=\"banner\"\n // because banner landmarks should not be contained in other landmarks (like aside)\n // The header element still provides structural meaning for the panel\n return (\n <header className={cn(panelHeaderVariants(), className)}>\n <div className=\"flex flex-col gap-1\">{children}</div>\n </header>\n );\n}\nPanelHeader.displayName = \"PanelHeader\";\n\n// ============================================================================\n// Panel.Title\n// ============================================================================\n\nfunction PanelTitle({\n children,\n as: Tag = \"h2\",\n className,\n}: PanelTitleProps): ReactElement | null {\n const { titleId } = usePanelContext();\n\n return (\n <Tag\n id={titleId}\n // aria-live=\"polite\" announces title changes to screen readers\n // This is important for master-detail patterns where panel content\n // updates when selecting different items (e.g., candidate profiles)\n aria-live=\"polite\"\n className={cn(\n \"text-lg font-semibold text-[var(--content-foreground)]\",\n className\n )}\n >\n {children}\n </Tag>\n );\n}\nPanelTitle.displayName = \"PanelTitle\";\n\n// ============================================================================\n// Panel.Description\n// ============================================================================\n\nfunction PanelDescription({\n children,\n className,\n}: PanelDescriptionProps): ReactElement | null {\n return (\n <p className={cn(\"text-sm text-[var(--menu-muted)]\", className)}>\n {children}\n </p>\n );\n}\nPanelDescription.displayName = \"PanelDescription\";\n\n// ============================================================================\n// Panel.Body\n// ============================================================================\n\nfunction PanelBody({ children, className }: PanelBodyProps): ReactElement | null {\n return <div className={cn(panelBodyVariants(), className)}>{children}</div>;\n}\nPanelBody.displayName = \"PanelBody\";\n\n// ============================================================================\n// Panel.Footer\n// ============================================================================\n\nfunction PanelFooter({\n children,\n className,\n}: PanelFooterProps): ReactElement | null {\n // Note: We use a <footer> element but DON'T add role=\"contentinfo\"\n // because contentinfo landmarks should not be contained in other landmarks (like aside)\n // The footer element still provides structural meaning for the panel\n return (\n <footer className={cn(panelFooterVariants(), className)}>\n {children}\n </footer>\n );\n}\nPanelFooter.displayName = \"PanelFooter\";\n\n// ============================================================================\n// Panel.Close\n// ============================================================================\n\nfunction PanelClose({ children }: PanelCloseProps): ReactElement | null {\n const { handleClose } = usePanelContext();\n\n if (!isValidElement(children)) {\n return null;\n }\n\n // Clone the child and add onPress/onClick handler\n const childProps = children.props as unknown as {\n onPress?: () => void;\n onClick?: () => void;\n };\n\n return cloneElement(children, {\n onPress: () => {\n childProps.onPress?.();\n handleClose();\n },\n onClick: () => {\n childProps.onClick?.();\n handleClose();\n },\n } as unknown as Record<string, unknown>);\n}\nPanelClose.displayName = \"PanelClose\";\n\n// ============================================================================\n// Compound Component Export\n// ============================================================================\n\n/**\n * Panel Component\n *\n * Accessible non-modal sidebar panel using the disclosure pattern with\n * a complementary region for WCAG 2.2 AAA compliance.\n *\n * @example\n * ```tsx\n * <Panel>\n * <Panel.Trigger>\n * <Button>Open Panel</Button>\n * </Panel.Trigger>\n * <Panel.Content>\n * <Panel.Header>\n * <Panel.Title>Panel Title</Panel.Title>\n * <Panel.Description>Optional description</Panel.Description>\n * </Panel.Header>\n * <Panel.Body>\n * Panel content here\n * </Panel.Body>\n * <Panel.Footer>\n * <Button>Action</Button>\n * </Panel.Footer>\n * </Panel.Content>\n * </Panel>\n * ```\n */\nexport const Panel = Object.assign(PanelRoot, {\n Trigger: PanelTrigger,\n Content: PanelContent,\n Header: PanelHeader,\n Title: PanelTitle,\n Description: PanelDescription,\n Body: PanelBody,\n Footer: PanelFooter,\n Close: PanelClose,\n});\n\n// Named exports for individual components\nexport {\n PanelRoot,\n PanelTrigger,\n PanelContent,\n PanelHeader,\n PanelTitle,\n PanelDescription,\n PanelBody,\n PanelFooter,\n PanelClose,\n};\n\n// Export CVA variant types for customization\nexport type PanelContentVariants = VariantProps<typeof panelContentVariants>;\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/utils/cn.ts","../../../src/elements/Panel/Panel.tsx"],"names":["cn","inputs","twMerge","clsx","panelContentVariants","cva","panelHeaderVariants","panelBodyVariants","panelFooterVariants","panelCloseButtonVariants","PanelContext","createContext","usePanelContext","context","useContext","useMediaQuery","query","matches","setMatches","useState","useEffect","media","listener","e","PanelRoot","children","controlledIsOpen","defaultOpen","onOpenChange","onClose","position","width","mobileBreakpoint","uncontrolledIsOpen","setUncontrolledIsOpen","isControlled","isOpen","triggerRef","useRef","uniqueId","useId","panelId","titleId","isMobile","setIsOpen","useCallback","open","handleClose","handleKeyDown","contextValue","jsx","PanelTrigger","asChild","setRef","element","handlePress","isValidElement","childProps","cloneElement","AriaButton","BuiltInCloseButton","X","PanelContent","showClose","className","AriaModalOverlay","AriaModal","jsxs","AriaDialog","PanelHeader","PanelTitle","Tag","PanelDescription","PanelBody","PanelFooter","PanelClose","Panel"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Class Name Utility\n * Merges Tailwind CSS classes with conflict resolution\n *\n * Combines clsx for conditional classes and tailwind-merge for deduplication\n *\n * @example\n * cn('px-2 py-1', 'px-4') // => 'py-1 px-4' (px-4 overrides px-2)\n * cn('text-red-500', condition && 'text-blue-500') // => conditional application\n */\n\nimport { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\n/**\n * Panel Component - Accessible Non-Modal Sidebar\n * Disclosure pattern + Complementary region for WCAG 2.2 AAA\n *\n * @see plan.md for architecture details\n * @see panel-prd.md for requirements\n * @see ADR 0007 for accessibility decisions\n */\n\nimport {\n type ReactElement,\n type MutableRefObject,\n createContext,\n useContext,\n useState,\n useId,\n useRef,\n useCallback,\n useEffect,\n cloneElement,\n isValidElement,\n} from \"react\";\nimport {\n Button as AriaButton,\n Modal as AriaModal,\n ModalOverlay as AriaModalOverlay,\n Dialog as AriaDialog,\n} from \"react-aria-components\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { X } from \"lucide-react\";\nimport { cn } from \"../../utils/cn\";\nimport type {\n PanelProps,\n PanelTriggerProps,\n PanelContentProps,\n PanelHeaderProps,\n PanelTitleProps,\n PanelDescriptionProps,\n PanelBodyProps,\n PanelFooterProps,\n PanelCloseProps,\n PanelPosition,\n PanelWidth,\n} from \"./Panel.types\";\n\n// ============================================================================\n// CVA Variants\n// ============================================================================\n\n/**\n * Panel content variants\n * Controls position-based styling and width\n */\nexport const panelContentVariants = cva(\n [\n \"flex flex-col h-full\",\n \"bg-[var(--content-background)] text-[var(--content-foreground)]\",\n \"shadow-lg\",\n ],\n {\n variants: {\n position: {\n right: \"border-l border-[var(--border)]\",\n left: \"border-r border-[var(--border)]\",\n },\n width: {\n sm: \"w-[300px]\",\n md: \"w-[400px]\",\n lg: \"w-[500px]\",\n xl: \"w-[600px]\",\n auto: \"w-auto\",\n },\n },\n defaultVariants: {\n position: \"right\",\n width: \"md\",\n },\n }\n);\n\n/**\n * Panel header variants\n */\nexport const panelHeaderVariants = cva([\n \"flex items-start justify-between\",\n \"px-4 py-4\",\n \"border-b border-[var(--border)]\",\n]);\n\n/**\n * Panel body variants\n */\nexport const panelBodyVariants = cva([\"flex-1 overflow-y-auto\", \"px-4 py-4\"]);\n\n/**\n * Panel footer variants\n */\nexport const panelFooterVariants = cva([\n \"flex items-center justify-end gap-2\",\n \"px-4 py-4\",\n \"border-t border-[var(--border)]\",\n]);\n\n/**\n * Close button variants\n */\nexport const panelCloseButtonVariants = cva([\n \"inline-flex items-center justify-center rounded-md\",\n \"min-h-[44px] min-w-[44px]\",\n \"text-[var(--menu-muted)]\",\n \"hover:bg-[var(--accent)] hover:text-[var(--accent-foreground)]\",\n \"focus-visible:outline-none focus-visible:ring-2\",\n \"focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2\",\n // No transition - instant state changes (v1)\n]);\n\n// ============================================================================\n// Context\n// ============================================================================\n\ninterface PanelContextValue {\n isOpen: boolean;\n setIsOpen: (open: boolean) => void;\n panelId: string;\n titleId: string;\n triggerRef: MutableRefObject<HTMLElement | null>;\n position: PanelPosition;\n width: PanelWidth;\n mobileBreakpoint: number;\n isMobile: boolean;\n handleClose: () => void;\n}\n\nconst PanelContext = createContext<PanelContextValue | null>(null);\n\nfunction usePanelContext(): PanelContextValue {\n const context = useContext(PanelContext);\n if (!context) {\n throw new Error(\"Panel compound components must be used within a Panel\");\n }\n return context;\n}\n\n// ============================================================================\n// Hooks\n// ============================================================================\n\n/**\n * SSR-safe media query hook\n * Defaults to false on server (desktop mode)\n */\nfunction useMediaQuery(query: string): boolean {\n const [matches, setMatches] = useState(() => {\n // Default to false on server (desktop mode)\n if (typeof window === \"undefined\") return false;\n return window.matchMedia(query).matches;\n });\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const media = window.matchMedia(query);\n // Sync state after mount (handles SSR mismatch)\n if (media.matches !== matches) {\n setMatches(media.matches);\n }\n const listener = (e: MediaQueryListEvent) => setMatches(e.matches);\n media.addEventListener(\"change\", listener);\n return () => media.removeEventListener(\"change\", listener);\n }, [query, matches]);\n\n return matches;\n}\n\n// ============================================================================\n// Panel Root\n// ============================================================================\n\nfunction PanelRoot({\n children,\n isOpen: controlledIsOpen,\n defaultOpen = false,\n onOpenChange,\n onClose,\n position = \"right\",\n width = \"md\",\n mobileBreakpoint = 768,\n}: PanelProps): ReactElement {\n // State management (controlled/uncontrolled)\n const [uncontrolledIsOpen, setUncontrolledIsOpen] = useState(defaultOpen);\n const isControlled = controlledIsOpen !== undefined;\n const isOpen = isControlled ? controlledIsOpen : uncontrolledIsOpen;\n\n // Refs\n const triggerRef = useRef<HTMLElement | null>(null);\n\n // IDs for ARIA\n const uniqueId = useId();\n const panelId = `panel-${uniqueId}`;\n const titleId = `panel-title-${uniqueId}`;\n\n // Responsive detection\n const isMobile = useMediaQuery(`(max-width: ${mobileBreakpoint - 1}px)`);\n\n // State setter\n const setIsOpen = useCallback(\n (open: boolean) => {\n if (!isControlled) {\n setUncontrolledIsOpen(open);\n }\n onOpenChange?.(open);\n },\n [isControlled, onOpenChange]\n );\n\n // Close handler with focus restoration\n const handleClose = useCallback(() => {\n setIsOpen(false);\n onClose?.();\n // Return focus to trigger if it still exists in DOM\n if (triggerRef.current) {\n window.requestAnimationFrame(() => {\n triggerRef.current?.focus();\n });\n }\n }, [setIsOpen, onClose]);\n\n // Escape key handler (desktop only - mobile handled by React Aria)\n useEffect(() => {\n if (!isOpen || isMobile) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n e.preventDefault();\n handleClose();\n }\n };\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [isOpen, isMobile, handleClose]);\n\n const contextValue: PanelContextValue = {\n isOpen,\n setIsOpen,\n panelId,\n titleId,\n triggerRef,\n position,\n width,\n mobileBreakpoint,\n isMobile,\n handleClose,\n };\n\n return (\n <PanelContext.Provider value={contextValue}>\n {children}\n </PanelContext.Provider>\n );\n}\nPanelRoot.displayName = \"Panel\";\n\n// ============================================================================\n// Panel.Trigger\n// ============================================================================\n\nfunction PanelTrigger({\n children,\n asChild = true,\n}: PanelTriggerProps): ReactElement | null {\n const { isOpen, setIsOpen, panelId, triggerRef } = usePanelContext();\n\n // Capture ref via callback ref pattern\n const setRef = useCallback(\n (element: HTMLElement | null) => {\n triggerRef.current = element;\n },\n [triggerRef]\n );\n\n const handlePress = useCallback(() => {\n setIsOpen(!isOpen);\n }, [setIsOpen, isOpen]);\n\n // If asChild, clone the child element and add props\n if (asChild && isValidElement(children)) {\n const childProps = children.props as unknown as {\n onPress?: () => void;\n onClick?: () => void;\n };\n return cloneElement(children, {\n ref: setRef,\n \"aria-expanded\": isOpen,\n \"aria-controls\": isOpen ? panelId : undefined,\n \"data-testid\": \"panel-trigger\",\n onPress: handlePress,\n onClick: childProps.onClick\n ? () => {\n childProps.onClick?.();\n handlePress();\n }\n : handlePress,\n } as unknown as Record<string, unknown>);\n }\n\n // Fallback: wrap in AriaButton\n return (\n <AriaButton\n ref={setRef}\n aria-expanded={isOpen}\n aria-controls={isOpen ? panelId : undefined}\n onPress={handlePress}\n data-testid=\"panel-trigger\"\n >\n {children}\n </AriaButton>\n );\n}\nPanelTrigger.displayName = \"PanelTrigger\";\n\n// ============================================================================\n// Built-in Close Button\n// ============================================================================\n\nfunction BuiltInCloseButton(): ReactElement {\n const { handleClose } = usePanelContext();\n\n return (\n <AriaButton\n aria-label=\"Close panel\"\n data-testid=\"panel-close\"\n onPress={handleClose}\n className={panelCloseButtonVariants()}\n >\n <X className=\"h-5 w-5\" />\n </AriaButton>\n );\n}\n\n// ============================================================================\n// Panel.Content\n// ============================================================================\n\nfunction PanelContent({\n children,\n showClose = true,\n className,\n}: PanelContentProps): ReactElement | null {\n const { isOpen, isMobile, panelId, titleId, position, width, handleClose } =\n usePanelContext();\n\n if (!isOpen) return null;\n\n // Mobile: Use React Aria Modal primitives\n if (isMobile) {\n return (\n <AriaModalOverlay\n isDismissable\n isOpen={isOpen}\n onOpenChange={(open) => !open && handleClose()}\n className=\"fixed inset-0 z-50 bg-black/50\"\n >\n <AriaModal className=\"fixed inset-0 z-50 flex items-center justify-center p-4\">\n <AriaDialog\n aria-labelledby={titleId}\n className={cn(\n \"w-full max-w-lg rounded-lg bg-[var(--content-background)] shadow-lg outline-none\",\n \"flex max-h-[90vh] flex-col\",\n className\n )}\n >\n {showClose && (\n <div className=\"absolute right-2 top-2\">\n <BuiltInCloseButton />\n </div>\n )}\n {children}\n </AriaDialog>\n </AriaModal>\n </AriaModalOverlay>\n );\n }\n\n // Desktop: Render aside in-place\n return (\n <aside\n id={panelId}\n aria-labelledby={titleId}\n data-testid=\"panel-content\"\n className={cn(panelContentVariants({ position, width }), className)}\n >\n {children}\n {showClose && (\n <div className=\"absolute right-2 top-2\">\n <BuiltInCloseButton />\n </div>\n )}\n </aside>\n );\n}\nPanelContent.displayName = \"PanelContent\";\n\n// ============================================================================\n// Panel.Header\n// ============================================================================\n\nfunction PanelHeader({\n children,\n className,\n}: PanelHeaderProps): ReactElement | null {\n // Note: We use a <header> element but DON'T add role=\"banner\"\n // because banner landmarks should not be contained in other landmarks (like aside)\n // The header element still provides structural meaning for the panel\n return (\n <header className={cn(panelHeaderVariants(), className)}>\n <div className=\"flex flex-col gap-1\">{children}</div>\n </header>\n );\n}\nPanelHeader.displayName = \"PanelHeader\";\n\n// ============================================================================\n// Panel.Title\n// ============================================================================\n\nfunction PanelTitle({\n children,\n as: Tag = \"h2\",\n className,\n}: PanelTitleProps): ReactElement | null {\n const { titleId } = usePanelContext();\n\n return (\n <Tag\n id={titleId}\n // aria-live=\"polite\" announces title changes to screen readers\n // This is important for master-detail patterns where panel content\n // updates when selecting different items (e.g., candidate profiles)\n aria-live=\"polite\"\n className={cn(\n \"text-lg font-semibold text-[var(--content-foreground)]\",\n className\n )}\n >\n {children}\n </Tag>\n );\n}\nPanelTitle.displayName = \"PanelTitle\";\n\n// ============================================================================\n// Panel.Description\n// ============================================================================\n\nfunction PanelDescription({\n children,\n className,\n}: PanelDescriptionProps): ReactElement | null {\n return (\n <p className={cn(\"text-sm text-[var(--menu-muted)]\", className)}>\n {children}\n </p>\n );\n}\nPanelDescription.displayName = \"PanelDescription\";\n\n// ============================================================================\n// Panel.Body\n// ============================================================================\n\nfunction PanelBody({ children, className }: PanelBodyProps): ReactElement | null {\n return <div className={cn(panelBodyVariants(), className)}>{children}</div>;\n}\nPanelBody.displayName = \"PanelBody\";\n\n// ============================================================================\n// Panel.Footer\n// ============================================================================\n\nfunction PanelFooter({\n children,\n className,\n}: PanelFooterProps): ReactElement | null {\n // Note: We use a <footer> element but DON'T add role=\"contentinfo\"\n // because contentinfo landmarks should not be contained in other landmarks (like aside)\n // The footer element still provides structural meaning for the panel\n return (\n <footer className={cn(panelFooterVariants(), className)}>\n {children}\n </footer>\n );\n}\nPanelFooter.displayName = \"PanelFooter\";\n\n// ============================================================================\n// Panel.Close\n// ============================================================================\n\nfunction PanelClose({ children }: PanelCloseProps): ReactElement | null {\n const { handleClose } = usePanelContext();\n\n if (!isValidElement(children)) {\n return null;\n }\n\n // Clone the child and add onPress/onClick handler\n const childProps = children.props as unknown as {\n onPress?: () => void;\n onClick?: () => void;\n };\n\n return cloneElement(children, {\n onPress: () => {\n childProps.onPress?.();\n handleClose();\n },\n onClick: () => {\n childProps.onClick?.();\n handleClose();\n },\n } as unknown as Record<string, unknown>);\n}\nPanelClose.displayName = \"PanelClose\";\n\n// ============================================================================\n// Compound Component Export\n// ============================================================================\n\n/**\n * Panel Component\n *\n * Accessible non-modal sidebar panel using the disclosure pattern with\n * a complementary region for WCAG 2.2 AAA compliance.\n *\n * @example\n * ```tsx\n * <Panel>\n * <Panel.Trigger>\n * <Button>Open Panel</Button>\n * </Panel.Trigger>\n * <Panel.Content>\n * <Panel.Header>\n * <Panel.Title>Panel Title</Panel.Title>\n * <Panel.Description>Optional description</Panel.Description>\n * </Panel.Header>\n * <Panel.Body>\n * Panel content here\n * </Panel.Body>\n * <Panel.Footer>\n * <Button>Action</Button>\n * </Panel.Footer>\n * </Panel.Content>\n * </Panel>\n * ```\n */\nexport const Panel = Object.assign(PanelRoot, {\n Trigger: PanelTrigger,\n Content: PanelContent,\n Header: PanelHeader,\n Title: PanelTitle,\n Description: PanelDescription,\n Body: PanelBody,\n Footer: PanelFooter,\n Close: PanelClose,\n});\n\n// Named exports for individual components\nexport {\n PanelRoot,\n PanelTrigger,\n PanelContent,\n PanelHeader,\n PanelTitle,\n PanelDescription,\n PanelBody,\n PanelFooter,\n PanelClose,\n};\n\n// Export CVA variant types for customization\nexport type PanelContentVariants = VariantProps<typeof panelContentVariants>;\n"]}
|