@dashforge/tw 0.8.1-beta → 0.9.1-beta
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 +155 -0
- package/COVERAGE.md +63 -0
- package/LICENSE +21 -0
- package/PERFORMANCE.md +59 -0
- package/REFINEMENT-NOTES.md +50 -0
- package/dist/index.esm.js +806 -134
- package/dist/src/components/Calendar/Calendar.d.ts +14 -0
- package/dist/src/components/Calendar/Calendar.d.ts.map +1 -0
- package/dist/src/components/Calendar/calendar.types.d.ts +72 -0
- package/dist/src/components/Calendar/calendar.types.d.ts.map +1 -0
- package/dist/src/components/Calendar/calendar.variants.d.ts +99 -0
- package/dist/src/components/Calendar/calendar.variants.d.ts.map +1 -0
- package/dist/src/components/DataGrid/DataGrid.d.ts.map +1 -1
- package/dist/src/components/DataGrid/filters/ColumnFilters.d.ts.map +1 -1
- package/dist/src/components/DataGrid/resize/useColumnResize.d.ts +7 -0
- package/dist/src/components/DataGrid/resize/useColumnResize.d.ts.map +1 -1
- package/dist/src/components/DataGrid/visibility/ColumnVisibilityMenu.d.ts +6 -2
- package/dist/src/components/DataGrid/visibility/ColumnVisibilityMenu.d.ts.map +1 -1
- package/dist/src/components/DatePicker/DatePicker.d.ts +14 -0
- package/dist/src/components/DatePicker/DatePicker.d.ts.map +1 -0
- package/dist/src/components/DatePicker/datePicker.types.d.ts +88 -0
- package/dist/src/components/DatePicker/datePicker.types.d.ts.map +1 -0
- package/dist/src/components/DatePicker/datePicker.variants.d.ts +97 -0
- package/dist/src/components/DatePicker/datePicker.variants.d.ts.map +1 -0
- package/dist/src/components/Dialog/Dialog.d.ts.map +1 -1
- package/dist/src/components/Table/Table.d.ts.map +1 -1
- package/dist/src/components/Table/cells/RenderChip.d.ts.map +1 -1
- package/dist/src/components/Table/table.types.d.ts +28 -3
- package/dist/src/components/Table/table.types.d.ts.map +1 -1
- package/dist/src/components/Typography/typography.variants.d.ts.map +1 -1
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.d.ts.map +1 -1
- package/eslint.config.mjs +16 -1
- package/package.json +10 -6
- package/src/components/Breadcrumbs/Breadcrumbs.perf.test.tsx +5 -2
- package/src/components/Calendar/Calendar.test.tsx +75 -0
- package/src/components/Calendar/Calendar.tsx +264 -0
- package/src/components/Calendar/calendar.types.ts +65 -0
- package/src/components/Calendar/calendar.variants.ts +64 -0
- package/src/components/DataGrid/DataGrid.perf.test.tsx +114 -0
- package/src/components/DataGrid/DataGrid.test.tsx +240 -0
- package/src/components/DataGrid/DataGrid.tsx +104 -16
- package/src/components/DataGrid/filters/ColumnFilters.test.tsx +330 -0
- package/src/components/DataGrid/filters/ColumnFilters.tsx +26 -2
- package/src/components/DataGrid/reorder/useColumnReorder.test.ts +95 -3
- package/src/components/DataGrid/resize/useColumnResize.test.ts +144 -0
- package/src/components/DataGrid/resize/useColumnResize.ts +30 -1
- package/src/components/DataGrid/visibility/ColumnVisibilityMenu.tsx +42 -25
- package/src/components/DatePicker/DatePicker.test.tsx +76 -0
- package/src/components/DatePicker/DatePicker.tsx +305 -0
- package/src/components/DatePicker/datePicker.types.ts +77 -0
- package/src/components/DatePicker/datePicker.variants.ts +50 -0
- package/src/components/Dialog/Dialog.tsx +9 -0
- package/src/components/Table/Table.perf.test.tsx +81 -0
- package/src/components/Table/Table.test.tsx +72 -1
- package/src/components/Table/Table.tsx +40 -4
- package/src/components/Table/cells/RenderChip.tsx +6 -9
- package/src/components/Table/cells/cells.test.tsx +247 -0
- package/src/components/Table/table.types.ts +28 -3
- package/src/components/Typography/Typography.test.tsx +15 -0
- package/src/components/Typography/typography.variants.ts +14 -1
- package/src/index.ts +21 -1
- package/test-output/vitest/coverage/base.css +224 -0
- package/test-output/vitest/coverage/block-navigation.js +87 -0
- package/test-output/vitest/coverage/clover.xml +2418 -0
- package/test-output/vitest/coverage/coverage-final.json +89 -0
- package/test-output/vitest/coverage/favicon.png +0 -0
- package/test-output/vitest/coverage/index.html +746 -0
- package/test-output/vitest/coverage/prettify.css +1 -0
- package/test-output/vitest/coverage/prettify.js +2 -0
- package/test-output/vitest/coverage/sort-arrow-sprite.png +0 -0
- package/test-output/vitest/coverage/sorter.js +210 -0
- package/test-output/vitest/coverage/src/components/Accordion/Accordion.tsx.html +376 -0
- package/test-output/vitest/coverage/src/components/Accordion/accordion.variants.ts.html +181 -0
- package/test-output/vitest/coverage/src/components/Accordion/index.html +131 -0
- package/test-output/vitest/coverage/src/components/AppShell/AppShell.tsx.html +802 -0
- package/test-output/vitest/coverage/src/components/AppShell/appShell.variants.ts.html +247 -0
- package/test-output/vitest/coverage/src/components/AppShell/index.html +131 -0
- package/test-output/vitest/coverage/src/components/AspectRatio/AspectRatio.tsx.html +304 -0
- package/test-output/vitest/coverage/src/components/AspectRatio/index.html +116 -0
- package/test-output/vitest/coverage/src/components/Autocomplete/Autocomplete.tsx.html +3292 -0
- package/test-output/vitest/coverage/src/components/Autocomplete/autocomplete.variants.ts.html +508 -0
- package/test-output/vitest/coverage/src/components/Autocomplete/index.html +131 -0
- package/test-output/vitest/coverage/src/components/Box/Box.tsx.html +334 -0
- package/test-output/vitest/coverage/src/components/Box/box.variants.ts.html +775 -0
- package/test-output/vitest/coverage/src/components/Box/index.html +131 -0
- package/test-output/vitest/coverage/src/components/Breadcrumbs/Breadcrumbs.tsx.html +694 -0
- package/test-output/vitest/coverage/src/components/Breadcrumbs/breadcrumbs.variants.ts.html +247 -0
- package/test-output/vitest/coverage/src/components/Breadcrumbs/index.html +131 -0
- package/test-output/vitest/coverage/src/components/Button/Button.tsx.html +535 -0
- package/test-output/vitest/coverage/src/components/Button/button.variants.ts.html +562 -0
- package/test-output/vitest/coverage/src/components/Button/index.html +131 -0
- package/test-output/vitest/coverage/src/components/Checkbox/Checkbox.tsx.html +943 -0
- package/test-output/vitest/coverage/src/components/Checkbox/checkbox.variants.ts.html +289 -0
- package/test-output/vitest/coverage/src/components/Checkbox/index.html +131 -0
- package/test-output/vitest/coverage/src/components/ConfirmDialog/ConfirmDialog.tsx.html +841 -0
- package/test-output/vitest/coverage/src/components/ConfirmDialog/confirmDialog.variants.ts.html +313 -0
- package/test-output/vitest/coverage/src/components/ConfirmDialog/index.html +131 -0
- package/test-output/vitest/coverage/src/components/Container/Container.tsx.html +307 -0
- package/test-output/vitest/coverage/src/components/Container/container.variants.ts.html +328 -0
- package/test-output/vitest/coverage/src/components/Container/index.html +131 -0
- package/test-output/vitest/coverage/src/components/DataGrid/DataGrid.tsx.html +3937 -0
- package/test-output/vitest/coverage/src/components/DataGrid/dataGrid.variants.ts.html +541 -0
- package/test-output/vitest/coverage/src/components/DataGrid/filters/ColumnFilters.tsx.html +1453 -0
- package/test-output/vitest/coverage/src/components/DataGrid/filters/index.html +116 -0
- package/test-output/vitest/coverage/src/components/DataGrid/index.html +131 -0
- package/test-output/vitest/coverage/src/components/DataGrid/reorder/index.html +116 -0
- package/test-output/vitest/coverage/src/components/DataGrid/reorder/useColumnReorder.ts.html +517 -0
- package/test-output/vitest/coverage/src/components/DataGrid/resize/index.html +116 -0
- package/test-output/vitest/coverage/src/components/DataGrid/resize/useColumnResize.ts.html +490 -0
- package/test-output/vitest/coverage/src/components/DataGrid/visibility/ColumnVisibilityMenu.tsx.html +592 -0
- package/test-output/vitest/coverage/src/components/DataGrid/visibility/index.html +116 -0
- package/test-output/vitest/coverage/src/components/DateTimePicker/DateTimePicker.tsx.html +1042 -0
- package/test-output/vitest/coverage/src/components/DateTimePicker/dateTimePicker.variants.ts.html +337 -0
- package/test-output/vitest/coverage/src/components/DateTimePicker/index.html +131 -0
- package/test-output/vitest/coverage/src/components/Dialog/Dialog.tsx.html +391 -0
- package/test-output/vitest/coverage/src/components/Dialog/dialog.variants.ts.html +283 -0
- package/test-output/vitest/coverage/src/components/Dialog/index.html +131 -0
- package/test-output/vitest/coverage/src/components/Divider/Divider.tsx.html +505 -0
- package/test-output/vitest/coverage/src/components/Divider/divider.variants.ts.html +433 -0
- package/test-output/vitest/coverage/src/components/Divider/index.html +131 -0
- package/test-output/vitest/coverage/src/components/Grid/Grid.tsx.html +529 -0
- package/test-output/vitest/coverage/src/components/Grid/grid.variants.ts.html +484 -0
- package/test-output/vitest/coverage/src/components/Grid/index.html +131 -0
- package/test-output/vitest/coverage/src/components/LeftNav/LeftNav.tsx.html +1075 -0
- package/test-output/vitest/coverage/src/components/LeftNav/index.html +131 -0
- package/test-output/vitest/coverage/src/components/LeftNav/leftNav.variants.ts.html +406 -0
- package/test-output/vitest/coverage/src/components/NumberField/NumberField.tsx.html +1048 -0
- package/test-output/vitest/coverage/src/components/NumberField/index.html +131 -0
- package/test-output/vitest/coverage/src/components/NumberField/numberField.variants.ts.html +367 -0
- package/test-output/vitest/coverage/src/components/OTPField/OTPField.tsx.html +835 -0
- package/test-output/vitest/coverage/src/components/OTPField/index.html +131 -0
- package/test-output/vitest/coverage/src/components/OTPField/otpField.variants.ts.html +301 -0
- package/test-output/vitest/coverage/src/components/Pagination/Pagination.tsx.html +916 -0
- package/test-output/vitest/coverage/src/components/Pagination/index.html +146 -0
- package/test-output/vitest/coverage/src/components/Pagination/pagination.helpers.ts.html +391 -0
- package/test-output/vitest/coverage/src/components/Pagination/pagination.variants.ts.html +436 -0
- package/test-output/vitest/coverage/src/components/Popover/Popover.tsx.html +250 -0
- package/test-output/vitest/coverage/src/components/Popover/index.html +131 -0
- package/test-output/vitest/coverage/src/components/Popover/popover.variants.ts.html +157 -0
- package/test-output/vitest/coverage/src/components/RadioGroup/RadioGroup.tsx.html +934 -0
- package/test-output/vitest/coverage/src/components/RadioGroup/index.html +131 -0
- package/test-output/vitest/coverage/src/components/RadioGroup/radioGroup.variants.ts.html +346 -0
- package/test-output/vitest/coverage/src/components/Skeleton/Skeleton.tsx.html +232 -0
- package/test-output/vitest/coverage/src/components/Skeleton/index.html +131 -0
- package/test-output/vitest/coverage/src/components/Skeleton/skeleton.variants.ts.html +346 -0
- package/test-output/vitest/coverage/src/components/Snackbar/Snackbar.tsx.html +979 -0
- package/test-output/vitest/coverage/src/components/Snackbar/index.html +131 -0
- package/test-output/vitest/coverage/src/components/Snackbar/snackbar.variants.ts.html +307 -0
- package/test-output/vitest/coverage/src/components/Stack/Stack.tsx.html +430 -0
- package/test-output/vitest/coverage/src/components/Stack/index.html +131 -0
- package/test-output/vitest/coverage/src/components/Stack/stack.variants.ts.html +325 -0
- package/test-output/vitest/coverage/src/components/Switch/Switch.tsx.html +559 -0
- package/test-output/vitest/coverage/src/components/Switch/index.html +131 -0
- package/test-output/vitest/coverage/src/components/Switch/switch.variants.ts.html +295 -0
- package/test-output/vitest/coverage/src/components/Table/Table.tsx.html +2677 -0
- package/test-output/vitest/coverage/src/components/Table/cells/RenderButton.tsx.html +154 -0
- package/test-output/vitest/coverage/src/components/Table/cells/RenderChip.tsx.html +385 -0
- package/test-output/vitest/coverage/src/components/Table/cells/RenderText.tsx.html +196 -0
- package/test-output/vitest/coverage/src/components/Table/cells/RenderTwoLine.tsx.html +187 -0
- package/test-output/vitest/coverage/src/components/Table/cells/RowActionsMenu.tsx.html +526 -0
- package/test-output/vitest/coverage/src/components/Table/cells/index.html +191 -0
- package/test-output/vitest/coverage/src/components/Table/cells/index.ts.html +127 -0
- package/test-output/vitest/coverage/src/components/Table/index.html +131 -0
- package/test-output/vitest/coverage/src/components/Table/table.variants.ts.html +706 -0
- package/test-output/vitest/coverage/src/components/Tabs/Tabs.tsx.html +283 -0
- package/test-output/vitest/coverage/src/components/Tabs/index.html +131 -0
- package/test-output/vitest/coverage/src/components/Tabs/tabs.variants.ts.html +292 -0
- package/test-output/vitest/coverage/src/components/TextField/TextField.tsx.html +820 -0
- package/test-output/vitest/coverage/src/components/TextField/index.html +131 -0
- package/test-output/vitest/coverage/src/components/TextField/textField.variants.ts.html +400 -0
- package/test-output/vitest/coverage/src/components/Textarea/Textarea.tsx.html +655 -0
- package/test-output/vitest/coverage/src/components/Textarea/index.html +131 -0
- package/test-output/vitest/coverage/src/components/Textarea/textarea.variants.ts.html +340 -0
- package/test-output/vitest/coverage/src/components/Tooltip/Tooltip.tsx.html +295 -0
- package/test-output/vitest/coverage/src/components/Tooltip/index.html +131 -0
- package/test-output/vitest/coverage/src/components/Tooltip/tooltip.variants.ts.html +169 -0
- package/test-output/vitest/coverage/src/components/TopBar/TopBar.tsx.html +283 -0
- package/test-output/vitest/coverage/src/components/TopBar/index.html +131 -0
- package/test-output/vitest/coverage/src/components/TopBar/topBar.variants.ts.html +199 -0
- package/test-output/vitest/coverage/src/components/Typography/Typography.tsx.html +421 -0
- package/test-output/vitest/coverage/src/components/Typography/index.html +131 -0
- package/test-output/vitest/coverage/src/components/Typography/typography.variants.ts.html +436 -0
- package/test-output/vitest/coverage/src/components/VisuallyHidden/VisuallyHidden.tsx.html +262 -0
- package/test-output/vitest/coverage/src/components/VisuallyHidden/index.html +116 -0
- package/test-output/vitest/coverage/src/components/_shared/data/getNestedValue.ts.html +181 -0
- package/test-output/vitest/coverage/src/components/_shared/data/index.html +236 -0
- package/test-output/vitest/coverage/src/components/_shared/data/useColumnAutoDetect.ts.html +385 -0
- package/test-output/vitest/coverage/src/components/_shared/data/useControllableState.ts.html +229 -0
- package/test-output/vitest/coverage/src/components/_shared/data/useDebouncedValue.ts.html +157 -0
- package/test-output/vitest/coverage/src/components/_shared/data/useTableFilter.ts.html +439 -0
- package/test-output/vitest/coverage/src/components/_shared/data/useTableSearch.ts.html +283 -0
- package/test-output/vitest/coverage/src/components/_shared/data/useTableSelection.ts.html +385 -0
- package/test-output/vitest/coverage/src/components/_shared/data/useTableSort.ts.html +508 -0
- package/test-output/vitest/coverage/src/components/_shared/data/useVirtualizer.ts.html +682 -0
- package/test-output/vitest/coverage/src/components/_shared/index.html +116 -0
- package/test-output/vitest/coverage/src/components/_shared/resolveValidationState.ts.html +265 -0
- package/test-output/vitest/coverage/src/hooks/index.html +116 -0
- package/test-output/vitest/coverage/src/hooks/useAccessState.ts.html +328 -0
- package/test-output/vitest/coverage/src/index.html +116 -0
- package/test-output/vitest/coverage/src/index.ts.html +1153 -0
- package/test-output/vitest/coverage/src/utils/cn.ts.html +172 -0
- package/test-output/vitest/coverage/src/utils/index.html +116 -0
- package/tsconfig.lib.json +7 -4
package/dist/index.esm.js
CHANGED
|
@@ -11,10 +11,11 @@ import { useDashFieldMeta } from '@dashforge/forms';
|
|
|
11
11
|
import * as RadixCheckbox from '@radix-ui/react-checkbox';
|
|
12
12
|
import * as RadixSwitch from '@radix-ui/react-switch';
|
|
13
13
|
import * as RadixRadioGroup from '@radix-ui/react-radio-group';
|
|
14
|
+
import { useCalendar, resolveCalendarKey, formatLongDate, parseISODate } from '@dashforge/calendar-core';
|
|
15
|
+
import * as RadixPopover from '@radix-ui/react-popover';
|
|
14
16
|
import * as RadixDialog from '@radix-ui/react-dialog';
|
|
15
17
|
import * as RadixTabs from '@radix-ui/react-tabs';
|
|
16
18
|
import * as RadixTooltip from '@radix-ui/react-tooltip';
|
|
17
|
-
import * as RadixPopover from '@radix-ui/react-popover';
|
|
18
19
|
import * as RadixAccordion from '@radix-ui/react-accordion';
|
|
19
20
|
|
|
20
21
|
function _extends() {
|
|
@@ -3754,6 +3755,632 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
3754
3755
|
});
|
|
3755
3756
|
}
|
|
3756
3757
|
|
|
3758
|
+
/**
|
|
3759
|
+
* Structural slots for `<Calendar>`.
|
|
3760
|
+
*
|
|
3761
|
+
* Theme identity: every class is a bare neutral / brand token-backed
|
|
3762
|
+
* Tailwind class. The `dashforgePreset()` CSS-variable swap auto-inverts
|
|
3763
|
+
* the neutral palette, so there are NO `dark:` variants here (a `dark:` on
|
|
3764
|
+
* a neutral class would double-invert — see `_shared/themeIdentity.test.ts`).
|
|
3765
|
+
*/ const calendarVariants = tv({
|
|
3766
|
+
slots: {
|
|
3767
|
+
root: 'inline-flex w-fit flex-col gap-2 rounded-lg border border-neutral-200 bg-neutral-50 p-3 select-none',
|
|
3768
|
+
header: 'flex items-center justify-between gap-1',
|
|
3769
|
+
navButton: 'inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-[1rem] text-neutral-600 transition-colors hover:bg-neutral-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 disabled:pointer-events-none disabled:opacity-40',
|
|
3770
|
+
monthLabel: 'flex-1 text-center text-sm font-semibold text-neutral-900',
|
|
3771
|
+
grid: 'flex flex-col gap-1 focus:outline-none',
|
|
3772
|
+
weekdayRow: 'grid grid-cols-7 gap-1',
|
|
3773
|
+
weekday: 'flex h-8 items-center justify-center text-xs font-semibold text-neutral-500',
|
|
3774
|
+
weekRow: 'grid grid-cols-7 gap-1'
|
|
3775
|
+
},
|
|
3776
|
+
variants: {
|
|
3777
|
+
disabled: {
|
|
3778
|
+
true: {
|
|
3779
|
+
root: 'opacity-60'
|
|
3780
|
+
}
|
|
3781
|
+
}
|
|
3782
|
+
},
|
|
3783
|
+
defaultVariants: {
|
|
3784
|
+
disabled: false
|
|
3785
|
+
}
|
|
3786
|
+
});
|
|
3787
|
+
/**
|
|
3788
|
+
* Per-day-cell recipe. Each of the 42 grid cells is rendered with its own
|
|
3789
|
+
* state (`siblingMonth` / `today` / `selected` / `disabled`).
|
|
3790
|
+
*/ const calendarDayVariants = tv({
|
|
3791
|
+
base: 'flex h-9 w-9 items-center justify-center rounded-md text-sm text-neutral-900 transition-colors hover:bg-neutral-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500',
|
|
3792
|
+
variants: {
|
|
3793
|
+
siblingMonth: {
|
|
3794
|
+
true: 'text-neutral-400'
|
|
3795
|
+
},
|
|
3796
|
+
today: {
|
|
3797
|
+
true: 'font-bold ring-1 ring-inset ring-primary-400'
|
|
3798
|
+
},
|
|
3799
|
+
selected: {
|
|
3800
|
+
true: 'bg-primary-600 text-white hover:bg-primary-600'
|
|
3801
|
+
},
|
|
3802
|
+
disabled: {
|
|
3803
|
+
true: 'cursor-default text-neutral-300 opacity-60 hover:bg-transparent'
|
|
3804
|
+
}
|
|
3805
|
+
},
|
|
3806
|
+
compoundVariants: [
|
|
3807
|
+
// A selected cell drops the "today" ring — the solid fill is enough.
|
|
3808
|
+
{
|
|
3809
|
+
selected: true,
|
|
3810
|
+
today: true,
|
|
3811
|
+
class: 'ring-0'
|
|
3812
|
+
}
|
|
3813
|
+
],
|
|
3814
|
+
defaultVariants: {
|
|
3815
|
+
siblingMonth: false,
|
|
3816
|
+
today: false,
|
|
3817
|
+
selected: false,
|
|
3818
|
+
disabled: false
|
|
3819
|
+
}
|
|
3820
|
+
});
|
|
3821
|
+
|
|
3822
|
+
// Inline 16×16 stroke chevrons — no icon dependency (tw convention).
|
|
3823
|
+
function ChevronLeftIcon() {
|
|
3824
|
+
return jsx("svg", {
|
|
3825
|
+
width: "1em",
|
|
3826
|
+
height: "1em",
|
|
3827
|
+
viewBox: "0 0 16 16",
|
|
3828
|
+
fill: "none",
|
|
3829
|
+
"aria-hidden": "true",
|
|
3830
|
+
children: jsx("path", {
|
|
3831
|
+
d: "M10 4 6 8l4 4",
|
|
3832
|
+
stroke: "currentColor",
|
|
3833
|
+
strokeWidth: "1.5",
|
|
3834
|
+
strokeLinecap: "round",
|
|
3835
|
+
strokeLinejoin: "round"
|
|
3836
|
+
})
|
|
3837
|
+
});
|
|
3838
|
+
}
|
|
3839
|
+
function ChevronRightIcon$1() {
|
|
3840
|
+
return jsx("svg", {
|
|
3841
|
+
width: "1em",
|
|
3842
|
+
height: "1em",
|
|
3843
|
+
viewBox: "0 0 16 16",
|
|
3844
|
+
fill: "none",
|
|
3845
|
+
"aria-hidden": "true",
|
|
3846
|
+
children: jsx("path", {
|
|
3847
|
+
d: "m6 4 4 4-4 4",
|
|
3848
|
+
stroke: "currentColor",
|
|
3849
|
+
strokeWidth: "1.5",
|
|
3850
|
+
strokeLinecap: "round",
|
|
3851
|
+
strokeLinejoin: "round"
|
|
3852
|
+
})
|
|
3853
|
+
});
|
|
3854
|
+
}
|
|
3855
|
+
/**
|
|
3856
|
+
* `<Calendar>` — a standalone, inline month-grid date picker.
|
|
3857
|
+
*
|
|
3858
|
+
* The Tailwind presentation layer over the headless `useCalendar` engine
|
|
3859
|
+
* from `@dashforge/calendar-core` (the same engine the MUI `@dashforge/ui`
|
|
3860
|
+
* calendar consumes — only the skin differs). Implements the WCAG grid
|
|
3861
|
+
* pattern with roving tab-index focus.
|
|
3862
|
+
*
|
|
3863
|
+
* It is a primitive: no form-bridge / RBAC integration — use `<DatePicker>`
|
|
3864
|
+
* for a form-bound date field.
|
|
3865
|
+
*/ function Calendar(props) {
|
|
3866
|
+
var _slotProps_root, _slotProps_header, _slotProps_grid;
|
|
3867
|
+
const { value, defaultValue, onChange, month, year, defaultMonth, defaultYear, onMonthChange, minDate, maxDate, disabledDates, isDateDisabled, weekStartDay, locale, today, disabled = false, autoFocus = false, sx, slotProps, testId, 'aria-label': ariaLabel = 'Calendar' } = props;
|
|
3868
|
+
const isValueControlled = value !== undefined;
|
|
3869
|
+
const [internalValue, setInternalValue] = useState(defaultValue != null ? defaultValue : null);
|
|
3870
|
+
const selectedValue = isValueControlled ? value : internalValue;
|
|
3871
|
+
const handleSelect = useCallback((iso)=>{
|
|
3872
|
+
if (!isValueControlled) {
|
|
3873
|
+
setInternalValue(iso);
|
|
3874
|
+
}
|
|
3875
|
+
onChange == null ? void 0 : onChange(iso);
|
|
3876
|
+
}, [
|
|
3877
|
+
isValueControlled,
|
|
3878
|
+
onChange
|
|
3879
|
+
]);
|
|
3880
|
+
const calendar = useCalendar({
|
|
3881
|
+
selected: selectedValue,
|
|
3882
|
+
month,
|
|
3883
|
+
year,
|
|
3884
|
+
defaultMonth,
|
|
3885
|
+
defaultYear,
|
|
3886
|
+
onMonthChange,
|
|
3887
|
+
minDate,
|
|
3888
|
+
maxDate,
|
|
3889
|
+
disabledDates,
|
|
3890
|
+
isDateDisabled,
|
|
3891
|
+
weekStartDay,
|
|
3892
|
+
locale,
|
|
3893
|
+
today,
|
|
3894
|
+
onSelect: handleSelect
|
|
3895
|
+
});
|
|
3896
|
+
// Roving focus: the active day cell holds DOM focus; it is moved only
|
|
3897
|
+
// after a keyboard interaction (or on mount when `autoFocus`). `focus`
|
|
3898
|
+
// uses `preventScroll` so opening the calendar inside a popup never
|
|
3899
|
+
// scroll-jumps the page before the popup is positioned.
|
|
3900
|
+
const focusedCellRef = useRef(null);
|
|
3901
|
+
const shouldFocusRef = useRef(autoFocus);
|
|
3902
|
+
useEffect(()=>{
|
|
3903
|
+
if (shouldFocusRef.current && focusedCellRef.current) {
|
|
3904
|
+
focusedCellRef.current.focus({
|
|
3905
|
+
preventScroll: true
|
|
3906
|
+
});
|
|
3907
|
+
shouldFocusRef.current = false;
|
|
3908
|
+
}
|
|
3909
|
+
}, [
|
|
3910
|
+
calendar.focusedDate
|
|
3911
|
+
]);
|
|
3912
|
+
const handleKeyDown = useCallback((event)=>{
|
|
3913
|
+
if (disabled) {
|
|
3914
|
+
return;
|
|
3915
|
+
}
|
|
3916
|
+
const action = resolveCalendarKey(event.key);
|
|
3917
|
+
if (action === null) {
|
|
3918
|
+
return;
|
|
3919
|
+
}
|
|
3920
|
+
event.preventDefault();
|
|
3921
|
+
shouldFocusRef.current = true;
|
|
3922
|
+
switch(action){
|
|
3923
|
+
case 'select':
|
|
3924
|
+
calendar.selectDate(calendar.focusedDate);
|
|
3925
|
+
break;
|
|
3926
|
+
case 'monthForward':
|
|
3927
|
+
calendar.goToNextMonth();
|
|
3928
|
+
break;
|
|
3929
|
+
case 'monthBackward':
|
|
3930
|
+
calendar.goToPreviousMonth();
|
|
3931
|
+
break;
|
|
3932
|
+
default:
|
|
3933
|
+
calendar.navigate(action);
|
|
3934
|
+
}
|
|
3935
|
+
}, [
|
|
3936
|
+
disabled,
|
|
3937
|
+
calendar
|
|
3938
|
+
]);
|
|
3939
|
+
const resolvedLocale = locale != null ? locale : 'en-US';
|
|
3940
|
+
const v = calendarVariants({
|
|
3941
|
+
disabled
|
|
3942
|
+
});
|
|
3943
|
+
return jsxs("div", {
|
|
3944
|
+
"data-testid": testId,
|
|
3945
|
+
className: cn(v.root(), sx, slotProps == null ? void 0 : (_slotProps_root = slotProps.root) == null ? void 0 : _slotProps_root.className),
|
|
3946
|
+
children: [
|
|
3947
|
+
jsxs("div", {
|
|
3948
|
+
className: cn(v.header(), slotProps == null ? void 0 : (_slotProps_header = slotProps.header) == null ? void 0 : _slotProps_header.className),
|
|
3949
|
+
children: [
|
|
3950
|
+
jsx("button", {
|
|
3951
|
+
type: "button",
|
|
3952
|
+
className: v.navButton(),
|
|
3953
|
+
disabled: disabled,
|
|
3954
|
+
"aria-label": "Previous month",
|
|
3955
|
+
onClick: ()=>{
|
|
3956
|
+
calendar.goToPreviousMonth();
|
|
3957
|
+
},
|
|
3958
|
+
children: jsx(ChevronLeftIcon, {})
|
|
3959
|
+
}),
|
|
3960
|
+
jsx("span", {
|
|
3961
|
+
"aria-live": "polite",
|
|
3962
|
+
className: v.monthLabel(),
|
|
3963
|
+
children: calendar.monthLabel
|
|
3964
|
+
}),
|
|
3965
|
+
jsx("button", {
|
|
3966
|
+
type: "button",
|
|
3967
|
+
className: v.navButton(),
|
|
3968
|
+
disabled: disabled,
|
|
3969
|
+
"aria-label": "Next month",
|
|
3970
|
+
onClick: ()=>{
|
|
3971
|
+
calendar.goToNextMonth();
|
|
3972
|
+
},
|
|
3973
|
+
children: jsx(ChevronRightIcon$1, {})
|
|
3974
|
+
})
|
|
3975
|
+
]
|
|
3976
|
+
}),
|
|
3977
|
+
jsxs("div", {
|
|
3978
|
+
role: "grid",
|
|
3979
|
+
"aria-label": ariaLabel,
|
|
3980
|
+
onKeyDown: handleKeyDown,
|
|
3981
|
+
className: cn(v.grid(), slotProps == null ? void 0 : (_slotProps_grid = slotProps.grid) == null ? void 0 : _slotProps_grid.className),
|
|
3982
|
+
children: [
|
|
3983
|
+
jsx("div", {
|
|
3984
|
+
role: "row",
|
|
3985
|
+
className: v.weekdayRow(),
|
|
3986
|
+
children: calendar.weekdayNames.map((dayName, index)=>jsx("span", {
|
|
3987
|
+
role: "columnheader",
|
|
3988
|
+
className: v.weekday(),
|
|
3989
|
+
children: dayName
|
|
3990
|
+
}, `weekday-${String(index)}`))
|
|
3991
|
+
}),
|
|
3992
|
+
calendar.weeks.map((week)=>jsx("div", {
|
|
3993
|
+
role: "row",
|
|
3994
|
+
className: v.weekRow(),
|
|
3995
|
+
children: week.days.map((day)=>{
|
|
3996
|
+
var _slotProps_day;
|
|
3997
|
+
const isActiveCell = day.iso === calendar.focusedDate;
|
|
3998
|
+
return jsx("span", {
|
|
3999
|
+
role: "gridcell",
|
|
4000
|
+
className: "flex",
|
|
4001
|
+
children: jsx("button", {
|
|
4002
|
+
type: "button",
|
|
4003
|
+
ref: isActiveCell ? focusedCellRef : null,
|
|
4004
|
+
tabIndex: isActiveCell ? 0 : -1,
|
|
4005
|
+
disabled: disabled,
|
|
4006
|
+
"aria-label": formatLongDate({
|
|
4007
|
+
year: day.year,
|
|
4008
|
+
month: day.month,
|
|
4009
|
+
day: day.day
|
|
4010
|
+
}, resolvedLocale),
|
|
4011
|
+
"aria-current": day.isToday ? 'date' : undefined,
|
|
4012
|
+
"aria-pressed": day.isSelected,
|
|
4013
|
+
"aria-disabled": day.isDisabled,
|
|
4014
|
+
onClick: ()=>{
|
|
4015
|
+
calendar.selectDate(day.iso);
|
|
4016
|
+
},
|
|
4017
|
+
className: cn(calendarDayVariants({
|
|
4018
|
+
siblingMonth: day.isSiblingMonth,
|
|
4019
|
+
today: day.isToday,
|
|
4020
|
+
selected: day.isSelected,
|
|
4021
|
+
disabled: day.isDisabled
|
|
4022
|
+
}), slotProps == null ? void 0 : (_slotProps_day = slotProps.day) == null ? void 0 : _slotProps_day.className),
|
|
4023
|
+
children: day.day
|
|
4024
|
+
})
|
|
4025
|
+
}, day.iso);
|
|
4026
|
+
})
|
|
4027
|
+
}, week.key))
|
|
4028
|
+
]
|
|
4029
|
+
})
|
|
4030
|
+
]
|
|
4031
|
+
});
|
|
4032
|
+
}
|
|
4033
|
+
|
|
4034
|
+
const popoverVariants = tv({
|
|
4035
|
+
slots: {
|
|
4036
|
+
content: [
|
|
4037
|
+
// Neutral border auto-inverts via CSS-var swap.
|
|
4038
|
+
'z-50 rounded-md border border-neutral-200',
|
|
4039
|
+
// bg-white hardcoded (no auto-invert) so `dark:` is required.
|
|
4040
|
+
// Target `dark:bg-neutral-100` resolves to #171717 in dark mode
|
|
4041
|
+
// (one elevation tier above the page surface).
|
|
4042
|
+
'bg-white dark:bg-neutral-100 p-4 shadow-lg',
|
|
4043
|
+
// Text auto-inverts.
|
|
4044
|
+
'text-sm text-neutral-900',
|
|
4045
|
+
'focus:outline-none',
|
|
4046
|
+
'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
|
|
4047
|
+
'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
|
|
4048
|
+
'motion-reduce:transition-none motion-reduce:duration-0'
|
|
4049
|
+
],
|
|
4050
|
+
// Arrow follows content bg: `fill-white` in light + matching dark target.
|
|
4051
|
+
arrow: 'fill-white dark:fill-neutral-100'
|
|
4052
|
+
}
|
|
4053
|
+
});
|
|
4054
|
+
|
|
4055
|
+
/**
|
|
4056
|
+
* Dashforge TW `<Popover>` — clickable floating content panel.
|
|
4057
|
+
*
|
|
4058
|
+
* Built on `@radix-ui/react-popover`:
|
|
4059
|
+
* - Focus trap inside the popover (Tab cycles within).
|
|
4060
|
+
* - Outside-click + Escape dismiss.
|
|
4061
|
+
* - APG dialog-like semantics (`role="dialog"` on content).
|
|
4062
|
+
* - Portal-rendered.
|
|
4063
|
+
*
|
|
4064
|
+
* Use `<Popover>` for menus / pickers / settings panels with
|
|
4065
|
+
* interactive children. Use `<Tooltip>` for read-only hints.
|
|
4066
|
+
*/ function Popover(props) {
|
|
4067
|
+
var _slotProps_content, _slotProps_arrow;
|
|
4068
|
+
const { children, content, side = 'bottom', align = 'center', showArrow = false, open, onOpenChange, sideOffset = 8, width, slotProps } = props;
|
|
4069
|
+
const v = popoverVariants();
|
|
4070
|
+
return jsxs(RadixPopover.Root, {
|
|
4071
|
+
open: open,
|
|
4072
|
+
onOpenChange: onOpenChange,
|
|
4073
|
+
children: [
|
|
4074
|
+
jsx(RadixPopover.Trigger, {
|
|
4075
|
+
asChild: true,
|
|
4076
|
+
children: children
|
|
4077
|
+
}),
|
|
4078
|
+
jsx(RadixPopover.Portal, {
|
|
4079
|
+
children: jsxs(RadixPopover.Content, {
|
|
4080
|
+
side: side,
|
|
4081
|
+
align: align,
|
|
4082
|
+
sideOffset: sideOffset,
|
|
4083
|
+
style: width != null ? {
|
|
4084
|
+
width
|
|
4085
|
+
} : undefined,
|
|
4086
|
+
className: cn(v.content(), slotProps == null ? void 0 : (_slotProps_content = slotProps.content) == null ? void 0 : _slotProps_content.className),
|
|
4087
|
+
children: [
|
|
4088
|
+
content,
|
|
4089
|
+
showArrow && jsx(RadixPopover.Arrow, {
|
|
4090
|
+
className: cn(v.arrow(), slotProps == null ? void 0 : (_slotProps_arrow = slotProps.arrow) == null ? void 0 : _slotProps_arrow.className)
|
|
4091
|
+
})
|
|
4092
|
+
]
|
|
4093
|
+
})
|
|
4094
|
+
})
|
|
4095
|
+
]
|
|
4096
|
+
});
|
|
4097
|
+
}
|
|
4098
|
+
|
|
4099
|
+
/**
|
|
4100
|
+
* Slots for `<DatePicker>` — a bridge-integrated date field whose trigger
|
|
4101
|
+
* is a `<button>` styled as an input, opening a `<Calendar>` popover.
|
|
4102
|
+
*
|
|
4103
|
+
* Theme identity: bare neutral / brand token-backed classes only — no
|
|
4104
|
+
* `dark:` variants on the neutral palette (the preset CSS-var swap
|
|
4105
|
+
* auto-inverts). See `_shared/themeIdentity.test.ts`.
|
|
4106
|
+
*/ const datePickerVariants = tv({
|
|
4107
|
+
slots: {
|
|
4108
|
+
root: 'flex flex-col',
|
|
4109
|
+
label: 'block text-sm font-medium text-neutral-900 mb-1',
|
|
4110
|
+
requiredMark: 'text-danger-500 ml-0.5',
|
|
4111
|
+
trigger: 'flex items-center gap-2 h-10 min-w-[12rem] rounded-md border border-neutral-300 bg-neutral-50 px-3 text-base text-neutral-900 cursor-pointer transition-colors hover:border-neutral-400 focus-visible:outline-none focus-visible:border-primary-500 focus-visible:ring-2 focus-visible:ring-primary-500/30 disabled:cursor-not-allowed disabled:opacity-60 disabled:bg-neutral-200',
|
|
4112
|
+
value: 'flex-1 truncate text-left',
|
|
4113
|
+
placeholder: 'flex-1 truncate text-left text-neutral-400',
|
|
4114
|
+
icon: 'shrink-0 text-[1rem] text-neutral-500',
|
|
4115
|
+
helperText: 'mt-1 text-sm text-neutral-600',
|
|
4116
|
+
errorText: 'mt-1 text-sm text-danger-600'
|
|
4117
|
+
},
|
|
4118
|
+
variants: {
|
|
4119
|
+
layout: {
|
|
4120
|
+
stacked: {},
|
|
4121
|
+
inline: {
|
|
4122
|
+
root: 'flex-row items-start gap-3',
|
|
4123
|
+
label: 'mb-0 pt-2.5 whitespace-nowrap shrink-0'
|
|
4124
|
+
}
|
|
4125
|
+
},
|
|
4126
|
+
error: {
|
|
4127
|
+
true: {
|
|
4128
|
+
trigger: 'border-danger-500 hover:border-danger-500 focus-visible:border-danger-500 focus-visible:ring-danger-500/30'
|
|
4129
|
+
}
|
|
4130
|
+
},
|
|
4131
|
+
fullWidth: {
|
|
4132
|
+
true: {
|
|
4133
|
+
root: 'w-full',
|
|
4134
|
+
trigger: 'w-full min-w-0'
|
|
4135
|
+
}
|
|
4136
|
+
}
|
|
4137
|
+
},
|
|
4138
|
+
defaultVariants: {
|
|
4139
|
+
layout: 'stacked',
|
|
4140
|
+
error: false,
|
|
4141
|
+
fullWidth: false
|
|
4142
|
+
}
|
|
4143
|
+
});
|
|
4144
|
+
|
|
4145
|
+
// Inline 16×16 stroke calendar glyph — no icon dependency (tw convention).
|
|
4146
|
+
function CalendarIcon() {
|
|
4147
|
+
return jsxs("svg", {
|
|
4148
|
+
width: "1em",
|
|
4149
|
+
height: "1em",
|
|
4150
|
+
viewBox: "0 0 16 16",
|
|
4151
|
+
fill: "none",
|
|
4152
|
+
"aria-hidden": "true",
|
|
4153
|
+
children: [
|
|
4154
|
+
jsx("rect", {
|
|
4155
|
+
x: "2.5",
|
|
4156
|
+
y: "3.5",
|
|
4157
|
+
width: "11",
|
|
4158
|
+
height: "10",
|
|
4159
|
+
rx: "1.5",
|
|
4160
|
+
stroke: "currentColor",
|
|
4161
|
+
strokeWidth: "1.5"
|
|
4162
|
+
}),
|
|
4163
|
+
jsx("path", {
|
|
4164
|
+
d: "M2.5 6.5h11M5.5 2v3M10.5 2v3",
|
|
4165
|
+
stroke: "currentColor",
|
|
4166
|
+
strokeWidth: "1.5",
|
|
4167
|
+
strokeLinecap: "round"
|
|
4168
|
+
})
|
|
4169
|
+
]
|
|
4170
|
+
});
|
|
4171
|
+
}
|
|
4172
|
+
/**
|
|
4173
|
+
* Formats a stored ISO date for the trigger display, localized to a medium
|
|
4174
|
+
* date style (e.g. `"May 20, 2026"`). Formatting is done in UTC so the
|
|
4175
|
+
* displayed day always matches the stored `YYYY-MM-DD`.
|
|
4176
|
+
*/ function formatDisplayDate(iso, locale) {
|
|
4177
|
+
if (iso == null || iso === '') {
|
|
4178
|
+
return '';
|
|
4179
|
+
}
|
|
4180
|
+
const parts = parseISODate(iso);
|
|
4181
|
+
if (parts === null) {
|
|
4182
|
+
return iso;
|
|
4183
|
+
}
|
|
4184
|
+
return new Intl.DateTimeFormat(locale, {
|
|
4185
|
+
dateStyle: 'medium',
|
|
4186
|
+
timeZone: 'UTC'
|
|
4187
|
+
}).format(Date.UTC(parts.year, parts.month - 1, parts.day));
|
|
4188
|
+
}
|
|
4189
|
+
/**
|
|
4190
|
+
* Dashforge TW `<DatePicker>` — a form-bound single-date field.
|
|
4191
|
+
*
|
|
4192
|
+
* A read-only trigger button paired with a `<Calendar>` popover (Radix
|
|
4193
|
+
* Popover — toggle / Escape / outside-click / focus-trap are Radix-managed).
|
|
4194
|
+
* Integrates with the Dashforge form bridge + RBAC, mirroring the other
|
|
4195
|
+
* `@dashforge/tw` form fields.
|
|
4196
|
+
*
|
|
4197
|
+
* Storage contract: an ISO `YYYY-MM-DD` string, or `null` — shared with the
|
|
4198
|
+
* MUI `@dashforge/ui` `DatePicker`.
|
|
4199
|
+
*/ function DatePicker(props) {
|
|
4200
|
+
var _slotProps_root, _slotProps_label, _slotProps_requiredMark, _slotProps_trigger, _slotProps_errorText, _slotProps_helperText;
|
|
4201
|
+
const { name, rules, label, helperText, error, required, disabled, placeholder, layout = 'stacked', visibleWhen, access, value: explicitValue, defaultValue, onChange, minDate, maxDate, disabledDates, isDateDisabled, weekStartDay, locale, fullWidth, sx, slotProps, testId } = props;
|
|
4202
|
+
// ───── Hooks (unconditional, before any early return) ─────
|
|
4203
|
+
const bridge = useContext(DashFormContext);
|
|
4204
|
+
const isVisible = useEngineVisibility(bridge == null ? void 0 : bridge.engine, visibleWhen);
|
|
4205
|
+
const fieldMeta = useDashFieldMeta(name);
|
|
4206
|
+
const accessState = useAccessState(access);
|
|
4207
|
+
const fieldId = useId();
|
|
4208
|
+
const helperId = `${fieldId}-help`;
|
|
4209
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
4210
|
+
const [internalValue, setInternalValue] = useState(defaultValue != null ? defaultValue : null);
|
|
4211
|
+
// StrictMode-safe unregister-on-unmount.
|
|
4212
|
+
const unregisterRef = useRef({
|
|
4213
|
+
bridge,
|
|
4214
|
+
name
|
|
4215
|
+
});
|
|
4216
|
+
unregisterRef.current = {
|
|
4217
|
+
bridge,
|
|
4218
|
+
name
|
|
4219
|
+
};
|
|
4220
|
+
const isMountedRef = useRef(false);
|
|
4221
|
+
useEffect(()=>{
|
|
4222
|
+
isMountedRef.current = true;
|
|
4223
|
+
return ()=>{
|
|
4224
|
+
isMountedRef.current = false;
|
|
4225
|
+
const { bridge: cap, name: capName } = unregisterRef.current;
|
|
4226
|
+
queueMicrotask(()=>{
|
|
4227
|
+
var _cap_unregister;
|
|
4228
|
+
if (!isMountedRef.current) cap == null ? void 0 : (_cap_unregister = cap.unregister) == null ? void 0 : _cap_unregister.call(cap, capName);
|
|
4229
|
+
});
|
|
4230
|
+
};
|
|
4231
|
+
}, []);
|
|
4232
|
+
// ───── Derived ─────
|
|
4233
|
+
const effectiveDisabled = Boolean(disabled) || accessState.disabled;
|
|
4234
|
+
const effectiveReadOnly = accessState.readonly;
|
|
4235
|
+
const isInteractive = !effectiveDisabled && !effectiveReadOnly;
|
|
4236
|
+
const isFormMode = Boolean(bridge == null ? void 0 : bridge.register);
|
|
4237
|
+
let resolvedError = error;
|
|
4238
|
+
let resolvedHelperText = helperText;
|
|
4239
|
+
let registration = null;
|
|
4240
|
+
let resolvedValue;
|
|
4241
|
+
if (isFormMode && bridge) {
|
|
4242
|
+
registration = bridge.register(name, rules);
|
|
4243
|
+
const validation = resolveValidationState(name, bridge, error, helperText);
|
|
4244
|
+
resolvedError = validation.error;
|
|
4245
|
+
resolvedHelperText = validation.helperText;
|
|
4246
|
+
if (explicitValue !== undefined) {
|
|
4247
|
+
resolvedValue = explicitValue;
|
|
4248
|
+
} else {
|
|
4249
|
+
const bv = fieldMeta.value;
|
|
4250
|
+
resolvedValue = bv == null ? null : String(bv);
|
|
4251
|
+
}
|
|
4252
|
+
} else if (explicitValue !== undefined) {
|
|
4253
|
+
resolvedValue = explicitValue;
|
|
4254
|
+
} else {
|
|
4255
|
+
resolvedValue = internalValue;
|
|
4256
|
+
}
|
|
4257
|
+
// RHF threads its own callback ref through here in form mode.
|
|
4258
|
+
const registrationRefFn = registration == null ? void 0 : registration.ref;
|
|
4259
|
+
const triggerRef = useCallback((instance)=>{
|
|
4260
|
+
if (typeof registrationRefFn === 'function') {
|
|
4261
|
+
registrationRefFn(instance);
|
|
4262
|
+
}
|
|
4263
|
+
}, [
|
|
4264
|
+
registrationRefFn
|
|
4265
|
+
]);
|
|
4266
|
+
// ───── Render-time guards (after all hooks) ─────
|
|
4267
|
+
if (!isVisible) return null;
|
|
4268
|
+
if (!accessState.visible) return null;
|
|
4269
|
+
const v = datePickerVariants({
|
|
4270
|
+
layout,
|
|
4271
|
+
error: resolvedError,
|
|
4272
|
+
fullWidth
|
|
4273
|
+
});
|
|
4274
|
+
const resolvedLocale = locale != null ? locale : 'en-US';
|
|
4275
|
+
const displayValue = formatDisplayDate(resolvedValue, resolvedLocale);
|
|
4276
|
+
const commitValue = (next)=>{
|
|
4277
|
+
if (isFormMode && bridge) {
|
|
4278
|
+
var _registration_onChange;
|
|
4279
|
+
bridge.setValue == null ? void 0 : bridge.setValue.call(bridge, name, next);
|
|
4280
|
+
void (registration == null ? void 0 : (_registration_onChange = registration.onChange) == null ? void 0 : _registration_onChange.call(registration, {
|
|
4281
|
+
target: {
|
|
4282
|
+
name,
|
|
4283
|
+
value: next != null ? next : ''
|
|
4284
|
+
},
|
|
4285
|
+
type: 'change'
|
|
4286
|
+
}));
|
|
4287
|
+
} else if (explicitValue === undefined) {
|
|
4288
|
+
setInternalValue(next);
|
|
4289
|
+
}
|
|
4290
|
+
onChange == null ? void 0 : onChange(next);
|
|
4291
|
+
};
|
|
4292
|
+
// Mark the field touched on popover close. Reads the freshest committed
|
|
4293
|
+
// value straight from the bridge so the touched-blur never reverts a
|
|
4294
|
+
// value just chosen in the calendar.
|
|
4295
|
+
const markTouched = ()=>{
|
|
4296
|
+
if (isFormMode && bridge) {
|
|
4297
|
+
var _registration_onBlur;
|
|
4298
|
+
const committed = bridge.getValue(name);
|
|
4299
|
+
void (registration == null ? void 0 : (_registration_onBlur = registration.onBlur) == null ? void 0 : _registration_onBlur.call(registration, {
|
|
4300
|
+
target: {
|
|
4301
|
+
name,
|
|
4302
|
+
value: committed == null ? '' : String(committed)
|
|
4303
|
+
},
|
|
4304
|
+
type: 'blur'
|
|
4305
|
+
}));
|
|
4306
|
+
}
|
|
4307
|
+
};
|
|
4308
|
+
const handleOpenChange = (next)=>{
|
|
4309
|
+
if (next && !isInteractive) {
|
|
4310
|
+
return;
|
|
4311
|
+
}
|
|
4312
|
+
setIsOpen(next);
|
|
4313
|
+
if (!next) {
|
|
4314
|
+
markTouched();
|
|
4315
|
+
}
|
|
4316
|
+
};
|
|
4317
|
+
const handleCalendarChange = (next)=>{
|
|
4318
|
+
commitValue(next);
|
|
4319
|
+
setIsOpen(false);
|
|
4320
|
+
markTouched();
|
|
4321
|
+
};
|
|
4322
|
+
return jsxs("div", {
|
|
4323
|
+
"data-testid": testId,
|
|
4324
|
+
className: cn(v.root(), sx, slotProps == null ? void 0 : (_slotProps_root = slotProps.root) == null ? void 0 : _slotProps_root.className),
|
|
4325
|
+
children: [
|
|
4326
|
+
label && jsxs("label", {
|
|
4327
|
+
htmlFor: fieldId,
|
|
4328
|
+
className: cn(v.label(), slotProps == null ? void 0 : (_slotProps_label = slotProps.label) == null ? void 0 : _slotProps_label.className),
|
|
4329
|
+
children: [
|
|
4330
|
+
label,
|
|
4331
|
+
required && jsx("span", {
|
|
4332
|
+
"aria-hidden": "true",
|
|
4333
|
+
className: cn(v.requiredMark(), slotProps == null ? void 0 : (_slotProps_requiredMark = slotProps.requiredMark) == null ? void 0 : _slotProps_requiredMark.className),
|
|
4334
|
+
children: "*"
|
|
4335
|
+
})
|
|
4336
|
+
]
|
|
4337
|
+
}),
|
|
4338
|
+
jsx(Popover, {
|
|
4339
|
+
open: isOpen,
|
|
4340
|
+
onOpenChange: handleOpenChange,
|
|
4341
|
+
side: "bottom",
|
|
4342
|
+
align: "start",
|
|
4343
|
+
content: jsx(Calendar, {
|
|
4344
|
+
value: resolvedValue,
|
|
4345
|
+
onChange: handleCalendarChange,
|
|
4346
|
+
minDate: minDate,
|
|
4347
|
+
maxDate: maxDate,
|
|
4348
|
+
disabledDates: disabledDates,
|
|
4349
|
+
isDateDisabled: isDateDisabled,
|
|
4350
|
+
weekStartDay: weekStartDay,
|
|
4351
|
+
locale: locale,
|
|
4352
|
+
"aria-label": "Choose date",
|
|
4353
|
+
sx: "border-0 bg-transparent p-0"
|
|
4354
|
+
}),
|
|
4355
|
+
children: jsxs("button", {
|
|
4356
|
+
type: "button",
|
|
4357
|
+
id: fieldId,
|
|
4358
|
+
ref: triggerRef,
|
|
4359
|
+
disabled: effectiveDisabled,
|
|
4360
|
+
"aria-invalid": resolvedError ? true : undefined,
|
|
4361
|
+
"aria-describedby": resolvedHelperText ? helperId : undefined,
|
|
4362
|
+
className: cn(v.trigger(), slotProps == null ? void 0 : (_slotProps_trigger = slotProps.trigger) == null ? void 0 : _slotProps_trigger.className),
|
|
4363
|
+
children: [
|
|
4364
|
+
jsx("span", {
|
|
4365
|
+
className: displayValue ? v.value() : v.placeholder(),
|
|
4366
|
+
children: displayValue || placeholder || 'Select a date'
|
|
4367
|
+
}),
|
|
4368
|
+
jsx("span", {
|
|
4369
|
+
className: v.icon(),
|
|
4370
|
+
children: jsx(CalendarIcon, {})
|
|
4371
|
+
})
|
|
4372
|
+
]
|
|
4373
|
+
})
|
|
4374
|
+
}),
|
|
4375
|
+
resolvedHelperText && jsx("p", {
|
|
4376
|
+
id: helperId,
|
|
4377
|
+
className: cn(resolvedError ? v.errorText() : v.helperText(), resolvedError ? slotProps == null ? void 0 : (_slotProps_errorText = slotProps.errorText) == null ? void 0 : _slotProps_errorText.className : slotProps == null ? void 0 : (_slotProps_helperText = slotProps.helperText) == null ? void 0 : _slotProps_helperText.className),
|
|
4378
|
+
children: resolvedHelperText
|
|
4379
|
+
})
|
|
4380
|
+
]
|
|
4381
|
+
});
|
|
4382
|
+
}
|
|
4383
|
+
|
|
3757
4384
|
/**
|
|
3758
4385
|
* Tailwind-variants recipe for `<Breadcrumbs>`.
|
|
3759
4386
|
*
|
|
@@ -5285,6 +5912,15 @@ const SnackbarContext = /*#__PURE__*/ createContext(null);
|
|
|
5285
5912
|
* has set its own color (e.g. `<Box variant="solid" color="primary">`
|
|
5286
5913
|
* paints white text).
|
|
5287
5914
|
*/ color: {
|
|
5915
|
+
// `inherit` is the explicit escape hatch — use it on a
|
|
5916
|
+
// `<Typography>` inside a coloured surface (e.g.
|
|
5917
|
+
// `<Box variant="solid" color="primary">` paints white text)
|
|
5918
|
+
// so the text picks up the surface colour. It is NO LONGER the
|
|
5919
|
+
// default: a bare `<Typography>` would otherwise inherit the
|
|
5920
|
+
// consuming app's arbitrary root colour (often OS-`prefers-
|
|
5921
|
+
// color-scheme` driven), which diverges from the Dashforge
|
|
5922
|
+
// `data-dash-tw-theme` mode and breaks colour consistency in
|
|
5923
|
+
// dark mode. The default is now `neutral` — see `defaultVariants`.
|
|
5288
5924
|
inherit: 'text-inherit',
|
|
5289
5925
|
primary: 'text-primary-700 dark:text-primary-400',
|
|
5290
5926
|
secondary: 'text-secondary-700 dark:text-secondary-400',
|
|
@@ -5337,7 +5973,11 @@ const SnackbarContext = /*#__PURE__*/ createContext(null);
|
|
|
5337
5973
|
},
|
|
5338
5974
|
defaultVariants: {
|
|
5339
5975
|
variant: 'body1',
|
|
5340
|
-
|
|
5976
|
+
// `neutral` (not `inherit`): a bare `<Typography>` emits
|
|
5977
|
+
// `text-neutral-900`, which auto-inverts via the dashforgePreset()
|
|
5978
|
+
// CSS-var swap and follows the Dashforge mode. Pass
|
|
5979
|
+
// `color="inherit"` explicitly inside a coloured surface.
|
|
5980
|
+
color: 'neutral',
|
|
5341
5981
|
align: 'left'
|
|
5342
5982
|
}
|
|
5343
5983
|
});
|
|
@@ -6915,8 +7555,11 @@ VisuallyHidden.displayName = 'VisuallyHidden';
|
|
|
6915
7555
|
jsx(RadixDialog.Overlay, {
|
|
6916
7556
|
className: cn(v.overlay(), slotProps == null ? void 0 : (_slotProps_overlay = slotProps.overlay) == null ? void 0 : _slotProps_overlay.className)
|
|
6917
7557
|
}),
|
|
6918
|
-
jsxs(RadixDialog.Content, {
|
|
6919
|
-
className: cn(v.content(), sx, slotProps == null ? void 0 : (_slotProps_content = slotProps.content) == null ? void 0 : _slotProps_content.className)
|
|
7558
|
+
jsxs(RadixDialog.Content, _extends({
|
|
7559
|
+
className: cn(v.content(), sx, slotProps == null ? void 0 : (_slotProps_content = slotProps.content) == null ? void 0 : _slotProps_content.className)
|
|
7560
|
+
}, description == null ? {
|
|
7561
|
+
'aria-describedby': undefined
|
|
7562
|
+
} : {}, {
|
|
6920
7563
|
onPointerDownOutside: (event)=>{
|
|
6921
7564
|
if (disableBackdropClose) event.preventDefault();
|
|
6922
7565
|
},
|
|
@@ -6955,7 +7598,7 @@ VisuallyHidden.displayName = 'VisuallyHidden';
|
|
|
6955
7598
|
})
|
|
6956
7599
|
})
|
|
6957
7600
|
]
|
|
6958
|
-
})
|
|
7601
|
+
}))
|
|
6959
7602
|
]
|
|
6960
7603
|
})
|
|
6961
7604
|
});
|
|
@@ -7160,71 +7803,6 @@ const tooltipVariants = tv({
|
|
|
7160
7803
|
});
|
|
7161
7804
|
}
|
|
7162
7805
|
|
|
7163
|
-
const popoverVariants = tv({
|
|
7164
|
-
slots: {
|
|
7165
|
-
content: [
|
|
7166
|
-
// Neutral border auto-inverts via CSS-var swap.
|
|
7167
|
-
'z-50 rounded-md border border-neutral-200',
|
|
7168
|
-
// bg-white hardcoded (no auto-invert) so `dark:` is required.
|
|
7169
|
-
// Target `dark:bg-neutral-100` resolves to #171717 in dark mode
|
|
7170
|
-
// (one elevation tier above the page surface).
|
|
7171
|
-
'bg-white dark:bg-neutral-100 p-4 shadow-lg',
|
|
7172
|
-
// Text auto-inverts.
|
|
7173
|
-
'text-sm text-neutral-900',
|
|
7174
|
-
'focus:outline-none',
|
|
7175
|
-
'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
|
|
7176
|
-
'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
|
|
7177
|
-
'motion-reduce:transition-none motion-reduce:duration-0'
|
|
7178
|
-
],
|
|
7179
|
-
// Arrow follows content bg: `fill-white` in light + matching dark target.
|
|
7180
|
-
arrow: 'fill-white dark:fill-neutral-100'
|
|
7181
|
-
}
|
|
7182
|
-
});
|
|
7183
|
-
|
|
7184
|
-
/**
|
|
7185
|
-
* Dashforge TW `<Popover>` — clickable floating content panel.
|
|
7186
|
-
*
|
|
7187
|
-
* Built on `@radix-ui/react-popover`:
|
|
7188
|
-
* - Focus trap inside the popover (Tab cycles within).
|
|
7189
|
-
* - Outside-click + Escape dismiss.
|
|
7190
|
-
* - APG dialog-like semantics (`role="dialog"` on content).
|
|
7191
|
-
* - Portal-rendered.
|
|
7192
|
-
*
|
|
7193
|
-
* Use `<Popover>` for menus / pickers / settings panels with
|
|
7194
|
-
* interactive children. Use `<Tooltip>` for read-only hints.
|
|
7195
|
-
*/ function Popover(props) {
|
|
7196
|
-
var _slotProps_content, _slotProps_arrow;
|
|
7197
|
-
const { children, content, side = 'bottom', align = 'center', showArrow = false, open, onOpenChange, sideOffset = 8, width, slotProps } = props;
|
|
7198
|
-
const v = popoverVariants();
|
|
7199
|
-
return jsxs(RadixPopover.Root, {
|
|
7200
|
-
open: open,
|
|
7201
|
-
onOpenChange: onOpenChange,
|
|
7202
|
-
children: [
|
|
7203
|
-
jsx(RadixPopover.Trigger, {
|
|
7204
|
-
asChild: true,
|
|
7205
|
-
children: children
|
|
7206
|
-
}),
|
|
7207
|
-
jsx(RadixPopover.Portal, {
|
|
7208
|
-
children: jsxs(RadixPopover.Content, {
|
|
7209
|
-
side: side,
|
|
7210
|
-
align: align,
|
|
7211
|
-
sideOffset: sideOffset,
|
|
7212
|
-
style: width != null ? {
|
|
7213
|
-
width
|
|
7214
|
-
} : undefined,
|
|
7215
|
-
className: cn(v.content(), slotProps == null ? void 0 : (_slotProps_content = slotProps.content) == null ? void 0 : _slotProps_content.className),
|
|
7216
|
-
children: [
|
|
7217
|
-
content,
|
|
7218
|
-
showArrow && jsx(RadixPopover.Arrow, {
|
|
7219
|
-
className: cn(v.arrow(), slotProps == null ? void 0 : (_slotProps_arrow = slotProps.arrow) == null ? void 0 : _slotProps_arrow.className)
|
|
7220
|
-
})
|
|
7221
|
-
]
|
|
7222
|
-
})
|
|
7223
|
-
})
|
|
7224
|
-
]
|
|
7225
|
-
});
|
|
7226
|
-
}
|
|
7227
|
-
|
|
7228
7806
|
const accordionVariants = tv({
|
|
7229
7807
|
slots: {
|
|
7230
7808
|
root: 'w-full',
|
|
@@ -8650,6 +9228,7 @@ function inferType(value) {
|
|
|
8650
9228
|
const DEFAULT_LABELS$1 = {
|
|
8651
9229
|
searchPlaceholder: 'Search...',
|
|
8652
9230
|
noData: 'No data',
|
|
9231
|
+
noResults: 'No matching results',
|
|
8653
9232
|
loading: 'Loading…',
|
|
8654
9233
|
ariaSortNone: 'Not sorted. Click to sort ascending.',
|
|
8655
9234
|
ariaSortAscending: 'Sorted ascending. Click to sort descending.',
|
|
@@ -8676,8 +9255,7 @@ const DEFAULT_LABELS$1 = {
|
|
|
8676
9255
|
densitySpacious: 'Spacious',
|
|
8677
9256
|
columnsButton: 'Columns',
|
|
8678
9257
|
columnsTitle: 'Toggle columns',
|
|
8679
|
-
|
|
8680
|
-
columnsHideAll: 'Hide all'
|
|
9258
|
+
columnsToggleAll: 'Toggle all columns'
|
|
8681
9259
|
};
|
|
8682
9260
|
const DEFAULT_GET_ROW_ID = (_row, index)=>String(index);
|
|
8683
9261
|
/**
|
|
@@ -8706,6 +9284,26 @@ const DEFAULT_GET_ROW_ID = (_row, index)=>String(index);
|
|
|
8706
9284
|
sortModel: sortModelProp, onSortChange, defaultSortModel, enableSearch = false, searchQuery: searchQueryProp, onSearchQueryChange, searchPlaceholder, searchDebounceMs = 200, filterModel: filterModelProp, onFilterChange, rowSelection = 'none', selectedRowIds: selectedRowIdsProp, onSelectionChange, bulkActions, expandable, rowActions, loading = false, loadingRowCount = 5, emptyState, stickyHeader = true, caption, showCaption = false, access: tableAccess, labels: labelsProp, variant, size, density, sx, slotProps } = props;
|
|
8707
9285
|
const labels = _extends({}, DEFAULT_LABELS$1, labelsProp);
|
|
8708
9286
|
const tableAccessState = useAccessState(tableAccess);
|
|
9287
|
+
// Dev-only guard: `getRowId` is optional (positional-index fallback)
|
|
9288
|
+
// but that fallback breaks row identity once rows reorder (sort) or
|
|
9289
|
+
// change presence (search / filter) — selection + expansion state
|
|
9290
|
+
// then jump to the wrong rows. Warn when it's omitted AND a feature
|
|
9291
|
+
// that relies on stable identity is active. Stripped in production.
|
|
9292
|
+
const getRowIdProvided = props.getRowId !== undefined;
|
|
9293
|
+
useEffect(()=>{
|
|
9294
|
+
if (process.env.NODE_ENV === 'production') return;
|
|
9295
|
+
if (getRowIdProvided) return;
|
|
9296
|
+
const identityMatters = rowSelection !== 'none' || expandable != null || enableSearch || cols.some((c)=>Boolean(c.sortable));
|
|
9297
|
+
if (identityMatters) {
|
|
9298
|
+
console.warn('[Dashforge Table] `getRowId` is not set — falling back to the ' + 'positional row index. With sort / search / selection / ' + 'expandable rows active, the index-based key breaks row ' + 'identity when rows reorder. Pass a stable resolver, e.g. ' + '`getRowId={(row) => row.id}`.');
|
|
9299
|
+
}
|
|
9300
|
+
}, [
|
|
9301
|
+
getRowIdProvided,
|
|
9302
|
+
rowSelection,
|
|
9303
|
+
expandable,
|
|
9304
|
+
enableSearch,
|
|
9305
|
+
cols
|
|
9306
|
+
]);
|
|
8709
9307
|
// ───── Controllable state machinery ─────
|
|
8710
9308
|
const [sortModel, setSortModel] = useControllableState({
|
|
8711
9309
|
controlledValue: sortModelProp,
|
|
@@ -8861,7 +9459,9 @@ const DEFAULT_GET_ROW_ID = (_row, index)=>String(index);
|
|
|
8861
9459
|
}) : sortedRows.length === 0 ? renderEmptyState$1({
|
|
8862
9460
|
totalColumnCount,
|
|
8863
9461
|
emptyState,
|
|
8864
|
-
|
|
9462
|
+
// Distinguish a genuinely empty dataset from one
|
|
9463
|
+
// emptied by an active search / filter.
|
|
9464
|
+
fallback: rows.length > 0 && (debouncedSearchQuery.trim().length > 0 || filterModel.length > 0) ? labels.noResults : labels.noData,
|
|
8865
9465
|
cellClass: cn(v.cell(), v.emptyState(), slotProps == null ? void 0 : (_slotProps_emptyState = slotProps.emptyState) == null ? void 0 : _slotProps_emptyState.className)
|
|
8866
9466
|
}) : sortedRows.map((row, idx)=>{
|
|
8867
9467
|
var _slotProps_row, _slotProps_cell, _slotProps_cell1, _slotProps_selectionCell, _slotProps_cell2, _slotProps_expandToggleCell, _slotProps_cell3, _slotProps_rowActionsCell, _slotProps_expandedRow;
|
|
@@ -9083,6 +9683,10 @@ function handleSortClick$1(col, event, current, setSortModel) {
|
|
|
9083
9683
|
}
|
|
9084
9684
|
function renderLoadingRows$1(args) {
|
|
9085
9685
|
const { count, visibleCols, showSelectionColumn, showExpandColumn, showRowActionsColumn, rowClass, cellClass } = args;
|
|
9686
|
+
// No explicit row height here (unlike DataGrid's loading rows, which
|
|
9687
|
+
// pin `rowHeight` for the virtualization math): Table is auto-height
|
|
9688
|
+
// by design. Skeleton rows inherit the same density padding via
|
|
9689
|
+
// `cellClass`, so they line up with real rows without a fixed height.
|
|
9086
9690
|
return Array.from({
|
|
9087
9691
|
length: count
|
|
9088
9692
|
}).map((_, i)=>jsxs("tr", {
|
|
@@ -9324,15 +9928,12 @@ function SearchIcon$1(props) {
|
|
|
9324
9928
|
color: 'secondary',
|
|
9325
9929
|
class: 'bg-secondary-100 dark:bg-secondary-950 text-secondary-700 dark:text-secondary-300'
|
|
9326
9930
|
},
|
|
9327
|
-
// solid = full bg +
|
|
9328
|
-
// solid neutral: bg
|
|
9329
|
-
//
|
|
9330
|
-
//
|
|
9331
|
-
//
|
|
9332
|
-
//
|
|
9333
|
-
// auto-inverts: light text in light mode (against dark bg) is
|
|
9334
|
-
// what we want; in dark mode `bg-neutral-700` becomes light AND
|
|
9335
|
-
// `text-neutral-50` becomes dark — contrast preserved.
|
|
9931
|
+
// solid = full bg + contrasting text.
|
|
9932
|
+
// solid neutral: both `bg-neutral-700` and `text-neutral-50` sit
|
|
9933
|
+
// on the neutral palette, so they auto-invert together via the
|
|
9934
|
+
// CSS-var swap — bg/text contrast is preserved in BOTH modes.
|
|
9935
|
+
// (`text-white` is deliberately NOT used here: white does not
|
|
9936
|
+
// auto-invert, which would flip the contrast in dark mode.)
|
|
9336
9937
|
{
|
|
9337
9938
|
variant: 'solid',
|
|
9338
9939
|
color: 'neutral',
|
|
@@ -9863,6 +10464,17 @@ function setFilter(model, field, next) {
|
|
|
9863
10464
|
next
|
|
9864
10465
|
];
|
|
9865
10466
|
}
|
|
10467
|
+
/**
|
|
10468
|
+
* Shared `onKeyDown` for filter inputs — pressing Enter commits the
|
|
10469
|
+
* filter (equivalent to clicking Apply). Without this the user has to
|
|
10470
|
+
* leave the keyboard and click the button, which reads as broken for
|
|
10471
|
+
* a single-field text filter.
|
|
10472
|
+
*/ function onEnterApply(e, apply) {
|
|
10473
|
+
if (e.key === 'Enter') {
|
|
10474
|
+
e.preventDefault();
|
|
10475
|
+
apply();
|
|
10476
|
+
}
|
|
10477
|
+
}
|
|
9866
10478
|
const DEFAULT_FILTER_LABELS = {
|
|
9867
10479
|
filterColumn: 'Filter',
|
|
9868
10480
|
filterApply: 'Apply',
|
|
@@ -9948,6 +10560,11 @@ function FilterPanel(props) {
|
|
|
9948
10560
|
function TextFilter(props) {
|
|
9949
10561
|
const { field, current, onCommit, labels } = props;
|
|
9950
10562
|
const [value, setValue] = useState(typeof (current == null ? void 0 : current.value) === 'string' ? current.value : '');
|
|
10563
|
+
const apply = ()=>onCommit(value.trim() ? {
|
|
10564
|
+
field,
|
|
10565
|
+
op: 'contains',
|
|
10566
|
+
value
|
|
10567
|
+
} : null);
|
|
9951
10568
|
return jsxs("div", {
|
|
9952
10569
|
className: "flex flex-col gap-2 min-w-[200px]",
|
|
9953
10570
|
children: [
|
|
@@ -9955,16 +10572,13 @@ function TextFilter(props) {
|
|
|
9955
10572
|
type: "text",
|
|
9956
10573
|
value: value,
|
|
9957
10574
|
onChange: (e)=>setValue(e.target.value),
|
|
10575
|
+
onKeyDown: (e)=>onEnterApply(e, apply),
|
|
9958
10576
|
placeholder: "\u2026",
|
|
9959
10577
|
autoFocus: true,
|
|
9960
10578
|
className: "rounded-md border border-neutral-300 bg-neutral-50 px-2 py-1.5 text-sm focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500"
|
|
9961
10579
|
}),
|
|
9962
10580
|
jsx(FilterActions, {
|
|
9963
|
-
onApply:
|
|
9964
|
-
field,
|
|
9965
|
-
op: 'contains',
|
|
9966
|
-
value
|
|
9967
|
-
} : null),
|
|
10581
|
+
onApply: apply,
|
|
9968
10582
|
onClear: ()=>{
|
|
9969
10583
|
setValue('');
|
|
9970
10584
|
onCommit(null);
|
|
@@ -10018,6 +10632,7 @@ function NumberRangeFilter(props) {
|
|
|
10018
10632
|
type: "number",
|
|
10019
10633
|
value: min,
|
|
10020
10634
|
onChange: (e)=>setMin(e.target.value),
|
|
10635
|
+
onKeyDown: (e)=>onEnterApply(e, apply),
|
|
10021
10636
|
autoFocus: true,
|
|
10022
10637
|
className: "rounded-md border border-neutral-300 bg-neutral-50 px-2 py-1.5 text-sm focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500"
|
|
10023
10638
|
})
|
|
@@ -10033,6 +10648,7 @@ function NumberRangeFilter(props) {
|
|
|
10033
10648
|
type: "number",
|
|
10034
10649
|
value: max,
|
|
10035
10650
|
onChange: (e)=>setMax(e.target.value),
|
|
10651
|
+
onKeyDown: (e)=>onEnterApply(e, apply),
|
|
10036
10652
|
className: "rounded-md border border-neutral-300 bg-neutral-50 px-2 py-1.5 text-sm focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500"
|
|
10037
10653
|
})
|
|
10038
10654
|
]
|
|
@@ -10141,6 +10757,7 @@ function DateRangeFilter(props) {
|
|
|
10141
10757
|
type: "date",
|
|
10142
10758
|
value: from,
|
|
10143
10759
|
onChange: (e)=>setFrom(e.target.value),
|
|
10760
|
+
onKeyDown: (e)=>onEnterApply(e, apply),
|
|
10144
10761
|
autoFocus: true,
|
|
10145
10762
|
className: "rounded-md border border-neutral-300 bg-neutral-50 px-2 py-1.5 text-sm focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500"
|
|
10146
10763
|
})
|
|
@@ -10156,6 +10773,7 @@ function DateRangeFilter(props) {
|
|
|
10156
10773
|
type: "date",
|
|
10157
10774
|
value: to,
|
|
10158
10775
|
onChange: (e)=>setTo(e.target.value),
|
|
10776
|
+
onKeyDown: (e)=>onEnterApply(e, apply),
|
|
10159
10777
|
className: "rounded-md border border-neutral-300 bg-neutral-50 px-2 py-1.5 text-sm focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500"
|
|
10160
10778
|
})
|
|
10161
10779
|
]
|
|
@@ -10215,8 +10833,7 @@ function FilterIcon() {
|
|
|
10215
10833
|
const DEFAULT_VISIBILITY_LABELS = {
|
|
10216
10834
|
columnsButton: 'Columns',
|
|
10217
10835
|
columnsTitle: 'Toggle columns',
|
|
10218
|
-
|
|
10219
|
-
columnsHideAll: 'Hide all'
|
|
10836
|
+
columnsToggleAll: 'Toggle all columns'
|
|
10220
10837
|
};
|
|
10221
10838
|
function ColumnVisibilityTrigger(props) {
|
|
10222
10839
|
const { cols, hiddenColumns, onChange, disabled, labels: labelsProp } = props;
|
|
@@ -10232,44 +10849,40 @@ function ColumnVisibilityTrigger(props) {
|
|
|
10232
10849
|
field
|
|
10233
10850
|
]);
|
|
10234
10851
|
};
|
|
10235
|
-
|
|
10236
|
-
|
|
10852
|
+
// Tri-state master: checked = every hideable column visible,
|
|
10853
|
+
// unchecked = every one hidden, indeterminate = mixed.
|
|
10854
|
+
const allVisible = hideableCols.every((c)=>!hiddenColumns.includes(c.field));
|
|
10855
|
+
const allHidden = hideableCols.length > 0 && hideableCols.every((c)=>hiddenColumns.includes(c.field));
|
|
10856
|
+
const someHidden = !allVisible && !allHidden;
|
|
10857
|
+
// Standard select-all semantics: when everything is visible the
|
|
10858
|
+
// master hides all; otherwise it shows all.
|
|
10859
|
+
const toggleAll = ()=>{
|
|
10860
|
+
onChange(allVisible ? hideableCols.map((c)=>c.field) : []);
|
|
10861
|
+
};
|
|
10237
10862
|
return jsx(Popover, {
|
|
10238
10863
|
open: open,
|
|
10239
10864
|
onOpenChange: setOpen,
|
|
10240
10865
|
side: "bottom",
|
|
10241
10866
|
align: "end",
|
|
10242
10867
|
content: jsxs("div", {
|
|
10243
|
-
className: "flex flex-col gap-
|
|
10868
|
+
className: "flex flex-col gap-1 min-w-[220px]",
|
|
10244
10869
|
children: [
|
|
10245
10870
|
jsxs("div", {
|
|
10246
|
-
className: "flex items-center
|
|
10871
|
+
className: "flex items-center gap-2 px-1 pb-2 mb-1 border-b border-neutral-200",
|
|
10247
10872
|
children: [
|
|
10873
|
+
jsx("input", {
|
|
10874
|
+
type: "checkbox",
|
|
10875
|
+
"aria-label": labels.columnsToggleAll,
|
|
10876
|
+
checked: allVisible,
|
|
10877
|
+
ref: (el)=>{
|
|
10878
|
+
if (el) el.indeterminate = someHidden;
|
|
10879
|
+
},
|
|
10880
|
+
onChange: toggleAll,
|
|
10881
|
+
className: "cursor-pointer"
|
|
10882
|
+
}),
|
|
10248
10883
|
jsx("span", {
|
|
10249
10884
|
className: "text-xs font-semibold text-neutral-700 uppercase tracking-wide",
|
|
10250
10885
|
children: labels.columnsTitle
|
|
10251
|
-
}),
|
|
10252
|
-
jsxs("div", {
|
|
10253
|
-
className: "flex items-center gap-2",
|
|
10254
|
-
children: [
|
|
10255
|
-
jsx("button", {
|
|
10256
|
-
type: "button",
|
|
10257
|
-
onClick: showAll,
|
|
10258
|
-
className: "text-xs text-neutral-600 hover:text-neutral-900 underline-offset-2 hover:underline focus:outline-none focus-visible:underline",
|
|
10259
|
-
children: labels.columnsShowAll
|
|
10260
|
-
}),
|
|
10261
|
-
jsx("span", {
|
|
10262
|
-
className: "text-neutral-300",
|
|
10263
|
-
"aria-hidden": "true",
|
|
10264
|
-
children: "\u00B7"
|
|
10265
|
-
}),
|
|
10266
|
-
jsx("button", {
|
|
10267
|
-
type: "button",
|
|
10268
|
-
onClick: hideAll,
|
|
10269
|
-
className: "text-xs text-neutral-600 hover:text-neutral-900 underline-offset-2 hover:underline focus:outline-none focus-visible:underline",
|
|
10270
|
-
children: labels.columnsHideAll
|
|
10271
|
-
})
|
|
10272
|
-
]
|
|
10273
10886
|
})
|
|
10274
10887
|
]
|
|
10275
10888
|
}),
|
|
@@ -10398,8 +11011,21 @@ function useColumnResize(args) {
|
|
|
10398
11011
|
minWidth,
|
|
10399
11012
|
maxWidth
|
|
10400
11013
|
]);
|
|
11014
|
+
const nudge = useCallback((field, currentWidth, delta, clampMin, clampMax)=>{
|
|
11015
|
+
const effectiveMin = Math.max(minWidth, clampMin);
|
|
11016
|
+
const effectiveMax = Math.min(maxWidth, clampMax);
|
|
11017
|
+
const next = clamp(currentWidth + delta, effectiveMin, effectiveMax);
|
|
11018
|
+
onChange(_extends({}, widthsRef.current, {
|
|
11019
|
+
[field]: next
|
|
11020
|
+
}));
|
|
11021
|
+
}, [
|
|
11022
|
+
onChange,
|
|
11023
|
+
minWidth,
|
|
11024
|
+
maxWidth
|
|
11025
|
+
]);
|
|
10401
11026
|
return {
|
|
10402
|
-
startResize
|
|
11027
|
+
startResize,
|
|
11028
|
+
nudge
|
|
10403
11029
|
};
|
|
10404
11030
|
}
|
|
10405
11031
|
function clamp(n, min, max) {
|
|
@@ -10494,6 +11120,7 @@ function arraysEqual(a, b) {
|
|
|
10494
11120
|
const DEFAULT_LABELS = {
|
|
10495
11121
|
searchPlaceholder: 'Search...',
|
|
10496
11122
|
noData: 'No data',
|
|
11123
|
+
noResults: 'No matching results',
|
|
10497
11124
|
loading: 'Loading…',
|
|
10498
11125
|
ariaSortNone: 'Not sorted. Click to sort ascending.',
|
|
10499
11126
|
ariaSortAscending: 'Sorted ascending. Click to sort descending.',
|
|
@@ -10520,8 +11147,7 @@ const DEFAULT_LABELS = {
|
|
|
10520
11147
|
densitySpacious: 'Spacious',
|
|
10521
11148
|
columnsButton: 'Columns',
|
|
10522
11149
|
columnsTitle: 'Toggle columns',
|
|
10523
|
-
|
|
10524
|
-
columnsHideAll: 'Hide all'
|
|
11150
|
+
columnsToggleAll: 'Toggle all columns'
|
|
10525
11151
|
};
|
|
10526
11152
|
/**
|
|
10527
11153
|
* Dashforge TW `<DataGrid>` — virtualized data table.
|
|
@@ -10609,7 +11235,7 @@ const DEFAULT_LABELS = {
|
|
|
10609
11235
|
defaultValue: defaultColumnWidths,
|
|
10610
11236
|
onChange: onColumnWidthsChange
|
|
10611
11237
|
});
|
|
10612
|
-
const { startResize } = useColumnResize({
|
|
11238
|
+
const { startResize, nudge: nudgeColumnWidth } = useColumnResize({
|
|
10613
11239
|
widths: columnWidths,
|
|
10614
11240
|
onChange: setColumnWidths
|
|
10615
11241
|
});
|
|
@@ -10626,6 +11252,22 @@ const DEFAULT_LABELS = {
|
|
|
10626
11252
|
order: columnOrder,
|
|
10627
11253
|
onChange: setColumnOrder
|
|
10628
11254
|
});
|
|
11255
|
+
// Column reorder rides on native HTML5 drag-and-drop, whose
|
|
11256
|
+
// `dragstart` / `drop` events DO NOT fire for touch input. On a
|
|
11257
|
+
// coarse-pointer device the drag would be a dead affordance — so
|
|
11258
|
+
// we detect the primary pointer and gate reorder off there. Mouse
|
|
11259
|
+
// / trackpad / pen (fine pointer) keep the feature. A pointer-event
|
|
11260
|
+
// based reorder that also covers touch is tracked as a follow-up
|
|
11261
|
+
// in REFINEMENT-NOTES.md.
|
|
11262
|
+
const [coarsePointer, setCoarsePointer] = useState(false);
|
|
11263
|
+
useEffect(()=>{
|
|
11264
|
+
if (typeof window === 'undefined' || !window.matchMedia) return;
|
|
11265
|
+
const mq = window.matchMedia('(pointer: coarse)');
|
|
11266
|
+
setCoarsePointer(mq.matches);
|
|
11267
|
+
const onChange = (e)=>setCoarsePointer(e.matches);
|
|
11268
|
+
mq.addEventListener('change', onChange);
|
|
11269
|
+
return ()=>mq.removeEventListener('change', onChange);
|
|
11270
|
+
}, []);
|
|
10629
11271
|
// Apply the active order to the columns list. Cols not in the
|
|
10630
11272
|
// order array (e.g. newly-added columns at runtime) are appended
|
|
10631
11273
|
// in their original position from `cols`.
|
|
@@ -10780,8 +11422,7 @@ const DEFAULT_LABELS = {
|
|
|
10780
11422
|
labels: {
|
|
10781
11423
|
columnsButton: labels.columnsButton,
|
|
10782
11424
|
columnsTitle: labels.columnsTitle,
|
|
10783
|
-
|
|
10784
|
-
columnsHideAll: labels.columnsHideAll
|
|
11425
|
+
columnsToggleAll: labels.columnsToggleAll
|
|
10785
11426
|
}
|
|
10786
11427
|
})
|
|
10787
11428
|
]
|
|
@@ -10795,6 +11436,7 @@ const DEFAULT_LABELS = {
|
|
|
10795
11436
|
} : undefined,
|
|
10796
11437
|
children: jsxs("table", {
|
|
10797
11438
|
className: cn(v.table(), slotProps == null ? void 0 : (_slotProps_table = slotProps.table) == null ? void 0 : _slotProps_table.className),
|
|
11439
|
+
"aria-rowcount": effectiveTotalCount > 0 ? effectiveTotalCount + 1 : -1,
|
|
10798
11440
|
children: [
|
|
10799
11441
|
caption != null && jsx("caption", {
|
|
10800
11442
|
className: cn(!showCaption && 'sr-only', 'py-2 text-sm text-neutral-500'),
|
|
@@ -10803,6 +11445,7 @@ const DEFAULT_LABELS = {
|
|
|
10803
11445
|
jsx("thead", {
|
|
10804
11446
|
className: cn(stickyHeader ? v.thead() : 'bg-neutral-50', slotProps == null ? void 0 : (_slotProps_thead = slotProps.thead) == null ? void 0 : _slotProps_thead.className),
|
|
10805
11447
|
children: jsxs("tr", {
|
|
11448
|
+
"aria-rowindex": 1,
|
|
10806
11449
|
className: cn(v.headerRow(), slotProps == null ? void 0 : (_slotProps_headerRow = slotProps.headerRow) == null ? void 0 : _slotProps_headerRow.className),
|
|
10807
11450
|
children: [
|
|
10808
11451
|
showSelectionColumn && jsx("th", {
|
|
@@ -10832,7 +11475,8 @@ const DEFAULT_LABELS = {
|
|
|
10832
11475
|
effectiveWidth: resolveColumnWidth(col, columnWidths),
|
|
10833
11476
|
resizable: enableColumnResize && col.resizable !== false,
|
|
10834
11477
|
startResize: startResize,
|
|
10835
|
-
|
|
11478
|
+
nudgeColumnWidth: nudgeColumnWidth,
|
|
11479
|
+
reorderable: enableColumnReorder && col.reorderable !== false && !coarsePointer,
|
|
10836
11480
|
reorderHandlers: reorderHandlers,
|
|
10837
11481
|
labels: labels,
|
|
10838
11482
|
className: cn(v.headerCell(), col.sticky === 'left' && v.stickyLeftHeaderCell(), col.sticky === 'right' && v.stickyRightHeaderCell(), slotProps == null ? void 0 : (_slotProps_headerCell = slotProps.headerCell) == null ? void 0 : _slotProps_headerCell.className),
|
|
@@ -10861,7 +11505,11 @@ const DEFAULT_LABELS = {
|
|
|
10861
11505
|
}) : sortedRows.length === 0 ? renderEmptyState({
|
|
10862
11506
|
totalColumnCount,
|
|
10863
11507
|
emptyState,
|
|
10864
|
-
|
|
11508
|
+
// Distinguish a genuinely empty dataset from one
|
|
11509
|
+
// emptied by an active search / filter (works for
|
|
11510
|
+
// server-side mode too — a query that returns 0
|
|
11511
|
+
// rows still reads as "no matching results").
|
|
11512
|
+
fallback: rows.length > 0 && (debouncedSearchQuery.trim().length > 0 || filterModel.length > 0) ? labels.noResults : labels.noData,
|
|
10865
11513
|
cellClass: cn(v.cell(), v.emptyState(), slotProps == null ? void 0 : (_slotProps_emptyState = slotProps.emptyState) == null ? void 0 : _slotProps_emptyState.className)
|
|
10866
11514
|
}) : renderVirtualizedBody({
|
|
10867
11515
|
vWindow,
|
|
@@ -10929,7 +11577,8 @@ const DEFAULT_LABELS = {
|
|
|
10929
11577
|
}
|
|
10930
11578
|
// ───── Header cell ─────
|
|
10931
11579
|
function HeaderCell(props) {
|
|
10932
|
-
|
|
11580
|
+
var _col_minWidth, _col_maxWidth;
|
|
11581
|
+
const { col, sortModel, onSortClick, inferredType, filterModel, onFilterChange, effectiveWidth, resizable, startResize, nudgeColumnWidth, reorderable, reorderHandlers, labels, className, buttonClassName, disabled } = props;
|
|
10933
11582
|
const align = resolveAlign(col, inferredType);
|
|
10934
11583
|
const alignClass = align === 'right' ? 'text-right' : align === 'center' ? 'text-center' : 'text-left';
|
|
10935
11584
|
const sortEntry = sortModel.find((s)=>s.field === col.field);
|
|
@@ -10956,20 +11605,42 @@ function HeaderCell(props) {
|
|
|
10956
11605
|
onDrop: reorderHandlers.drop(col.field),
|
|
10957
11606
|
onDragEnd: reorderHandlers.dragEnd
|
|
10958
11607
|
} : {};
|
|
11608
|
+
// Resize bounds — shared by the pointer-drag and keyboard paths.
|
|
11609
|
+
const resizeMin = (_col_minWidth = col.minWidth) != null ? _col_minWidth : 40;
|
|
11610
|
+
const resizeMax = (_col_maxWidth = col.maxWidth) != null ? _col_maxWidth : 1200;
|
|
11611
|
+
// Keyboard nudge step: 16px, or 64px with Shift held (coarse).
|
|
11612
|
+
const measureWidth = (handle)=>{
|
|
11613
|
+
var _ref;
|
|
11614
|
+
var _handle_parentElement;
|
|
11615
|
+
return (_ref = effectiveWidth != null ? effectiveWidth : (_handle_parentElement = handle.parentElement) == null ? void 0 : _handle_parentElement.getBoundingClientRect().width) != null ? _ref : 120;
|
|
11616
|
+
};
|
|
10959
11617
|
const resizeHandle = resizable ? jsx("span", {
|
|
10960
11618
|
role: "separator",
|
|
10961
11619
|
"aria-orientation": "vertical",
|
|
10962
11620
|
"aria-label": `Resize ${typeof col.header === 'string' ? col.header : col.field}`,
|
|
11621
|
+
"aria-valuemin": resizeMin,
|
|
11622
|
+
"aria-valuemax": resizeMax,
|
|
11623
|
+
"aria-valuenow": effectiveWidth != null ? effectiveWidth : undefined,
|
|
11624
|
+
// Keyboard-operable: focusable + ArrowLeft/Right resize. WCAG 2.1
|
|
11625
|
+
// keyboard operability — the drag is no longer the only path.
|
|
11626
|
+
tabIndex: disabled ? -1 : 0,
|
|
11627
|
+
// D6 guard: the parent `<th>` is `draggable` when reorderable;
|
|
11628
|
+
// marking the handle non-draggable means grabbing the handle
|
|
11629
|
+
// starts a RESIZE, never a column-reorder drag.
|
|
11630
|
+
draggable: false,
|
|
10963
11631
|
onPointerDown: (e)=>{
|
|
10964
|
-
|
|
10965
|
-
|
|
10966
|
-
|
|
10967
|
-
|
|
10968
|
-
|
|
10969
|
-
|
|
10970
|
-
|
|
10971
|
-
|
|
10972
|
-
|
|
11632
|
+
const startWidth = measureWidth(e.currentTarget);
|
|
11633
|
+
startResize(col.field, startWidth, resizeMin, resizeMax)(e);
|
|
11634
|
+
},
|
|
11635
|
+
onKeyDown: (e)=>{
|
|
11636
|
+
if (disabled) return;
|
|
11637
|
+
if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight') return;
|
|
11638
|
+
e.preventDefault();
|
|
11639
|
+
const step = (e.shiftKey ? 64 : 16) * (e.key === 'ArrowLeft' ? -1 : 1);
|
|
11640
|
+
const current = measureWidth(e.currentTarget);
|
|
11641
|
+
nudgeColumnWidth(col.field, current, step, resizeMin, resizeMax);
|
|
11642
|
+
},
|
|
11643
|
+
className: cn('absolute top-0 bottom-0 right-0 w-1.5', 'cursor-col-resize select-none touch-none', 'hover:bg-primary-300 active:bg-primary-400', 'focus:outline-none focus-visible:bg-primary-400', 'transition-colors motion-reduce:transition-none')
|
|
10973
11644
|
}) : null;
|
|
10974
11645
|
const filterType = col.filterable ? resolveFilterType(col, inferredType) : null;
|
|
10975
11646
|
const filterTrigger = col.filterable && filterType ? jsx(ColumnFilterTrigger, {
|
|
@@ -11096,6 +11767,7 @@ function renderVirtualizedBody(args) {
|
|
|
11096
11767
|
const rowId = getRowId(row, absoluteIdx);
|
|
11097
11768
|
const isSelected = selection.isSelected(rowId);
|
|
11098
11769
|
return jsxs("tr", {
|
|
11770
|
+
"aria-rowindex": absoluteIdx + 2,
|
|
11099
11771
|
"data-selected": isSelected ? 'true' : 'false',
|
|
11100
11772
|
"aria-selected": rowSelection !== 'none' ? isSelected : undefined,
|
|
11101
11773
|
className: classes.row,
|
|
@@ -11309,4 +11981,4 @@ function SearchIcon(props) {
|
|
|
11309
11981
|
* Package version (synced with `package.json` at publish time).
|
|
11310
11982
|
*/ const VERSION = '0.7.0-beta';
|
|
11311
11983
|
|
|
11312
|
-
export { Accordion, AppShell, AspectRatio, Autocomplete, Box, Breadcrumbs, Button, Checkbox, ConfirmDialogProvider, Container, DataGrid, DateTimePicker, Dialog, Divider, Grid, LeftNav, NumberField, OTPField, Pagination, Popover, RadioGroup, RenderButton, RenderChip, RenderText, RenderTwoLine, RowActionsMenu, Skeleton, SnackbarProvider, Stack, Switch, Table, Tabs, TextField, Textarea, Tooltip, TopBar, Typography, VERSION, VisuallyHidden, accordionVariants, appShellVariants, autocompleteVariants, boxVariants, breadcrumbsVariants, buttonVariants, checkboxVariants, cn, confirmDialogVariants, containerVariants, dataGridVariants, dateTimePickerVariants, dialogVariants, dividerLineVariants, dividerVariants, getNestedValue, gridVariants, isoToInputValue, leftNavVariants, numberFieldVariants, otpFieldVariants, paginationVariants, popoverVariants, radioGroupVariants, skeletonVariants, snackbarVariants, stackVariants, switchVariants, tableVariants, tabsVariants, textFieldVariants, textareaVariants, tooltipVariants, topBarVariants, typographyVariants, useAccessState, useConfirm, useSnackbar };
|
|
11984
|
+
export { Accordion, AppShell, AspectRatio, Autocomplete, Box, Breadcrumbs, Button, Calendar, Checkbox, ConfirmDialogProvider, Container, DataGrid, DatePicker, DateTimePicker, Dialog, Divider, Grid, LeftNav, NumberField, OTPField, Pagination, Popover, RadioGroup, RenderButton, RenderChip, RenderText, RenderTwoLine, RowActionsMenu, Skeleton, SnackbarProvider, Stack, Switch, Table, Tabs, TextField, Textarea, Tooltip, TopBar, Typography, VERSION, VisuallyHidden, accordionVariants, appShellVariants, autocompleteVariants, boxVariants, breadcrumbsVariants, buttonVariants, calendarDayVariants, calendarVariants, checkboxVariants, cn, confirmDialogVariants, containerVariants, dataGridVariants, datePickerVariants, dateTimePickerVariants, dialogVariants, dividerLineVariants, dividerVariants, getNestedValue, gridVariants, isoToInputValue, leftNavVariants, numberFieldVariants, otpFieldVariants, paginationVariants, popoverVariants, radioGroupVariants, skeletonVariants, snackbarVariants, stackVariants, switchVariants, tableVariants, tabsVariants, textFieldVariants, textareaVariants, tooltipVariants, topBarVariants, typographyVariants, useAccessState, useConfirm, useSnackbar };
|