@dashforge/tw 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +134 -1
- package/README.md +45 -0
- package/dist/index.esm.js +3986 -477
- package/dist/src/components/Accordion/Accordion.d.ts.map +1 -1
- package/dist/src/components/Accordion/accordion.types.d.ts +7 -0
- package/dist/src/components/Accordion/accordion.types.d.ts.map +1 -1
- package/dist/src/components/Alert/Alert.d.ts +1 -1
- package/dist/src/components/Alert/Alert.d.ts.map +1 -1
- package/dist/src/components/Alert/alert.types.d.ts +17 -0
- package/dist/src/components/Alert/alert.types.d.ts.map +1 -1
- package/dist/src/components/AppShell/AppShell.d.ts.map +1 -1
- package/dist/src/components/AppShell/appShell.types.d.ts +7 -0
- package/dist/src/components/AppShell/appShell.types.d.ts.map +1 -1
- package/dist/src/components/Autocomplete/Autocomplete.d.ts.map +1 -1
- package/dist/src/components/Autocomplete/autocomplete.types.d.ts +47 -1
- package/dist/src/components/Autocomplete/autocomplete.types.d.ts.map +1 -1
- package/dist/src/components/Avatar/Avatar.d.ts.map +1 -1
- package/dist/src/components/Avatar/avatar.types.d.ts +17 -1
- package/dist/src/components/Avatar/avatar.types.d.ts.map +1 -1
- package/dist/src/components/Badge/Badge.d.ts.map +1 -1
- package/dist/src/components/Badge/badge.types.d.ts +27 -2
- package/dist/src/components/Badge/badge.types.d.ts.map +1 -1
- package/dist/src/components/Box/Box.d.ts.map +1 -1
- package/dist/src/components/Box/box.types.d.ts +64 -1
- package/dist/src/components/Box/box.types.d.ts.map +1 -1
- package/dist/src/components/Breadcrumbs/Breadcrumbs.d.ts.map +1 -1
- package/dist/src/components/Breadcrumbs/breadcrumbs.types.d.ts +26 -1
- package/dist/src/components/Breadcrumbs/breadcrumbs.types.d.ts.map +1 -1
- package/dist/src/components/Button/Button.d.ts.map +1 -1
- package/dist/src/components/Button/button.types.d.ts +53 -1
- package/dist/src/components/Button/button.types.d.ts.map +1 -1
- package/dist/src/components/Calendar/Calendar.d.ts.map +1 -1
- package/dist/src/components/Calendar/calendar.types.d.ts +16 -0
- package/dist/src/components/Calendar/calendar.types.d.ts.map +1 -1
- package/dist/src/components/Card/Card.d.ts.map +1 -1
- package/dist/src/components/Card/card.types.d.ts +17 -0
- package/dist/src/components/Card/card.types.d.ts.map +1 -1
- package/dist/src/components/Checkbox/Checkbox.d.ts.map +1 -1
- package/dist/src/components/Checkbox/checkbox.types.d.ts +19 -1
- package/dist/src/components/Checkbox/checkbox.types.d.ts.map +1 -1
- package/dist/src/components/Chip/Chip.d.ts.map +1 -1
- package/dist/src/components/Chip/chip.types.d.ts +40 -1
- package/dist/src/components/Chip/chip.types.d.ts.map +1 -1
- package/dist/src/components/ConfirmDialog/ConfirmDialog.d.ts.map +1 -1
- package/dist/src/components/ConfirmDialog/confirmDialog.types.d.ts +30 -2
- package/dist/src/components/ConfirmDialog/confirmDialog.types.d.ts.map +1 -1
- package/dist/src/components/Container/Container.d.ts.map +1 -1
- package/dist/src/components/Container/container.types.d.ts +29 -1
- package/dist/src/components/Container/container.types.d.ts.map +1 -1
- package/dist/src/components/DataGrid/DataGrid.d.ts +1 -1
- package/dist/src/components/DataGrid/DataGrid.d.ts.map +1 -1
- package/dist/src/components/DataGrid/dataGrid.types.d.ts +124 -10
- package/dist/src/components/DataGrid/dataGrid.types.d.ts.map +1 -1
- package/dist/src/components/DatePicker/DatePicker.d.ts +1 -1
- package/dist/src/components/DatePicker/DatePicker.d.ts.map +1 -1
- package/dist/src/components/DatePicker/datePicker.types.d.ts +18 -0
- package/dist/src/components/DatePicker/datePicker.types.d.ts.map +1 -1
- package/dist/src/components/DateRangePicker/DateRangePicker.d.ts +1 -1
- package/dist/src/components/DateRangePicker/DateRangePicker.d.ts.map +1 -1
- package/dist/src/components/DateRangePicker/dateRangePicker.types.d.ts +18 -0
- package/dist/src/components/DateRangePicker/dateRangePicker.types.d.ts.map +1 -1
- package/dist/src/components/DateTimePicker/DateTimePicker.d.ts +1 -1
- package/dist/src/components/DateTimePicker/DateTimePicker.d.ts.map +1 -1
- package/dist/src/components/DateTimePicker/dateTimePicker.types.d.ts +20 -0
- package/dist/src/components/DateTimePicker/dateTimePicker.types.d.ts.map +1 -1
- package/dist/src/components/Dialog/Dialog.d.ts.map +1 -1
- package/dist/src/components/Dialog/dialog.types.d.ts +25 -1
- package/dist/src/components/Dialog/dialog.types.d.ts.map +1 -1
- package/dist/src/components/Divider/Divider.d.ts.map +1 -1
- package/dist/src/components/Divider/divider.types.d.ts +37 -1
- package/dist/src/components/Divider/divider.types.d.ts.map +1 -1
- package/dist/src/components/Drawer/Drawer.d.ts +21 -0
- package/dist/src/components/Drawer/Drawer.d.ts.map +1 -0
- package/dist/src/components/Drawer/drawer.types.d.ts +311 -0
- package/dist/src/components/Drawer/drawer.types.d.ts.map +1 -0
- package/dist/src/components/Drawer/drawer.variants.d.ts +177 -0
- package/dist/src/components/Drawer/drawer.variants.d.ts.map +1 -0
- package/dist/src/components/Grid/Grid.d.ts.map +1 -1
- package/dist/src/components/Grid/grid.types.d.ts +16 -0
- package/dist/src/components/Grid/grid.types.d.ts.map +1 -1
- package/dist/src/components/IconButton/IconButton.d.ts.map +1 -1
- package/dist/src/components/IconButton/iconButton.types.d.ts +40 -1
- package/dist/src/components/IconButton/iconButton.types.d.ts.map +1 -1
- package/dist/src/components/LeftNav/LeftNav.d.ts.map +1 -1
- package/dist/src/components/LeftNav/leftNav.types.d.ts +26 -6
- package/dist/src/components/LeftNav/leftNav.types.d.ts.map +1 -1
- package/dist/src/components/Link/Link.d.ts +15 -0
- package/dist/src/components/Link/Link.d.ts.map +1 -0
- package/dist/src/components/Link/link.types.d.ts +169 -0
- package/dist/src/components/Link/link.types.d.ts.map +1 -0
- package/dist/src/components/Link/link.variants.d.ts +107 -0
- package/dist/src/components/Link/link.variants.d.ts.map +1 -0
- package/dist/src/components/Menu/Menu.d.ts +1 -1
- package/dist/src/components/Menu/Menu.d.ts.map +1 -1
- package/dist/src/components/Menu/menu.types.d.ts +20 -0
- package/dist/src/components/Menu/menu.types.d.ts.map +1 -1
- package/dist/src/components/NumberField/NumberField.d.ts.map +1 -1
- package/dist/src/components/NumberField/numberField.types.d.ts +47 -1
- package/dist/src/components/NumberField/numberField.types.d.ts.map +1 -1
- package/dist/src/components/OTPField/OTPField.d.ts.map +1 -1
- package/dist/src/components/OTPField/otpField.types.d.ts +35 -1
- package/dist/src/components/OTPField/otpField.types.d.ts.map +1 -1
- package/dist/src/components/Pagination/Pagination.d.ts.map +1 -1
- package/dist/src/components/Pagination/pagination.types.d.ts +26 -1
- package/dist/src/components/Pagination/pagination.types.d.ts.map +1 -1
- package/dist/src/components/Popover/Popover.d.ts.map +1 -1
- package/dist/src/components/Popover/popover.types.d.ts +29 -0
- package/dist/src/components/Popover/popover.types.d.ts.map +1 -1
- package/dist/src/components/Progress/Progress.d.ts +20 -0
- package/dist/src/components/Progress/Progress.d.ts.map +1 -0
- package/dist/src/components/Progress/progress.types.d.ts +200 -0
- package/dist/src/components/Progress/progress.types.d.ts.map +1 -0
- package/dist/src/components/Progress/progress.variants.d.ts +224 -0
- package/dist/src/components/Progress/progress.variants.d.ts.map +1 -0
- package/dist/src/components/RadioGroup/RadioGroup.d.ts.map +1 -1
- package/dist/src/components/RadioGroup/radioGroup.types.d.ts +25 -1
- package/dist/src/components/RadioGroup/radioGroup.types.d.ts.map +1 -1
- package/dist/src/components/Select/Select.d.ts +14 -0
- package/dist/src/components/Select/Select.d.ts.map +1 -0
- package/dist/src/components/Select/select.types.d.ts +272 -0
- package/dist/src/components/Select/select.types.d.ts.map +1 -0
- package/dist/src/components/Select/select.variants.d.ts +204 -0
- package/dist/src/components/Select/select.variants.d.ts.map +1 -0
- package/dist/src/components/Skeleton/Skeleton.d.ts.map +1 -1
- package/dist/src/components/Skeleton/skeleton.types.d.ts +25 -1
- package/dist/src/components/Skeleton/skeleton.types.d.ts.map +1 -1
- package/dist/src/components/Slider/Slider.d.ts +15 -0
- package/dist/src/components/Slider/Slider.d.ts.map +1 -0
- package/dist/src/components/Slider/slider.types.d.ts +268 -0
- package/dist/src/components/Slider/slider.types.d.ts.map +1 -0
- package/dist/src/components/Slider/slider.variants.d.ts +313 -0
- package/dist/src/components/Slider/slider.variants.d.ts.map +1 -0
- package/dist/src/components/Snackbar/Snackbar.d.ts.map +1 -1
- package/dist/src/components/Snackbar/snackbar.types.d.ts +29 -5
- package/dist/src/components/Snackbar/snackbar.types.d.ts.map +1 -1
- package/dist/src/components/Spinner/Spinner.d.ts.map +1 -1
- package/dist/src/components/Spinner/spinner.types.d.ts +22 -2
- package/dist/src/components/Spinner/spinner.types.d.ts.map +1 -1
- package/dist/src/components/Stack/Stack.d.ts.map +1 -1
- package/dist/src/components/Stack/stack.types.d.ts +53 -2
- package/dist/src/components/Stack/stack.types.d.ts.map +1 -1
- package/dist/src/components/Stack/stack.variants.d.ts +23 -0
- package/dist/src/components/Stack/stack.variants.d.ts.map +1 -1
- package/dist/src/components/Stepper/Step.d.ts +30 -0
- package/dist/src/components/Stepper/Step.d.ts.map +1 -0
- package/dist/src/components/Stepper/Stepper.d.ts +15 -0
- package/dist/src/components/Stepper/Stepper.d.ts.map +1 -0
- package/dist/src/components/Stepper/StepperContext.d.ts +8 -0
- package/dist/src/components/Stepper/StepperContext.d.ts.map +1 -0
- package/dist/src/components/Stepper/stepper.types.d.ts +358 -0
- package/dist/src/components/Stepper/stepper.types.d.ts.map +1 -0
- package/dist/src/components/Stepper/stepper.variants.d.ts +253 -0
- package/dist/src/components/Stepper/stepper.variants.d.ts.map +1 -0
- package/dist/src/components/Stepper/useStep.d.ts +53 -0
- package/dist/src/components/Stepper/useStep.d.ts.map +1 -0
- package/dist/src/components/Switch/Switch.d.ts.map +1 -1
- package/dist/src/components/Switch/switch.types.d.ts +44 -1
- package/dist/src/components/Switch/switch.types.d.ts.map +1 -1
- package/dist/src/components/Table/Table.d.ts +1 -1
- package/dist/src/components/Table/Table.d.ts.map +1 -1
- package/dist/src/components/Table/table.types.d.ts +103 -8
- package/dist/src/components/Table/table.types.d.ts.map +1 -1
- package/dist/src/components/Tabs/Tabs.d.ts.map +1 -1
- package/dist/src/components/Tabs/tabs.types.d.ts +26 -1
- package/dist/src/components/Tabs/tabs.types.d.ts.map +1 -1
- package/dist/src/components/TextField/TextField.d.ts.map +1 -1
- package/dist/src/components/TextField/textField.types.d.ts +30 -1
- package/dist/src/components/TextField/textField.types.d.ts.map +1 -1
- package/dist/src/components/Textarea/Textarea.d.ts.map +1 -1
- package/dist/src/components/Textarea/textarea.types.d.ts +51 -1
- package/dist/src/components/Textarea/textarea.types.d.ts.map +1 -1
- package/dist/src/components/TimePicker/TimePicker.d.ts +1 -1
- package/dist/src/components/TimePicker/TimePicker.d.ts.map +1 -1
- package/dist/src/components/TimePicker/timePicker.types.d.ts +18 -0
- package/dist/src/components/TimePicker/timePicker.types.d.ts.map +1 -1
- package/dist/src/components/Tooltip/Tooltip.d.ts.map +1 -1
- package/dist/src/components/Tooltip/tooltip.types.d.ts +30 -0
- package/dist/src/components/Tooltip/tooltip.types.d.ts.map +1 -1
- package/dist/src/components/TopBar/TopBar.d.ts.map +1 -1
- package/dist/src/components/TopBar/topBar.types.d.ts +25 -1
- package/dist/src/components/TopBar/topBar.types.d.ts.map +1 -1
- package/dist/src/components/Typography/Typography.d.ts.map +1 -1
- package/dist/src/components/Typography/typography.types.d.ts +47 -1
- package/dist/src/components/Typography/typography.types.d.ts.map +1 -1
- package/dist/src/components/Typography/typography.variants.d.ts +2 -2
- package/dist/src/components/Typography/typography.variants.d.ts.map +1 -1
- package/dist/src/hooks/useStandaloneFieldWarning.d.ts +38 -0
- package/dist/src/hooks/useStandaloneFieldWarning.d.ts.map +1 -0
- package/dist/src/index.d.ts +62 -42
- package/dist/src/index.d.ts.map +1 -1
- package/package.json +11 -5
- package/.swcrc +0 -29
- package/A11Y.md +0 -130
- package/CONSUMER-VALIDATION.md +0 -130
- package/COVERAGE.md +0 -63
- package/PARITY.md +0 -481
- package/PERFORMANCE.md +0 -227
- package/REFINEMENT-NOTES.md +0 -50
- package/THEME-AUDIT.md +0 -174
- package/dashforge-tw-1.0.0.tgz +0 -0
- package/eslint.config.mjs +0 -62
- package/project.json +0 -15
- package/rollup.config.cjs +0 -16
- package/src/components/Accordion/Accordion.test.tsx +0 -95
- package/src/components/Accordion/Accordion.tsx +0 -97
- package/src/components/Accordion/accordion.types.ts +0 -66
- package/src/components/Accordion/accordion.variants.ts +0 -32
- package/src/components/Alert/Alert.test.tsx +0 -302
- package/src/components/Alert/Alert.tsx +0 -186
- package/src/components/Alert/alert.types.ts +0 -144
- package/src/components/Alert/alert.variants.ts +0 -71
- package/src/components/AppShell/AppShell.test.tsx +0 -223
- package/src/components/AppShell/AppShell.tsx +0 -239
- package/src/components/AppShell/appShell.types.ts +0 -49
- package/src/components/AppShell/appShell.variants.ts +0 -54
- package/src/components/AspectRatio/AspectRatio.test.tsx +0 -173
- package/src/components/AspectRatio/AspectRatio.tsx +0 -73
- package/src/components/AspectRatio/aspectRatio.types.ts +0 -54
- package/src/components/Autocomplete/Autocomplete.perf.test.tsx +0 -225
- package/src/components/Autocomplete/Autocomplete.test.tsx +0 -556
- package/src/components/Autocomplete/Autocomplete.tsx +0 -1069
- package/src/components/Autocomplete/autocomplete.types.ts +0 -198
- package/src/components/Autocomplete/autocomplete.variants.ts +0 -141
- package/src/components/Avatar/Avatar.test.tsx +0 -287
- package/src/components/Avatar/Avatar.tsx +0 -304
- package/src/components/Avatar/avatar.types.ts +0 -205
- package/src/components/Avatar/avatar.variants.ts +0 -194
- package/src/components/Badge/Badge.test.tsx +0 -385
- package/src/components/Badge/Badge.tsx +0 -174
- package/src/components/Badge/badge.types.ts +0 -154
- package/src/components/Badge/badge.variants.ts +0 -161
- package/src/components/Box/Box.test.tsx +0 -391
- package/src/components/Box/Box.tsx +0 -112
- package/src/components/Box/box.types.ts +0 -116
- package/src/components/Box/box.variants.ts +0 -230
- package/src/components/Breadcrumbs/Breadcrumbs.perf.test.tsx +0 -128
- package/src/components/Breadcrumbs/Breadcrumbs.test.tsx +0 -231
- package/src/components/Breadcrumbs/Breadcrumbs.tsx +0 -203
- package/src/components/Breadcrumbs/breadcrumbs.types.ts +0 -87
- package/src/components/Breadcrumbs/breadcrumbs.variants.ts +0 -54
- package/src/components/Button/Button.test.tsx +0 -204
- package/src/components/Button/Button.tsx +0 -151
- package/src/components/Button/button.types.ts +0 -110
- package/src/components/Button/button.variants.ts +0 -159
- package/src/components/Calendar/Calendar.test.tsx +0 -75
- package/src/components/Calendar/Calendar.tsx +0 -264
- package/src/components/Calendar/calendar.types.ts +0 -65
- package/src/components/Calendar/calendar.variants.ts +0 -64
- package/src/components/Card/Card.test.tsx +0 -291
- package/src/components/Card/Card.tsx +0 -241
- package/src/components/Card/card.types.ts +0 -132
- package/src/components/Checkbox/Checkbox.test.tsx +0 -146
- package/src/components/Checkbox/Checkbox.tsx +0 -286
- package/src/components/Checkbox/checkbox.types.ts +0 -88
- package/src/components/Checkbox/checkbox.variants.ts +0 -68
- package/src/components/Chip/Chip.test.tsx +0 -316
- package/src/components/Chip/Chip.tsx +0 -224
- package/src/components/Chip/chip.types.ts +0 -128
- package/src/components/Chip/chip.variants.ts +0 -173
- package/src/components/ConfirmDialog/ConfirmDialog.perf.test.tsx +0 -172
- package/src/components/ConfirmDialog/ConfirmDialog.test.tsx +0 -311
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +0 -252
- package/src/components/ConfirmDialog/confirmDialog.types.ts +0 -70
- package/src/components/ConfirmDialog/confirmDialog.variants.ts +0 -76
- package/src/components/Container/Container.test.tsx +0 -209
- package/src/components/Container/Container.tsx +0 -74
- package/src/components/Container/container.types.ts +0 -45
- package/src/components/Container/container.variants.ts +0 -81
- package/src/components/DataGrid/DataGrid.perf.test.tsx +0 -114
- package/src/components/DataGrid/DataGrid.test.tsx +0 -1153
- package/src/components/DataGrid/DataGrid.tsx +0 -1282
- package/src/components/DataGrid/dataGrid.types.ts +0 -238
- package/src/components/DataGrid/dataGrid.variants.ts +0 -152
- package/src/components/DataGrid/filters/ColumnFilters.test.tsx +0 -330
- package/src/components/DataGrid/filters/ColumnFilters.tsx +0 -456
- package/src/components/DataGrid/reorder/useColumnReorder.test.ts +0 -130
- package/src/components/DataGrid/reorder/useColumnReorder.ts +0 -144
- package/src/components/DataGrid/resize/useColumnResize.test.ts +0 -144
- package/src/components/DataGrid/resize/useColumnResize.ts +0 -135
- package/src/components/DataGrid/visibility/ColumnVisibilityMenu.tsx +0 -186
- package/src/components/DatePicker/DatePicker.test.tsx +0 -76
- package/src/components/DatePicker/DatePicker.tsx +0 -305
- package/src/components/DatePicker/datePicker.types.ts +0 -77
- package/src/components/DatePicker/datePicker.variants.ts +0 -50
- package/src/components/DateRangePicker/DateRangePicker.test.tsx +0 -92
- package/src/components/DateRangePicker/DateRangePicker.tsx +0 -510
- package/src/components/DateRangePicker/dateRangePicker.types.ts +0 -74
- package/src/components/DateRangePicker/dateRangePicker.variants.ts +0 -101
- package/src/components/DateTimePicker/DateTimePicker.test.tsx +0 -78
- package/src/components/DateTimePicker/DateTimePicker.tsx +0 -347
- package/src/components/DateTimePicker/dateTimePicker.types.ts +0 -79
- package/src/components/DateTimePicker/dateTimePicker.variants.ts +0 -55
- package/src/components/Dialog/Dialog.test.tsx +0 -96
- package/src/components/Dialog/Dialog.tsx +0 -102
- package/src/components/Dialog/dialog.types.ts +0 -62
- package/src/components/Dialog/dialog.variants.ts +0 -66
- package/src/components/Divider/Divider.test.tsx +0 -243
- package/src/components/Divider/Divider.tsx +0 -140
- package/src/components/Divider/divider.types.ts +0 -52
- package/src/components/Divider/divider.variants.ts +0 -116
- package/src/components/Grid/Grid.test.tsx +0 -321
- package/src/components/Grid/Grid.tsx +0 -148
- package/src/components/Grid/grid.types.ts +0 -112
- package/src/components/Grid/grid.variants.ts +0 -133
- package/src/components/IconButton/IconButton.test.tsx +0 -367
- package/src/components/IconButton/IconButton.tsx +0 -159
- package/src/components/IconButton/iconButton.types.ts +0 -106
- package/src/components/IconButton/iconButton.variants.ts +0 -31
- package/src/components/LeftNav/LeftNav.perf.test.tsx +0 -172
- package/src/components/LeftNav/LeftNav.test.tsx +0 -302
- package/src/components/LeftNav/LeftNav.tsx +0 -330
- package/src/components/LeftNav/leftNav.types.ts +0 -120
- package/src/components/LeftNav/leftNav.variants.ts +0 -107
- package/src/components/Menu/Menu.test.tsx +0 -408
- package/src/components/Menu/Menu.tsx +0 -335
- package/src/components/Menu/menu.types.ts +0 -221
- package/src/components/Menu/menu.variants.ts +0 -132
- package/src/components/NumberField/NumberField.test.tsx +0 -83
- package/src/components/NumberField/NumberField.tsx +0 -321
- package/src/components/NumberField/numberField.types.ts +0 -72
- package/src/components/NumberField/numberField.variants.ts +0 -94
- package/src/components/OTPField/OTPField.test.tsx +0 -121
- package/src/components/OTPField/OTPField.tsx +0 -250
- package/src/components/OTPField/otpField.types.ts +0 -58
- package/src/components/OTPField/otpField.variants.ts +0 -72
- package/src/components/Pagination/Pagination.test.tsx +0 -314
- package/src/components/Pagination/Pagination.tsx +0 -277
- package/src/components/Pagination/pagination.helpers.ts +0 -102
- package/src/components/Pagination/pagination.types.ts +0 -117
- package/src/components/Pagination/pagination.variants.ts +0 -117
- package/src/components/Popover/Popover.test.tsx +0 -72
- package/src/components/Popover/Popover.tsx +0 -55
- package/src/components/Popover/popover.types.ts +0 -42
- package/src/components/Popover/popover.variants.ts +0 -24
- package/src/components/RadioGroup/RadioGroup.test.tsx +0 -140
- package/src/components/RadioGroup/RadioGroup.tsx +0 -283
- package/src/components/RadioGroup/radioGroup.types.ts +0 -85
- package/src/components/RadioGroup/radioGroup.variants.ts +0 -87
- package/src/components/Skeleton/Skeleton.test.tsx +0 -127
- package/src/components/Skeleton/Skeleton.tsx +0 -49
- package/src/components/Skeleton/skeleton.types.ts +0 -59
- package/src/components/Skeleton/skeleton.variants.ts +0 -87
- package/src/components/Snackbar/Snackbar.perf.test.tsx +0 -144
- package/src/components/Snackbar/Snackbar.test.tsx +0 -327
- package/src/components/Snackbar/Snackbar.tsx +0 -316
- package/src/components/Snackbar/snackbar.types.ts +0 -127
- package/src/components/Snackbar/snackbar.variants.ts +0 -81
- package/src/components/Spinner/Spinner.test.tsx +0 -199
- package/src/components/Spinner/Spinner.tsx +0 -158
- package/src/components/Spinner/spinner.types.ts +0 -113
- package/src/components/Spinner/spinner.variants.ts +0 -83
- package/src/components/Stack/Stack.test.tsx +0 -309
- package/src/components/Stack/Stack.tsx +0 -115
- package/src/components/Stack/stack.types.ts +0 -68
- package/src/components/Stack/stack.variants.ts +0 -80
- package/src/components/Switch/Switch.test.tsx +0 -131
- package/src/components/Switch/Switch.tsx +0 -158
- package/src/components/Switch/switch.types.ts +0 -36
- package/src/components/Switch/switch.variants.ts +0 -70
- package/src/components/Table/Table.perf.test.tsx +0 -81
- package/src/components/Table/Table.test.tsx +0 -674
- package/src/components/Table/Table.tsx +0 -863
- package/src/components/Table/cells/RenderButton.tsx +0 -23
- package/src/components/Table/cells/RenderChip.tsx +0 -37
- package/src/components/Table/cells/RenderText.tsx +0 -37
- package/src/components/Table/cells/RenderTwoLine.tsx +0 -34
- package/src/components/Table/cells/RowActionsMenu.tsx +0 -147
- package/src/components/Table/cells/cells.test.tsx +0 -247
- package/src/components/Table/cells/index.ts +0 -14
- package/src/components/Table/table.types.ts +0 -438
- package/src/components/Table/table.variants.ts +0 -207
- package/src/components/Tabs/Tabs.test.tsx +0 -145
- package/src/components/Tabs/Tabs.tsx +0 -77
- package/src/components/Tabs/tabs.types.ts +0 -55
- package/src/components/Tabs/tabs.variants.ts +0 -69
- package/src/components/Tabs/useTabs.ts +0 -167
- package/src/components/TextField/TextField.test.tsx +0 -198
- package/src/components/TextField/TextField.tsx +0 -245
- package/src/components/TextField/textField.types.ts +0 -90
- package/src/components/TextField/textField.variants.ts +0 -105
- package/src/components/Textarea/Textarea.test.tsx +0 -75
- package/src/components/Textarea/Textarea.tsx +0 -190
- package/src/components/Textarea/textarea.types.ts +0 -60
- package/src/components/Textarea/textarea.variants.ts +0 -85
- package/src/components/TimePicker/TimePicker.test.tsx +0 -65
- package/src/components/TimePicker/TimePicker.tsx +0 -274
- package/src/components/TimePicker/timePicker.types.ts +0 -69
- package/src/components/TimePicker/timePicker.variants.ts +0 -53
- package/src/components/Tooltip/Tooltip.test.tsx +0 -73
- package/src/components/Tooltip/Tooltip.tsx +0 -70
- package/src/components/Tooltip/tooltip.types.ts +0 -44
- package/src/components/Tooltip/tooltip.variants.ts +0 -28
- package/src/components/TopBar/TopBar.test.tsx +0 -129
- package/src/components/TopBar/TopBar.tsx +0 -66
- package/src/components/TopBar/topBar.types.ts +0 -42
- package/src/components/TopBar/topBar.variants.ts +0 -38
- package/src/components/Typography/Typography.test.tsx +0 -247
- package/src/components/Typography/Typography.tsx +0 -112
- package/src/components/Typography/typography.types.ts +0 -65
- package/src/components/Typography/typography.variants.ts +0 -130
- package/src/components/VisuallyHidden/VisuallyHidden.test.tsx +0 -86
- package/src/components/VisuallyHidden/VisuallyHidden.tsx +0 -59
- package/src/components/VisuallyHidden/visuallyHidden.types.ts +0 -45
- package/src/components/_shared/data/getNestedValue.test.ts +0 -50
- package/src/components/_shared/data/getNestedValue.ts +0 -32
- package/src/components/_shared/data/useColumnAutoDetect.test.ts +0 -123
- package/src/components/_shared/data/useColumnAutoDetect.ts +0 -100
- package/src/components/_shared/data/useControllableState.ts +0 -48
- package/src/components/_shared/data/useDebouncedValue.ts +0 -24
- package/src/components/_shared/data/useTableFilter.test.ts +0 -166
- package/src/components/_shared/data/useTableFilter.ts +0 -118
- package/src/components/_shared/data/useTableSearch.test.ts +0 -115
- package/src/components/_shared/data/useTableSearch.ts +0 -66
- package/src/components/_shared/data/useTableSelection.test.ts +0 -100
- package/src/components/_shared/data/useTableSelection.ts +0 -100
- package/src/components/_shared/data/useTableSort.test.ts +0 -150
- package/src/components/_shared/data/useTableSort.ts +0 -141
- package/src/components/_shared/data/useVirtualizer.test.ts +0 -158
- package/src/components/_shared/data/useVirtualizer.ts +0 -199
- package/src/components/_shared/resolveValidationState.ts +0 -60
- package/src/components/_shared/severity/index.ts +0 -33
- package/src/components/_shared/severity/severity.types.ts +0 -50
- package/src/components/_shared/severity/severityIcons.tsx +0 -104
- package/src/components/_shared/severity/severityVariants.test.ts +0 -136
- package/src/components/_shared/severity/severityVariants.ts +0 -115
- package/src/components/_shared/themeIdentity.test.ts +0 -113
- package/src/hooks/useAccessState.spec.tsx +0 -161
- package/src/hooks/useAccessState.ts +0 -81
- package/src/index.ts +0 -550
- package/src/utils/cn.spec.ts +0 -16
- package/src/utils/cn.ts +0 -29
- package/test-output/vitest/coverage/base.css +0 -224
- package/test-output/vitest/coverage/block-navigation.js +0 -87
- package/test-output/vitest/coverage/clover.xml +0 -2418
- package/test-output/vitest/coverage/coverage-final.json +0 -89
- package/test-output/vitest/coverage/favicon.png +0 -0
- package/test-output/vitest/coverage/index.html +0 -746
- package/test-output/vitest/coverage/prettify.css +0 -1
- package/test-output/vitest/coverage/prettify.js +0 -2
- package/test-output/vitest/coverage/sort-arrow-sprite.png +0 -0
- package/test-output/vitest/coverage/sorter.js +0 -210
- package/test-output/vitest/coverage/src/components/Accordion/Accordion.tsx.html +0 -376
- package/test-output/vitest/coverage/src/components/Accordion/accordion.variants.ts.html +0 -181
- package/test-output/vitest/coverage/src/components/Accordion/index.html +0 -131
- package/test-output/vitest/coverage/src/components/AppShell/AppShell.tsx.html +0 -802
- package/test-output/vitest/coverage/src/components/AppShell/appShell.variants.ts.html +0 -247
- package/test-output/vitest/coverage/src/components/AppShell/index.html +0 -131
- package/test-output/vitest/coverage/src/components/AspectRatio/AspectRatio.tsx.html +0 -304
- package/test-output/vitest/coverage/src/components/AspectRatio/index.html +0 -116
- package/test-output/vitest/coverage/src/components/Autocomplete/Autocomplete.tsx.html +0 -3292
- package/test-output/vitest/coverage/src/components/Autocomplete/autocomplete.variants.ts.html +0 -508
- package/test-output/vitest/coverage/src/components/Autocomplete/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Box/Box.tsx.html +0 -334
- package/test-output/vitest/coverage/src/components/Box/box.variants.ts.html +0 -775
- package/test-output/vitest/coverage/src/components/Box/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Breadcrumbs/Breadcrumbs.tsx.html +0 -694
- package/test-output/vitest/coverage/src/components/Breadcrumbs/breadcrumbs.variants.ts.html +0 -247
- package/test-output/vitest/coverage/src/components/Breadcrumbs/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Button/Button.tsx.html +0 -535
- package/test-output/vitest/coverage/src/components/Button/button.variants.ts.html +0 -562
- package/test-output/vitest/coverage/src/components/Button/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Checkbox/Checkbox.tsx.html +0 -943
- package/test-output/vitest/coverage/src/components/Checkbox/checkbox.variants.ts.html +0 -289
- package/test-output/vitest/coverage/src/components/Checkbox/index.html +0 -131
- package/test-output/vitest/coverage/src/components/ConfirmDialog/ConfirmDialog.tsx.html +0 -841
- package/test-output/vitest/coverage/src/components/ConfirmDialog/confirmDialog.variants.ts.html +0 -313
- package/test-output/vitest/coverage/src/components/ConfirmDialog/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Container/Container.tsx.html +0 -307
- package/test-output/vitest/coverage/src/components/Container/container.variants.ts.html +0 -328
- package/test-output/vitest/coverage/src/components/Container/index.html +0 -131
- package/test-output/vitest/coverage/src/components/DataGrid/DataGrid.tsx.html +0 -3937
- package/test-output/vitest/coverage/src/components/DataGrid/dataGrid.variants.ts.html +0 -541
- package/test-output/vitest/coverage/src/components/DataGrid/filters/ColumnFilters.tsx.html +0 -1453
- package/test-output/vitest/coverage/src/components/DataGrid/filters/index.html +0 -116
- package/test-output/vitest/coverage/src/components/DataGrid/index.html +0 -131
- package/test-output/vitest/coverage/src/components/DataGrid/reorder/index.html +0 -116
- package/test-output/vitest/coverage/src/components/DataGrid/reorder/useColumnReorder.ts.html +0 -517
- package/test-output/vitest/coverage/src/components/DataGrid/resize/index.html +0 -116
- package/test-output/vitest/coverage/src/components/DataGrid/resize/useColumnResize.ts.html +0 -490
- package/test-output/vitest/coverage/src/components/DataGrid/visibility/ColumnVisibilityMenu.tsx.html +0 -592
- package/test-output/vitest/coverage/src/components/DataGrid/visibility/index.html +0 -116
- package/test-output/vitest/coverage/src/components/DateTimePicker/DateTimePicker.tsx.html +0 -1042
- package/test-output/vitest/coverage/src/components/DateTimePicker/dateTimePicker.variants.ts.html +0 -337
- package/test-output/vitest/coverage/src/components/DateTimePicker/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Dialog/Dialog.tsx.html +0 -391
- package/test-output/vitest/coverage/src/components/Dialog/dialog.variants.ts.html +0 -283
- package/test-output/vitest/coverage/src/components/Dialog/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Divider/Divider.tsx.html +0 -505
- package/test-output/vitest/coverage/src/components/Divider/divider.variants.ts.html +0 -433
- package/test-output/vitest/coverage/src/components/Divider/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Grid/Grid.tsx.html +0 -529
- package/test-output/vitest/coverage/src/components/Grid/grid.variants.ts.html +0 -484
- package/test-output/vitest/coverage/src/components/Grid/index.html +0 -131
- package/test-output/vitest/coverage/src/components/LeftNav/LeftNav.tsx.html +0 -1075
- package/test-output/vitest/coverage/src/components/LeftNav/index.html +0 -131
- package/test-output/vitest/coverage/src/components/LeftNav/leftNav.variants.ts.html +0 -406
- package/test-output/vitest/coverage/src/components/NumberField/NumberField.tsx.html +0 -1048
- package/test-output/vitest/coverage/src/components/NumberField/index.html +0 -131
- package/test-output/vitest/coverage/src/components/NumberField/numberField.variants.ts.html +0 -367
- package/test-output/vitest/coverage/src/components/OTPField/OTPField.tsx.html +0 -835
- package/test-output/vitest/coverage/src/components/OTPField/index.html +0 -131
- package/test-output/vitest/coverage/src/components/OTPField/otpField.variants.ts.html +0 -301
- package/test-output/vitest/coverage/src/components/Pagination/Pagination.tsx.html +0 -916
- package/test-output/vitest/coverage/src/components/Pagination/index.html +0 -146
- package/test-output/vitest/coverage/src/components/Pagination/pagination.helpers.ts.html +0 -391
- package/test-output/vitest/coverage/src/components/Pagination/pagination.variants.ts.html +0 -436
- package/test-output/vitest/coverage/src/components/Popover/Popover.tsx.html +0 -250
- package/test-output/vitest/coverage/src/components/Popover/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Popover/popover.variants.ts.html +0 -157
- package/test-output/vitest/coverage/src/components/RadioGroup/RadioGroup.tsx.html +0 -934
- package/test-output/vitest/coverage/src/components/RadioGroup/index.html +0 -131
- package/test-output/vitest/coverage/src/components/RadioGroup/radioGroup.variants.ts.html +0 -346
- package/test-output/vitest/coverage/src/components/Skeleton/Skeleton.tsx.html +0 -232
- package/test-output/vitest/coverage/src/components/Skeleton/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Skeleton/skeleton.variants.ts.html +0 -346
- package/test-output/vitest/coverage/src/components/Snackbar/Snackbar.tsx.html +0 -979
- package/test-output/vitest/coverage/src/components/Snackbar/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Snackbar/snackbar.variants.ts.html +0 -307
- package/test-output/vitest/coverage/src/components/Stack/Stack.tsx.html +0 -430
- package/test-output/vitest/coverage/src/components/Stack/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Stack/stack.variants.ts.html +0 -325
- package/test-output/vitest/coverage/src/components/Switch/Switch.tsx.html +0 -559
- package/test-output/vitest/coverage/src/components/Switch/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Switch/switch.variants.ts.html +0 -295
- package/test-output/vitest/coverage/src/components/Table/Table.tsx.html +0 -2677
- package/test-output/vitest/coverage/src/components/Table/cells/RenderButton.tsx.html +0 -154
- package/test-output/vitest/coverage/src/components/Table/cells/RenderChip.tsx.html +0 -385
- package/test-output/vitest/coverage/src/components/Table/cells/RenderText.tsx.html +0 -196
- package/test-output/vitest/coverage/src/components/Table/cells/RenderTwoLine.tsx.html +0 -187
- package/test-output/vitest/coverage/src/components/Table/cells/RowActionsMenu.tsx.html +0 -526
- package/test-output/vitest/coverage/src/components/Table/cells/index.html +0 -191
- package/test-output/vitest/coverage/src/components/Table/cells/index.ts.html +0 -127
- package/test-output/vitest/coverage/src/components/Table/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Table/table.variants.ts.html +0 -706
- package/test-output/vitest/coverage/src/components/Tabs/Tabs.tsx.html +0 -283
- package/test-output/vitest/coverage/src/components/Tabs/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Tabs/tabs.variants.ts.html +0 -292
- package/test-output/vitest/coverage/src/components/TextField/TextField.tsx.html +0 -820
- package/test-output/vitest/coverage/src/components/TextField/index.html +0 -131
- package/test-output/vitest/coverage/src/components/TextField/textField.variants.ts.html +0 -400
- package/test-output/vitest/coverage/src/components/Textarea/Textarea.tsx.html +0 -655
- package/test-output/vitest/coverage/src/components/Textarea/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Textarea/textarea.variants.ts.html +0 -340
- package/test-output/vitest/coverage/src/components/Tooltip/Tooltip.tsx.html +0 -295
- package/test-output/vitest/coverage/src/components/Tooltip/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Tooltip/tooltip.variants.ts.html +0 -169
- package/test-output/vitest/coverage/src/components/TopBar/TopBar.tsx.html +0 -283
- package/test-output/vitest/coverage/src/components/TopBar/index.html +0 -131
- package/test-output/vitest/coverage/src/components/TopBar/topBar.variants.ts.html +0 -199
- package/test-output/vitest/coverage/src/components/Typography/Typography.tsx.html +0 -421
- package/test-output/vitest/coverage/src/components/Typography/index.html +0 -131
- package/test-output/vitest/coverage/src/components/Typography/typography.variants.ts.html +0 -436
- package/test-output/vitest/coverage/src/components/VisuallyHidden/VisuallyHidden.tsx.html +0 -262
- package/test-output/vitest/coverage/src/components/VisuallyHidden/index.html +0 -116
- package/test-output/vitest/coverage/src/components/_shared/data/getNestedValue.ts.html +0 -181
- package/test-output/vitest/coverage/src/components/_shared/data/index.html +0 -236
- package/test-output/vitest/coverage/src/components/_shared/data/useColumnAutoDetect.ts.html +0 -385
- package/test-output/vitest/coverage/src/components/_shared/data/useControllableState.ts.html +0 -229
- package/test-output/vitest/coverage/src/components/_shared/data/useDebouncedValue.ts.html +0 -157
- package/test-output/vitest/coverage/src/components/_shared/data/useTableFilter.ts.html +0 -439
- package/test-output/vitest/coverage/src/components/_shared/data/useTableSearch.ts.html +0 -283
- package/test-output/vitest/coverage/src/components/_shared/data/useTableSelection.ts.html +0 -385
- package/test-output/vitest/coverage/src/components/_shared/data/useTableSort.ts.html +0 -508
- package/test-output/vitest/coverage/src/components/_shared/data/useVirtualizer.ts.html +0 -682
- package/test-output/vitest/coverage/src/components/_shared/index.html +0 -116
- package/test-output/vitest/coverage/src/components/_shared/resolveValidationState.ts.html +0 -265
- package/test-output/vitest/coverage/src/hooks/index.html +0 -116
- package/test-output/vitest/coverage/src/hooks/useAccessState.ts.html +0 -328
- package/test-output/vitest/coverage/src/index.html +0 -116
- package/test-output/vitest/coverage/src/index.ts.html +0 -1153
- package/test-output/vitest/coverage/src/utils/cn.ts.html +0 -172
- package/test-output/vitest/coverage/src/utils/index.html +0 -116
- package/tsconfig.json +0 -13
- package/tsconfig.lib.json +0 -58
- package/tsconfig.spec.json +0 -34
- package/vite.config.ts +0 -33
- package/vitest.config.mts +0 -29
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import type { ComponentPropsWithoutRef, ReactNode } from 'react';
|
|
2
|
-
import type { ClassValue } from 'tailwind-variants';
|
|
3
|
-
import type { Engine } from '@dashforge/ui-core';
|
|
4
|
-
import type { AccessRequirement } from '@dashforge/rbac';
|
|
5
|
-
import type { Severity, SeverityVariant } from '../_shared/severity/severity.types.js';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Per-slot override props for `<Alert>`. Mirrors the standard
|
|
9
|
-
* `slotProps` contract used across `@dashforge/tw` components.
|
|
10
|
-
*/
|
|
11
|
-
export interface AlertSlotProps {
|
|
12
|
-
root?: ComponentPropsWithoutRef<'div'>;
|
|
13
|
-
icon?: ComponentPropsWithoutRef<'span'>;
|
|
14
|
-
content?: ComponentPropsWithoutRef<'div'>;
|
|
15
|
-
action?: ComponentPropsWithoutRef<'div'>;
|
|
16
|
-
closeButton?: ComponentPropsWithoutRef<'button'>;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Props for the `<Alert>` component.
|
|
21
|
-
*
|
|
22
|
-
* Mirrors the MUI Alert API surface 1:1 (drop-in muscle memory) with
|
|
23
|
-
* a single deliberate divergence: `severity` uses `'danger'` instead of
|
|
24
|
-
* MUI's `'error'` for alignment with the `danger.*` token palette in
|
|
25
|
-
* `@dashforge/tw-tokens`.
|
|
26
|
-
*
|
|
27
|
-
* In addition to the MUI surface, Dashforge adds:
|
|
28
|
-
* - `visibleWhen` — engine-reactive visibility predicate (same
|
|
29
|
-
* contract as `<TextField>` / `<Checkbox>` / etc.)
|
|
30
|
-
* - `access` — RBAC integration via `@dashforge/rbac`
|
|
31
|
-
*
|
|
32
|
-
* @see https://mui.com/material-ui/api/alert/
|
|
33
|
-
*/
|
|
34
|
-
export interface AlertProps {
|
|
35
|
-
/**
|
|
36
|
-
* Visual severity. Drives the color treatment and the default ARIA
|
|
37
|
-
* role (`alert` for warning / danger, `status` for info / success).
|
|
38
|
-
*
|
|
39
|
-
* Note: this is `danger`, not MUI's `error`. The runtime behaviour
|
|
40
|
-
* is identical; the naming follows the Dashforge token palette.
|
|
41
|
-
*/
|
|
42
|
-
severity: Severity;
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Variant of the severity surface. Mirrors MUI's three-way axis.
|
|
46
|
-
*
|
|
47
|
-
* - `'standard'` (default) — tinted soft surface, severity-toned
|
|
48
|
-
* text. The "all-purpose" reading.
|
|
49
|
-
* - `'filled'` — solid colored surface, light text. High visual
|
|
50
|
-
* weight, for blocking / critical messages.
|
|
51
|
-
* - `'outlined'` — transparent surface, severity border + text.
|
|
52
|
-
* Minimal weight, for dense layouts.
|
|
53
|
-
*
|
|
54
|
-
* @default 'standard'
|
|
55
|
-
*/
|
|
56
|
-
variant?: SeverityVariant;
|
|
57
|
-
|
|
58
|
-
/** Body content of the alert. */
|
|
59
|
-
children?: ReactNode;
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Icon control with full MUI parity.
|
|
63
|
-
*
|
|
64
|
-
* - omitted / `undefined` → default per-severity icon (lib's inline
|
|
65
|
-
* stroke SVG)
|
|
66
|
-
* - `ReactNode` → consumer-provided custom icon (Lucide, Phosphor,
|
|
67
|
-
* Tabler, custom SVG — bring your own iconography)
|
|
68
|
-
* - `false` → no icon rendered (the colored surface carries the
|
|
69
|
-
* severity signal; use when icons compete with the layout)
|
|
70
|
-
*/
|
|
71
|
-
icon?: ReactNode | false;
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* When defined, the alert renders a trailing close button that
|
|
75
|
-
* fires this callback on click. Ignored if `action` is provided
|
|
76
|
-
* (`action` slot wins, matching MUI behaviour).
|
|
77
|
-
*/
|
|
78
|
-
onClose?: () => void;
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* `aria-label` for the close button.
|
|
82
|
-
* @default 'Close'
|
|
83
|
-
*/
|
|
84
|
-
closeText?: string;
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Custom trailing slot — CTA buttons, links, inline forms. When
|
|
88
|
-
* provided, replaces the auto-rendered close button. The consumer
|
|
89
|
-
* is responsible for wiring any close behaviour into the custom
|
|
90
|
-
* action node.
|
|
91
|
-
*/
|
|
92
|
-
action?: ReactNode;
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Override the auto-derived ARIA role. Defaults to `'alert'` for
|
|
96
|
-
* `severity={'warning' | 'danger'}` (assertive announcement) and
|
|
97
|
-
* `'status'` for `severity={'info' | 'success'}` (polite).
|
|
98
|
-
*/
|
|
99
|
-
role?: 'alert' | 'status';
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Reactive visibility predicate. Re-evaluated on every engine state
|
|
103
|
-
* change when the alert is mounted inside a `<DashForm>`; outside a
|
|
104
|
-
* form, evaluated as a plain predicate (the consumer captures any
|
|
105
|
-
* external state in the closure).
|
|
106
|
-
*
|
|
107
|
-
* When the predicate returns `false`, the component renders `null`
|
|
108
|
-
* — same contract as form fields.
|
|
109
|
-
*/
|
|
110
|
-
visibleWhen?: (engine: Engine) => boolean;
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* RBAC requirement. When the current subject does not satisfy the
|
|
114
|
-
* requirement, the alert is hidden (or disabled / read-only,
|
|
115
|
-
* per `onUnauthorized`).
|
|
116
|
-
*/
|
|
117
|
-
access?: AccessRequirement;
|
|
118
|
-
|
|
119
|
-
/** Root-element class shortcut (string or `clsx`-compatible value). */
|
|
120
|
-
sx?: ClassValue;
|
|
121
|
-
|
|
122
|
-
/** Standard React `className` — appended to the root via `cn()`. */
|
|
123
|
-
className?: string;
|
|
124
|
-
|
|
125
|
-
/** Per-slot overrides — typed handles for the inner elements. */
|
|
126
|
-
slotProps?: AlertSlotProps;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Props for the `<AlertTitle>` sub-component.
|
|
131
|
-
*
|
|
132
|
-
* AlertTitle is a separate exported component, not a prop on Alert —
|
|
133
|
-
* follows MUI's pattern. Pass it as the first child of `<Alert>`:
|
|
134
|
-
*
|
|
135
|
-
* @example
|
|
136
|
-
* <Alert severity="warning">
|
|
137
|
-
* <AlertTitle>Subscription expires soon</AlertTitle>
|
|
138
|
-
* Your Pro plan ends on 2026-06-20.
|
|
139
|
-
* </Alert>
|
|
140
|
-
*/
|
|
141
|
-
export interface AlertTitleProps {
|
|
142
|
-
children: ReactNode;
|
|
143
|
-
className?: string;
|
|
144
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { tv, type VariantProps } from 'tailwind-variants';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Tailwind-variants recipe for `<Alert>`.
|
|
5
|
-
*
|
|
6
|
-
* Slots:
|
|
7
|
-
* - `root` — outer surface, severity + variant color classes
|
|
8
|
-
* come from `_shared/severity/severityVariants`
|
|
9
|
-
* (NOT baked into this recipe, to keep the matrix
|
|
10
|
-
* shared across Alert / Snackbar / future Banner)
|
|
11
|
-
* - `icon` — leading severity icon container
|
|
12
|
-
* - `titleSlot` — `<AlertTitle>` rendered as the first content row
|
|
13
|
-
* - `content` — body text container
|
|
14
|
-
* - `action` — trailing action / close button container
|
|
15
|
-
* - `closeButton` — built-in close `×` (when `onClose` is defined)
|
|
16
|
-
*
|
|
17
|
-
* The severity color classes are merged at render time via `cn()` in
|
|
18
|
-
* `Alert.tsx` — see the call sites of `getSeverityClasses(variant,
|
|
19
|
-
* severity)`. This recipe owns layout + spacing + typography only.
|
|
20
|
-
*/
|
|
21
|
-
export const alertVariants = tv({
|
|
22
|
-
slots: {
|
|
23
|
-
root: [
|
|
24
|
-
'relative flex items-start gap-3',
|
|
25
|
-
'px-4 py-3',
|
|
26
|
-
'rounded-lg border',
|
|
27
|
-
'text-sm',
|
|
28
|
-
// Smooth visibility transition (opacity only) — matches the
|
|
29
|
-
// Snackbar entry timing, gated on motion-reduce per WCAG 2.3.3.
|
|
30
|
-
'transition-opacity duration-150 motion-reduce:transition-none motion-reduce:duration-0',
|
|
31
|
-
],
|
|
32
|
-
icon: [
|
|
33
|
-
'shrink-0 mt-0.5',
|
|
34
|
-
'inline-flex items-center justify-center',
|
|
35
|
-
],
|
|
36
|
-
titleSlot: [
|
|
37
|
-
'block font-medium leading-snug mb-1',
|
|
38
|
-
],
|
|
39
|
-
content: [
|
|
40
|
-
'flex-1 min-w-0',
|
|
41
|
-
'leading-relaxed',
|
|
42
|
-
],
|
|
43
|
-
action: [
|
|
44
|
-
'shrink-0 inline-flex items-center',
|
|
45
|
-
'ml-auto',
|
|
46
|
-
],
|
|
47
|
-
closeButton: [
|
|
48
|
-
'shrink-0 inline-flex items-center justify-center w-6 h-6',
|
|
49
|
-
'rounded-md',
|
|
50
|
-
'opacity-70 hover:opacity-100',
|
|
51
|
-
'outline-none focus-visible:ring-2 focus-visible:ring-current',
|
|
52
|
-
'transition-opacity',
|
|
53
|
-
],
|
|
54
|
-
},
|
|
55
|
-
variants: {
|
|
56
|
-
/**
|
|
57
|
-
* Layout density. Defaults to comfortable spacing; `compact`
|
|
58
|
-
* tightens the vertical padding for inline contexts (form
|
|
59
|
-
* validation summary at the top of a tight modal, etc.).
|
|
60
|
-
*/
|
|
61
|
-
density: {
|
|
62
|
-
comfortable: { root: 'px-4 py-3' },
|
|
63
|
-
compact: { root: 'px-3 py-2 text-[13px]' },
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
defaultVariants: {
|
|
67
|
-
density: 'comfortable',
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
export type AlertVariantProps = VariantProps<typeof alertVariants>;
|
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { describe, it, expect, vi, afterEach } from 'vitest';
|
|
4
|
-
import {
|
|
5
|
-
render,
|
|
6
|
-
screen,
|
|
7
|
-
fireEvent,
|
|
8
|
-
cleanup,
|
|
9
|
-
} from '@testing-library/react';
|
|
10
|
-
import { AppShell } from './AppShell.js';
|
|
11
|
-
|
|
12
|
-
void React;
|
|
13
|
-
afterEach(() => cleanup());
|
|
14
|
-
|
|
15
|
-
describe('<AppShell>', () => {
|
|
16
|
-
describe('rendering', () => {
|
|
17
|
-
it('renders main landmark with children', () => {
|
|
18
|
-
render(
|
|
19
|
-
<AppShell>
|
|
20
|
-
<p>page</p>
|
|
21
|
-
</AppShell>
|
|
22
|
-
);
|
|
23
|
-
const main = screen.getByRole('main');
|
|
24
|
-
expect(main.textContent).toBe('page');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('renders header slot', () => {
|
|
28
|
-
render(
|
|
29
|
-
<AppShell header={<div data-testid="hdr">H</div>}>
|
|
30
|
-
<p>page</p>
|
|
31
|
-
</AppShell>
|
|
32
|
-
);
|
|
33
|
-
expect(screen.getByTestId('hdr')).toBeTruthy();
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('renders footer slot', () => {
|
|
37
|
-
render(
|
|
38
|
-
<AppShell footer={<div data-testid="ftr">F</div>}>
|
|
39
|
-
<p>page</p>
|
|
40
|
-
</AppShell>
|
|
41
|
-
);
|
|
42
|
-
expect(screen.getByTestId('ftr')).toBeTruthy();
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('renders nav slot (twice — desktop inline + mobile drawer)', () => {
|
|
46
|
-
const { container } = render(
|
|
47
|
-
<AppShell nav={<div data-testid="nav">N</div>}>
|
|
48
|
-
<p>page</p>
|
|
49
|
-
</AppShell>
|
|
50
|
-
);
|
|
51
|
-
// Both copies are in the DOM; the mobile one is hidden via CSS.
|
|
52
|
-
expect(container.querySelectorAll('[data-testid="nav"]')).toHaveLength(2);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('does NOT render the nav-side asides when nav is omitted', () => {
|
|
56
|
-
const { container } = render(
|
|
57
|
-
<AppShell>
|
|
58
|
-
<p>page</p>
|
|
59
|
-
</AppShell>
|
|
60
|
-
);
|
|
61
|
-
// Only the <main> remains inside the body row.
|
|
62
|
-
expect(container.querySelectorAll('aside')).toHaveLength(0);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
describe('mobile drawer', () => {
|
|
67
|
-
it('renders the mobile aside with aria-hidden=true by default', () => {
|
|
68
|
-
const { container } = render(
|
|
69
|
-
<AppShell nav={<div>N</div>}>
|
|
70
|
-
<p>page</p>
|
|
71
|
-
</AppShell>
|
|
72
|
-
);
|
|
73
|
-
const mobile = container.querySelectorAll('aside')[1];
|
|
74
|
-
expect(mobile.getAttribute('aria-hidden')).toBe('true');
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('exposes the mobile aside (aria-hidden=false) when navOpen', () => {
|
|
78
|
-
const { container } = render(
|
|
79
|
-
<AppShell nav={<div>N</div>} navOpen onNavOpenChange={vi.fn()}>
|
|
80
|
-
<p>page</p>
|
|
81
|
-
</AppShell>
|
|
82
|
-
);
|
|
83
|
-
const mobile = container.querySelectorAll('aside')[1];
|
|
84
|
-
expect(mobile.getAttribute('aria-hidden')).toBe('false');
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('fires onNavOpenChange(false) when the backdrop is clicked', () => {
|
|
88
|
-
const cb = vi.fn();
|
|
89
|
-
render(
|
|
90
|
-
<AppShell nav={<div>N</div>} navOpen onNavOpenChange={cb}>
|
|
91
|
-
<p>page</p>
|
|
92
|
-
</AppShell>
|
|
93
|
-
);
|
|
94
|
-
fireEvent.click(screen.getByTestId('appshell-backdrop'));
|
|
95
|
-
expect(cb).toHaveBeenCalledWith(false);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('fires onNavOpenChange(false) when Escape is pressed', () => {
|
|
99
|
-
const cb = vi.fn();
|
|
100
|
-
render(
|
|
101
|
-
<AppShell nav={<div>N</div>} navOpen onNavOpenChange={cb}>
|
|
102
|
-
<p>page</p>
|
|
103
|
-
</AppShell>
|
|
104
|
-
);
|
|
105
|
-
fireEvent.keyDown(document, { key: 'Escape' });
|
|
106
|
-
expect(cb).toHaveBeenCalledWith(false);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it('does NOT fire Escape callback when drawer is closed', () => {
|
|
110
|
-
const cb = vi.fn();
|
|
111
|
-
render(
|
|
112
|
-
<AppShell nav={<div>N</div>} onNavOpenChange={cb}>
|
|
113
|
-
<p>page</p>
|
|
114
|
-
</AppShell>
|
|
115
|
-
);
|
|
116
|
-
fireEvent.keyDown(document, { key: 'Escape' });
|
|
117
|
-
expect(cb).not.toHaveBeenCalled();
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
describe('body scroll lock', () => {
|
|
122
|
-
it('locks body overflow while navOpen is true', () => {
|
|
123
|
-
const { rerender, unmount } = render(
|
|
124
|
-
<AppShell nav={<div>N</div>} navOpen>
|
|
125
|
-
<p>page</p>
|
|
126
|
-
</AppShell>
|
|
127
|
-
);
|
|
128
|
-
expect(document.body.style.overflow).toBe('hidden');
|
|
129
|
-
rerender(
|
|
130
|
-
<AppShell nav={<div>N</div>}>
|
|
131
|
-
<p>page</p>
|
|
132
|
-
</AppShell>
|
|
133
|
-
);
|
|
134
|
-
expect(document.body.style.overflow).not.toBe('hidden');
|
|
135
|
-
unmount();
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it('restores body overflow on unmount even if still open', () => {
|
|
139
|
-
const { unmount } = render(
|
|
140
|
-
<AppShell nav={<div>N</div>} navOpen>
|
|
141
|
-
<p>page</p>
|
|
142
|
-
</AppShell>
|
|
143
|
-
);
|
|
144
|
-
expect(document.body.style.overflow).toBe('hidden');
|
|
145
|
-
unmount();
|
|
146
|
-
expect(document.body.style.overflow).not.toBe('hidden');
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
describe('<AppShell> performance', () => {
|
|
152
|
-
it('mounts under 30ms with all slots filled', () => {
|
|
153
|
-
const t0 = performance.now();
|
|
154
|
-
render(
|
|
155
|
-
<AppShell
|
|
156
|
-
header={<div>H</div>}
|
|
157
|
-
nav={<div>N</div>}
|
|
158
|
-
footer={<div>F</div>}
|
|
159
|
-
>
|
|
160
|
-
<p>page</p>
|
|
161
|
-
</AppShell>
|
|
162
|
-
);
|
|
163
|
-
const t1 = performance.now();
|
|
164
|
-
expect(t1 - t0).toBeLessThan(30);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it('renders at most 2 times on mount', () => {
|
|
168
|
-
let count = 0;
|
|
169
|
-
const Counted = (p: React.ComponentProps<typeof AppShell>) => {
|
|
170
|
-
count++;
|
|
171
|
-
return <AppShell {...p} />;
|
|
172
|
-
};
|
|
173
|
-
render(
|
|
174
|
-
<Counted nav={<div>N</div>}>
|
|
175
|
-
<p>page</p>
|
|
176
|
-
</Counted>
|
|
177
|
-
);
|
|
178
|
-
expect(count).toBeLessThanOrEqual(2);
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it('toggling navOpen costs ≤ 1 extra render on the wrapper', () => {
|
|
182
|
-
let count = 0;
|
|
183
|
-
const Counted = (p: React.ComponentProps<typeof AppShell>) => {
|
|
184
|
-
count++;
|
|
185
|
-
return <AppShell {...p} />;
|
|
186
|
-
};
|
|
187
|
-
const { rerender } = render(
|
|
188
|
-
<Counted nav={<div>N</div>} navOpen={false}>
|
|
189
|
-
<p>page</p>
|
|
190
|
-
</Counted>
|
|
191
|
-
);
|
|
192
|
-
const baseline = count;
|
|
193
|
-
rerender(
|
|
194
|
-
<Counted nav={<div>N</div>} navOpen={true}>
|
|
195
|
-
<p>page</p>
|
|
196
|
-
</Counted>
|
|
197
|
-
);
|
|
198
|
-
expect(count - baseline).toBeLessThanOrEqual(2);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
it('does NOT re-render an unrelated sibling on navOpen toggle', () => {
|
|
202
|
-
const sibCount = { count: 0 };
|
|
203
|
-
const Sibling = () => {
|
|
204
|
-
sibCount.count++;
|
|
205
|
-
return null;
|
|
206
|
-
};
|
|
207
|
-
const Tree = ({ open }: { open: boolean }) => (
|
|
208
|
-
<div>
|
|
209
|
-
<AppShell nav={<div>N</div>} navOpen={open}>
|
|
210
|
-
<p>page</p>
|
|
211
|
-
</AppShell>
|
|
212
|
-
<Sibling />
|
|
213
|
-
</div>
|
|
214
|
-
);
|
|
215
|
-
const { rerender } = render(<Tree open={false} />);
|
|
216
|
-
const before = sibCount.count;
|
|
217
|
-
rerender(<Tree open={true} />);
|
|
218
|
-
rerender(<Tree open={false} />);
|
|
219
|
-
// Each parent re-render triggers the sibling once. 2 parent
|
|
220
|
-
// rerenders ⇒ ≤ 2 extra sibling renders (tolerance +1).
|
|
221
|
-
expect(sibCount.count - before).toBeLessThanOrEqual(3);
|
|
222
|
-
});
|
|
223
|
-
});
|
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef } from 'react';
|
|
2
|
-
import { cn } from '../../utils/cn.js';
|
|
3
|
-
import { appShellVariants } from './appShell.variants.js';
|
|
4
|
-
import type { AppShellProps } from './appShell.types.js';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Query selector for natively-focusable elements + anything with an
|
|
8
|
-
* explicit positive (or implicit `0`) tabindex. Used by the focus
|
|
9
|
-
* trap to enumerate the drawer's interactive content.
|
|
10
|
-
*
|
|
11
|
-
* Notes:
|
|
12
|
-
* - Excludes `[tabindex="-1"]` (intentionally non-tabbable).
|
|
13
|
-
* - Excludes `:disabled` — the browser already skips them in tab
|
|
14
|
-
* order; including them here would route focus to dead elements.
|
|
15
|
-
* - `details > summary` is the standard way to get a focusable
|
|
16
|
-
* `<details>` toggle.
|
|
17
|
-
*
|
|
18
|
-
* @internal
|
|
19
|
-
*/
|
|
20
|
-
const FOCUSABLE_SELECTOR = [
|
|
21
|
-
'a[href]',
|
|
22
|
-
'button:not(:disabled)',
|
|
23
|
-
'input:not(:disabled):not([type="hidden"])',
|
|
24
|
-
'select:not(:disabled)',
|
|
25
|
-
'textarea:not(:disabled)',
|
|
26
|
-
'[tabindex]:not([tabindex="-1"]):not(:disabled)',
|
|
27
|
-
'details > summary:first-of-type',
|
|
28
|
-
].join(',');
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Dashforge TW AppShell — top-level layout orchestrator.
|
|
32
|
-
*
|
|
33
|
-
* ┌──────────────────────────────────────────┐
|
|
34
|
-
* │ header │ ← `header` slot
|
|
35
|
-
* ├────────┬─────────────────────────────────┤
|
|
36
|
-
* │ │ │
|
|
37
|
-
* │ nav │ main │ ← `nav` + `children`
|
|
38
|
-
* │ │ │
|
|
39
|
-
* ├────────┴─────────────────────────────────┤
|
|
40
|
-
* │ footer │ ← `footer` slot
|
|
41
|
-
* └──────────────────────────────────────────┘
|
|
42
|
-
*
|
|
43
|
-
* **Responsive**:
|
|
44
|
-
* - `≥ md`: nav stays in the inline slot at the left.
|
|
45
|
-
* - `< md`: nav becomes a slide-in drawer toggled by `navOpen`.
|
|
46
|
-
* A backdrop closes the drawer on click (mirrors `<Dialog>` UX).
|
|
47
|
-
*
|
|
48
|
-
* **Body scroll lock**: when the mobile drawer is open we add
|
|
49
|
-
* `overflow-hidden` to `<body>` to prevent dual-scroll wobble. The
|
|
50
|
-
* effect cleans up on close + on unmount.
|
|
51
|
-
*
|
|
52
|
-
* **A11y**:
|
|
53
|
-
* - `<main>` landmark wraps the content area.
|
|
54
|
-
* - `<header>` / `<footer>` come for free from their HTML tags.
|
|
55
|
-
* - The mobile drawer + backdrop participate in the standard
|
|
56
|
-
* "click outside to close" pattern — `Escape` closes the drawer
|
|
57
|
-
* too (added via global keydown).
|
|
58
|
-
* - **Focus trap** (WCAG 2.4.3) — when the mobile drawer opens:
|
|
59
|
-
* 1. The previously-focused element is captured.
|
|
60
|
-
* 2. Focus moves to the first focusable element inside the drawer.
|
|
61
|
-
* 3. Tab / Shift+Tab wrap inside the drawer (cycle from
|
|
62
|
-
* last → first and first → last) — Tab can never escape
|
|
63
|
-
* to the page underneath while the drawer is open.
|
|
64
|
-
* 4. On close, focus returns to the captured element (typically
|
|
65
|
-
* the hamburger toggle the user pressed to open the drawer).
|
|
66
|
-
* This pattern matches WAI-ARIA APG's modal dialog guidance even
|
|
67
|
-
* though the drawer isn't strictly a dialog — same focus
|
|
68
|
-
* management makes keyboard users' experience predictable.
|
|
69
|
-
* - **`role="dialog"` + `aria-modal="true"`** are applied to the
|
|
70
|
-
* drawer when open so screen readers announce it as a modal
|
|
71
|
-
* overlay rather than just an aside.
|
|
72
|
-
*/
|
|
73
|
-
export function AppShell(props: AppShellProps) {
|
|
74
|
-
const {
|
|
75
|
-
header,
|
|
76
|
-
nav,
|
|
77
|
-
footer,
|
|
78
|
-
children,
|
|
79
|
-
navOpen = false,
|
|
80
|
-
onNavOpenChange,
|
|
81
|
-
sx,
|
|
82
|
-
slotProps,
|
|
83
|
-
} = props;
|
|
84
|
-
|
|
85
|
-
const v = appShellVariants({ navOpen });
|
|
86
|
-
|
|
87
|
-
// Lock body scroll while the mobile drawer is open.
|
|
88
|
-
useEffect(() => {
|
|
89
|
-
if (!navOpen) return;
|
|
90
|
-
const prev = document.body.style.overflow;
|
|
91
|
-
document.body.style.overflow = 'hidden';
|
|
92
|
-
return () => {
|
|
93
|
-
document.body.style.overflow = prev;
|
|
94
|
-
};
|
|
95
|
-
}, [navOpen]);
|
|
96
|
-
|
|
97
|
-
// Esc closes the drawer.
|
|
98
|
-
useEffect(() => {
|
|
99
|
-
if (!navOpen || !onNavOpenChange) return;
|
|
100
|
-
const handler = (event: KeyboardEvent) => {
|
|
101
|
-
if (event.key === 'Escape') onNavOpenChange(false);
|
|
102
|
-
};
|
|
103
|
-
document.addEventListener('keydown', handler);
|
|
104
|
-
return () => document.removeEventListener('keydown', handler);
|
|
105
|
-
}, [navOpen, onNavOpenChange]);
|
|
106
|
-
|
|
107
|
-
/*
|
|
108
|
-
* Focus trap for the mobile drawer.
|
|
109
|
-
*
|
|
110
|
-
* Implementation:
|
|
111
|
-
* - On open: capture the currently-focused element (`document.activeElement`)
|
|
112
|
-
* so we can restore it on close. Then move focus to the first
|
|
113
|
-
* focusable element inside the drawer.
|
|
114
|
-
* - While open: a Tab/Shift+Tab keydown listener wraps focus inside
|
|
115
|
-
* the drawer subtree (last → first on Tab from end, first → last
|
|
116
|
-
* on Shift+Tab from start).
|
|
117
|
-
* - On close: restore focus to the captured element.
|
|
118
|
-
*
|
|
119
|
-
* Hand-rolled (no `focus-trap-react` dep) — the logic is ~20 LOC and
|
|
120
|
-
* scoped to a single drawer; adding a runtime dep felt like overkill.
|
|
121
|
-
* If we ever need more sophisticated trap semantics (nested traps,
|
|
122
|
-
* sentinel nodes, etc.) the dep is the right call.
|
|
123
|
-
*/
|
|
124
|
-
const drawerRef = useRef<HTMLDivElement | null>(null);
|
|
125
|
-
const restoreFocusRef = useRef<HTMLElement | null>(null);
|
|
126
|
-
|
|
127
|
-
useEffect(() => {
|
|
128
|
-
if (!navOpen) return;
|
|
129
|
-
// 1) Capture the element that had focus before the drawer opened.
|
|
130
|
-
restoreFocusRef.current = document.activeElement as HTMLElement | null;
|
|
131
|
-
|
|
132
|
-
// 2) Move focus to the first focusable element inside the drawer.
|
|
133
|
-
// Defer via rAF so the drawer DOM (slide-in animation start) has
|
|
134
|
-
// settled and the elements are actually visible/focusable.
|
|
135
|
-
const raf = requestAnimationFrame(() => {
|
|
136
|
-
const drawer = drawerRef.current;
|
|
137
|
-
if (!drawer) return;
|
|
138
|
-
const first = drawer.querySelector<HTMLElement>(FOCUSABLE_SELECTOR);
|
|
139
|
-
first?.focus();
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
// 3) Trap Tab / Shift+Tab inside the drawer.
|
|
143
|
-
const onKeyDown = (event: KeyboardEvent) => {
|
|
144
|
-
if (event.key !== 'Tab') return;
|
|
145
|
-
const drawer = drawerRef.current;
|
|
146
|
-
if (!drawer) return;
|
|
147
|
-
const focusables = Array.from(
|
|
148
|
-
drawer.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)
|
|
149
|
-
);
|
|
150
|
-
if (focusables.length === 0) {
|
|
151
|
-
event.preventDefault();
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
const first = focusables[0];
|
|
155
|
-
const last = focusables[focusables.length - 1];
|
|
156
|
-
const active = document.activeElement as HTMLElement | null;
|
|
157
|
-
if (event.shiftKey) {
|
|
158
|
-
// Shift+Tab on first → wrap to last.
|
|
159
|
-
if (active === first || !drawer.contains(active)) {
|
|
160
|
-
event.preventDefault();
|
|
161
|
-
last.focus();
|
|
162
|
-
}
|
|
163
|
-
} else {
|
|
164
|
-
// Tab on last → wrap to first.
|
|
165
|
-
if (active === last || !drawer.contains(active)) {
|
|
166
|
-
event.preventDefault();
|
|
167
|
-
first.focus();
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
};
|
|
171
|
-
document.addEventListener('keydown', onKeyDown);
|
|
172
|
-
|
|
173
|
-
return () => {
|
|
174
|
-
cancelAnimationFrame(raf);
|
|
175
|
-
document.removeEventListener('keydown', onKeyDown);
|
|
176
|
-
// 4) Restore focus to the previously-focused element on close.
|
|
177
|
-
// Guard against the element being removed from the DOM (rare
|
|
178
|
-
// but possible if the consumer re-renders the page while the
|
|
179
|
-
// drawer is open).
|
|
180
|
-
const restore = restoreFocusRef.current;
|
|
181
|
-
if (restore && document.body.contains(restore)) {
|
|
182
|
-
restore.focus();
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
}, [navOpen]);
|
|
186
|
-
|
|
187
|
-
return (
|
|
188
|
-
<div className={cn(v.root(), sx, slotProps?.root?.className)}>
|
|
189
|
-
{header && (
|
|
190
|
-
<div className={cn(v.header(), slotProps?.header?.className)}>
|
|
191
|
-
{header}
|
|
192
|
-
</div>
|
|
193
|
-
)}
|
|
194
|
-
|
|
195
|
-
<div className={v.body()}>
|
|
196
|
-
{nav && (
|
|
197
|
-
<>
|
|
198
|
-
<aside className={cn(v.nav(), slotProps?.nav?.className)}>
|
|
199
|
-
{nav}
|
|
200
|
-
</aside>
|
|
201
|
-
<aside
|
|
202
|
-
ref={drawerRef}
|
|
203
|
-
className={cn(v.navMobile(), slotProps?.navMobile?.className)}
|
|
204
|
-
aria-hidden={!navOpen}
|
|
205
|
-
// When open, the drawer is a modal overlay — `dialog` +
|
|
206
|
-
// `aria-modal="true"` so screen readers announce it as
|
|
207
|
-
// such. When closed, drop both attributes (the aria-
|
|
208
|
-
// hidden=true above already removes it from the AT tree).
|
|
209
|
-
role={navOpen ? 'dialog' : undefined}
|
|
210
|
-
aria-modal={navOpen ? true : undefined}
|
|
211
|
-
// The drawer is the SAME nav content rendered twice — once
|
|
212
|
-
// inline (desktop) and once as a slide-in (mobile). We
|
|
213
|
-
// mark the inactive copy hidden from AT to avoid double
|
|
214
|
-
// announcements.
|
|
215
|
-
>
|
|
216
|
-
{nav}
|
|
217
|
-
</aside>
|
|
218
|
-
<div
|
|
219
|
-
className={cn(v.backdrop(), slotProps?.backdrop?.className)}
|
|
220
|
-
role="presentation"
|
|
221
|
-
onClick={() => onNavOpenChange?.(false)}
|
|
222
|
-
data-testid="appshell-backdrop"
|
|
223
|
-
/>
|
|
224
|
-
</>
|
|
225
|
-
)}
|
|
226
|
-
|
|
227
|
-
<main className={cn(v.main(), slotProps?.main?.className)}>
|
|
228
|
-
{children}
|
|
229
|
-
</main>
|
|
230
|
-
</div>
|
|
231
|
-
|
|
232
|
-
{footer && (
|
|
233
|
-
<div className={cn(v.footer(), slotProps?.footer?.className)}>
|
|
234
|
-
{footer}
|
|
235
|
-
</div>
|
|
236
|
-
)}
|
|
237
|
-
</div>
|
|
238
|
-
);
|
|
239
|
-
}
|