cursedbelt 5.28.0 → 5.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/react/components/ColorField.d.ts.map +1 -1
- package/dist/react/components/ColorField.js +2 -1
- package/dist/react/components/ColorField.js.map +1 -1
- package/dist/react/components/ComboboxField.d.ts.map +1 -1
- package/dist/react/components/ComboboxField.js +2 -1
- package/dist/react/components/ComboboxField.js.map +1 -1
- package/dist/react/components/DataTable/DataTable.d.ts +39 -2
- package/dist/react/components/DataTable/DataTable.d.ts.map +1 -1
- package/dist/react/components/DataTable/DataTable.js +92 -22
- package/dist/react/components/DataTable/DataTable.js.map +1 -1
- package/dist/react/components/DataTable/ViewOptionsMenu.d.ts.map +1 -1
- package/dist/react/components/DataTable/ViewOptionsMenu.js +20 -7
- package/dist/react/components/DataTable/ViewOptionsMenu.js.map +1 -1
- package/dist/react/components/DataTable/types.d.ts +11 -1
- package/dist/react/components/DataTable/types.d.ts.map +1 -1
- package/dist/react/components/DataTable/types.js +6 -0
- package/dist/react/components/DataTable/types.js.map +1 -1
- package/dist/react/components/DatePicker.d.ts.map +1 -1
- package/dist/react/components/DatePicker.js +2 -1
- package/dist/react/components/DatePicker.js.map +1 -1
- package/dist/react/components/DateTimeField.d.ts.map +1 -1
- package/dist/react/components/DateTimeField.js +2 -1
- package/dist/react/components/DateTimeField.js.map +1 -1
- package/dist/react/components/ExpandableRow.d.ts +56 -0
- package/dist/react/components/ExpandableRow.d.ts.map +1 -0
- package/dist/react/components/ExpandableRow.js +54 -0
- package/dist/react/components/ExpandableRow.js.map +1 -0
- package/dist/react/components/Field.d.ts +20 -1
- package/dist/react/components/Field.d.ts.map +1 -1
- package/dist/react/components/Field.js +24 -11
- package/dist/react/components/Field.js.map +1 -1
- package/dist/react/components/FieldInput.d.ts +15 -0
- package/dist/react/components/FieldInput.d.ts.map +1 -1
- package/dist/react/components/FieldInput.js +20 -0
- package/dist/react/components/FieldInput.js.map +1 -1
- package/dist/react/components/FileDropZone.d.ts +23 -1
- package/dist/react/components/FileDropZone.d.ts.map +1 -1
- package/dist/react/components/FileDropZone.js +56 -8
- package/dist/react/components/FileDropZone.js.map +1 -1
- package/dist/react/components/FormPreviewLayout.d.ts +49 -0
- package/dist/react/components/FormPreviewLayout.d.ts.map +1 -0
- package/dist/react/components/FormPreviewLayout.js +36 -0
- package/dist/react/components/FormPreviewLayout.js.map +1 -0
- package/dist/react/components/Input.d.ts.map +1 -1
- package/dist/react/components/Input.js +3 -1
- package/dist/react/components/Input.js.map +1 -1
- package/dist/react/components/KeyBadge.d.ts +34 -0
- package/dist/react/components/KeyBadge.d.ts.map +1 -0
- package/dist/react/components/KeyBadge.js +24 -0
- package/dist/react/components/KeyBadge.js.map +1 -0
- package/dist/react/components/Modal.d.ts.map +1 -1
- package/dist/react/components/Modal.js +35 -9
- package/dist/react/components/Modal.js.map +1 -1
- package/dist/react/components/NativeSelect.d.ts.map +1 -1
- package/dist/react/components/NativeSelect.js +2 -1
- package/dist/react/components/NativeSelect.js.map +1 -1
- package/dist/react/components/RenderField.d.ts.map +1 -1
- package/dist/react/components/RenderField.js +2 -2
- package/dist/react/components/RenderField.js.map +1 -1
- package/dist/react/components/RevealAction.d.ts +33 -0
- package/dist/react/components/RevealAction.d.ts.map +1 -0
- package/dist/react/components/RevealAction.js +45 -0
- package/dist/react/components/RevealAction.js.map +1 -0
- package/dist/react/components/SecretInput.d.ts.map +1 -1
- package/dist/react/components/SecretInput.js +2 -1
- package/dist/react/components/SecretInput.js.map +1 -1
- package/dist/react/components/SelectField.d.ts.map +1 -1
- package/dist/react/components/SelectField.js +2 -1
- package/dist/react/components/SelectField.js.map +1 -1
- package/dist/react/components/SettingsStrip.d.ts +22 -0
- package/dist/react/components/SettingsStrip.d.ts.map +1 -0
- package/dist/react/components/SettingsStrip.js +18 -0
- package/dist/react/components/SettingsStrip.js.map +1 -0
- package/dist/react/components/formKit.css +555 -0
- package/dist/react/components/formKit.d.ts +12 -0
- package/dist/react/components/formKit.d.ts.map +1 -0
- package/dist/react/components/formKit.js +14 -0
- package/dist/react/components/formKit.js.map +1 -0
- package/dist/react/filter-rail/RailSection.d.ts +2 -2
- package/dist/react/filter-rail/RailSection.d.ts.map +1 -1
- package/dist/react/filter-rail/RailSection.js +6 -61
- package/dist/react/filter-rail/RailSection.js.map +1 -1
- package/dist/react/index.d.ts +8 -3
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +8 -3
- package/dist/react/index.js.map +1 -1
- package/dist/react/lib/field.d.ts +43 -0
- package/dist/react/lib/field.d.ts.map +1 -1
- package/dist/react/lib/field.js +68 -0
- package/dist/react/lib/field.js.map +1 -1
- package/dist/react/media/PlayerStrip.d.ts +9 -1
- package/dist/react/media/PlayerStrip.d.ts.map +1 -1
- package/dist/react/media/PlayerStrip.js +29 -2
- package/dist/react/media/PlayerStrip.js.map +1 -1
- package/dist/react/media-gallery/rememberedPreference.d.ts +2 -0
- package/dist/react/media-gallery/rememberedPreference.d.ts.map +1 -0
- package/dist/react/media-gallery/rememberedPreference.js +75 -0
- package/dist/react/media-gallery/rememberedPreference.js.map +1 -0
- package/dist/styles-areas/analytics.css +2 -2
- package/dist/styles-areas/core.css +34 -28
- package/dist/styles-areas/data-table.css +2 -2
- package/dist/styles-areas/fields.css +3 -3
- package/dist/styles-areas/layout-engine.css +2 -2
- package/dist/styles-areas/nav.css +3 -3
- package/dist/styles-areas/wizard.css +2 -2
- package/dist/styles-areas/workbench.css +2 -2
- package/dist/styles-utilities.css +111 -9
- package/package.json +14 -1
- package/scripts/styleAreas.ts +2 -0
- package/src/react/components/ColorField.tsx +3 -1
- package/src/react/components/ComboboxField.tsx +3 -1
- package/src/react/components/DataTable/DataTable.spec.tsx +18 -0
- package/src/react/components/DataTable/DataTable.tsx +136 -22
- package/src/react/components/DataTable/ViewOptionsMenu.tsx +24 -9
- package/src/react/components/DataTable/selection.spec.tsx +57 -1
- package/src/react/components/DataTable/types.ts +12 -1
- package/src/react/components/DataTable/virtualize.spec.tsx +20 -0
- package/src/react/components/DatePicker.tsx +3 -1
- package/src/react/components/DateTimeField.tsx +3 -1
- package/src/react/components/ExpandableRow.tsx +146 -0
- package/src/react/components/Field.tsx +89 -13
- package/src/react/components/Field.width.spec.tsx +162 -0
- package/src/react/components/FieldInput.tsx +30 -0
- package/src/react/components/FileDropZone.spec.tsx +65 -0
- package/src/react/components/FileDropZone.tsx +83 -5
- package/src/react/components/FormPreviewLayout.tsx +94 -0
- package/src/react/components/Input.tsx +4 -1
- package/src/react/components/KeyBadge.tsx +52 -0
- package/src/react/components/Modal.tsx +58 -14
- package/src/react/components/NativeSelect.tsx +3 -1
- package/src/react/components/RenderField.tsx +2 -1
- package/src/react/components/RevealAction.tsx +83 -0
- package/src/react/components/SecretInput.tsx +3 -1
- package/src/react/components/SelectField.tsx +3 -1
- package/src/react/components/SettingsStrip.tsx +37 -0
- package/src/react/components/formKit.css +555 -0
- package/src/react/components/formKit.spec.tsx +138 -0
- package/src/react/components/formKit.ts +19 -0
- package/src/react/filter-rail/RailSection.tsx +6 -65
- package/src/react/index.ts +20 -2
- package/src/react/lib/field.ts +93 -0
- package/src/react/media/PlayerStrip.tsx +65 -20
- package/src/react/media/playerStrip.spec.tsx +23 -0
- package/src/react/media-gallery/rememberedPreference.ts +79 -0
- package/src/styles-areas/analytics.css +2 -2
- package/src/styles-areas/core.css +34 -28
- package/src/styles-areas/data-table.css +2 -2
- package/src/styles-areas/fields.css +3 -3
- package/src/styles-areas/layout-engine.css +2 -2
- package/src/styles-areas/nav.css +3 -3
- package/src/styles-areas/wizard.css +2 -2
- package/src/styles-areas/workbench.css +2 -2
- package/src/styles-utilities.css +111 -9
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type ColumnDef,
|
|
3
3
|
type ColumnFiltersState,
|
|
4
|
-
flexRender,
|
|
5
4
|
getCoreRowModel,
|
|
6
5
|
getFilteredRowModel,
|
|
7
6
|
getPaginationRowModel,
|
|
@@ -26,6 +25,7 @@ import type {
|
|
|
26
25
|
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
27
26
|
import { type ComponentType, Fragment, type Ref, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
|
|
28
27
|
import { useScrollEdgeFade } from '../../hooks/useScrollEdgeFade.js';
|
|
28
|
+
import { useRememberedPreference } from '../../media-gallery/rememberedPreference.js';
|
|
29
29
|
import { cn } from '../../lib/cn.js';
|
|
30
30
|
import { TAP_TARGET_HEIGHT } from '../../lib/tapTarget.js';
|
|
31
31
|
import { CardTapTarget } from '../CardTapTarget.js';
|
|
@@ -54,7 +54,7 @@ import type {
|
|
|
54
54
|
DataTableServerWindow,
|
|
55
55
|
TableViewMode,
|
|
56
56
|
} from './types.js';
|
|
57
|
-
import { resolveColumnAlign } from './types.js';
|
|
57
|
+
import { resolveColumnAlign, SELECT_COLUMN_ID } from './types.js';
|
|
58
58
|
import { lazyPart } from './lazyPart.js';
|
|
59
59
|
import type { TablePaginationProps } from './Pagination.js';
|
|
60
60
|
import type { TableToolbarProps } from './Toolbar.js';
|
|
@@ -109,12 +109,31 @@ function PaginationPlaceholder() {
|
|
|
109
109
|
);
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Render a header or cell template by CALLING it — never `flexRender`.
|
|
114
|
+
*
|
|
115
|
+
* 🔴 `flexRender` mounts a function template as a component (`<Template {...ctx} />`), so its
|
|
116
|
+
* identity IS the element type. Our templates are closures rebuilt whenever `columns` changes
|
|
117
|
+
* identity, and a consumer's `columns` memo commonly depends on something that changes often
|
|
118
|
+
* (music's depended on its player: play, pause, every track). Each rebuild was a new component
|
|
119
|
+
* type for every visible cell — React unmounted and remounted the whole body, every `<img>` was
|
|
120
|
+
* recreated, and on an iPhone the list's thumbnails flickered for as long as a song played
|
|
121
|
+
* (2026-09-29). Called as a function, a template returns `<CellRenderer>` / `<SortHeader>`,
|
|
122
|
+
* whose types never change, so a column rebuild is an ordinary re-render. Every template this
|
|
123
|
+
* file builds is hook-free — the hooks live in the components it returns — which is what
|
|
124
|
+
* makes the call sound; keep it so. `DataTable.spec.tsx` → "a columns rebuild keeps the cells".
|
|
125
|
+
*/
|
|
126
|
+
function renderTemplate<TContext>(template: ReactNode | ((context: TContext) => ReactNode) | undefined, context: TContext): ReactNode {
|
|
127
|
+
return typeof template === 'function' ? template(context) : (template ?? null);
|
|
128
|
+
}
|
|
129
|
+
|
|
112
130
|
// TanStack's header is a render fn (we build a sortable button), so its column has no
|
|
113
131
|
// string header for the ViewOptionsMenu to label a toggle with. Carry the label on
|
|
114
132
|
// `meta` instead — augmenting the base interface so it's typed everywhere.
|
|
115
133
|
declare module '@tanstack/react-table' {
|
|
116
134
|
interface ColumnMeta<TData extends RowData, TValue> {
|
|
117
135
|
label?: string;
|
|
136
|
+
menuLabel?: string;
|
|
118
137
|
align?: 'left' | 'right';
|
|
119
138
|
/** ColDef.width verbatim (px number or CSS string, e.g. `'15%'`) — TanStack's
|
|
120
139
|
* own `size` is number-only, so proportional tracks ride meta instead. */
|
|
@@ -187,6 +206,8 @@ export interface DataTableControls {
|
|
|
187
206
|
labelOf: (id: string) => string;
|
|
188
207
|
viewMode?: TableViewMode;
|
|
189
208
|
setViewMode?: (mode: TableViewMode) => void;
|
|
209
|
+
/** The checkbox column, when the table has one — so the Columns menu can hide it too. */
|
|
210
|
+
selectionColumn?: { visible: boolean; setVisible: (visible: boolean) => void };
|
|
190
211
|
}
|
|
191
212
|
|
|
192
213
|
export interface DataTableProps<TRow extends Record<string, unknown>> {
|
|
@@ -482,6 +503,20 @@ export interface DataTableProps<TRow extends Record<string, unknown>> {
|
|
|
482
503
|
*/
|
|
483
504
|
searchable?: boolean;
|
|
484
505
|
searchPlaceholder?: string;
|
|
506
|
+
/**
|
|
507
|
+
* Tune the DEFAULT toolbar without composing one — which of its controls to draw, and any
|
|
508
|
+
* extra actions. It stays lazy (a composed `toolbar={(t) => <TableToolbar …/>}` pulls the
|
|
509
|
+
* toolbar into the caller's first chunk). `searchable={false}` still removes the search box
|
|
510
|
+
* but, with this set, not the rest of the line.
|
|
511
|
+
*
|
|
512
|
+
* 🔴 Why (owner, 2026-09-30, music): *"Use the cb table that comes with filtering, checkboxes,
|
|
513
|
+
* and sorting out of the box."* The track list opted out of the whole toolbar because it has
|
|
514
|
+
* its own server-side search box, and so lost the filter builder and the Columns menu with it.
|
|
515
|
+
*/
|
|
516
|
+
toolbarOptions?: Pick<
|
|
517
|
+
TableToolbarProps<TRow>,
|
|
518
|
+
'enableColumnVisibility' | 'enableFilterBuilder' | 'enableSortStack' | 'extraActions'
|
|
519
|
+
>;
|
|
485
520
|
/**
|
|
486
521
|
* Multi-column sort: each header click puts that column at the FRONT of the
|
|
487
522
|
* stack and keeps the previous keys behind it as tie-breakers, so the owner's
|
|
@@ -552,6 +587,25 @@ export interface DataTableProps<TRow extends Record<string, unknown>> {
|
|
|
552
587
|
* owns the box. The row boxes are unchanged (`selectedIds` / `onSelectionChange`).
|
|
553
588
|
*/
|
|
554
589
|
selectAll?: DataTableSelectAll;
|
|
590
|
+
/**
|
|
591
|
+
* Which columns are shown, CONTROLLED — `{ [ColDef.key]: false }` hides one, and
|
|
592
|
+
* `'__select'` is the checkbox column's key. Omit and the table keeps it
|
|
593
|
+
* internally, seeded from `ColDef.hidden`, exactly as before.
|
|
594
|
+
*
|
|
595
|
+
* 🔴 Why a caller would own it (owner, 2026-09-30, music: *"Allow hiding any column, like
|
|
596
|
+
* the checkbox column"*): a column hidden through the Columns menu that comes back on the
|
|
597
|
+
* next reload was never hidden, it was dismissed. Only the app knows where its per-account
|
|
598
|
+
* preferences live, so the app persists this and hands it back.
|
|
599
|
+
*/
|
|
600
|
+
columnVisibility?: VisibilityState;
|
|
601
|
+
onColumnVisibilityChange?: (next: VisibilityState) => void;
|
|
602
|
+
/**
|
|
603
|
+
* Remember the Columns menu's choices under this key — `localStorage`, announced on the
|
|
604
|
+
* preference bus (`onPreferenceWrite`) and re-read on `seedPreferences`, exactly like a
|
|
605
|
+
* `RailSection`'s `persistKey`, so an app that already carries its rail to the server carries
|
|
606
|
+
* its columns with no code of its own. A controlled {@link columnVisibility} wins over it.
|
|
607
|
+
*/
|
|
608
|
+
columnsPersistKey?: string;
|
|
555
609
|
/**
|
|
556
610
|
* Attributes and handlers for one body ROW, merged onto it after the table's own —
|
|
557
611
|
* `className` joins, `style` spreads, and a handler the table also sets runs AFTER the
|
|
@@ -586,6 +640,19 @@ export interface DataTableProps<TRow extends Record<string, unknown>> {
|
|
|
586
640
|
selectionSummary?: (selectedRows: TRow[]) => ReactNode;
|
|
587
641
|
}
|
|
588
642
|
|
|
643
|
+
/** A remembered column-visibility value, or `null` for anything that is not one — a stored
|
|
644
|
+
* value from an older shape must fall back to the defaults, never throw a table off the page. */
|
|
645
|
+
function parseVisibility(stored: string | null): VisibilityState | null {
|
|
646
|
+
if (!stored) return null;
|
|
647
|
+
try {
|
|
648
|
+
const value: unknown = JSON.parse(stored);
|
|
649
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) return null;
|
|
650
|
+
return Object.fromEntries(Object.entries(value).filter(([, shown]) => typeof shown === 'boolean')) as VisibilityState;
|
|
651
|
+
} catch {
|
|
652
|
+
return null;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
589
656
|
/** Build an OnChangeFn that updates local state AND notifies an optional listener. */
|
|
590
657
|
function chainUpdater<T>(
|
|
591
658
|
set: Dispatch<SetStateAction<T>>,
|
|
@@ -850,6 +917,7 @@ export function DataTable<TRow extends Record<string, unknown>>({
|
|
|
850
917
|
cardMinWidth,
|
|
851
918
|
searchable = true,
|
|
852
919
|
searchPlaceholder,
|
|
920
|
+
toolbarOptions,
|
|
853
921
|
multiSort = true,
|
|
854
922
|
filterQuery: filterQueryProp,
|
|
855
923
|
onFilterQueryChange,
|
|
@@ -863,6 +931,9 @@ export function DataTable<TRow extends Record<string, unknown>>({
|
|
|
863
931
|
selectionSummary,
|
|
864
932
|
selectAll,
|
|
865
933
|
getRowProps,
|
|
934
|
+
columnVisibility: columnVisibilityProp,
|
|
935
|
+
onColumnVisibilityChange,
|
|
936
|
+
columnsPersistKey,
|
|
866
937
|
}: DataTableProps<TRow>) {
|
|
867
938
|
// Vertical cell padding for the wide grid tier — the one lever the density knob
|
|
868
939
|
// pulls (headers + body cells stay aligned by sharing it).
|
|
@@ -875,9 +946,30 @@ export function DataTable<TRow extends Record<string, unknown>>({
|
|
|
875
946
|
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
|
876
947
|
const [globalFilter, setGlobalFilter] = useState('');
|
|
877
948
|
|
|
878
|
-
const [
|
|
949
|
+
const [ownColumnVisibility, setOwnColumnVisibility] = useState<VisibilityState>(() =>
|
|
879
950
|
Object.fromEntries(columns.filter((c) => c.hidden).map((c) => [c.key, false])),
|
|
880
951
|
);
|
|
952
|
+
const [rememberedColumns, setRememberedColumns] = useRememberedPreference(columnsPersistKey);
|
|
953
|
+
const remembered = useMemo(() => parseVisibility(rememberedColumns), [rememberedColumns]);
|
|
954
|
+
const columnVisibility =
|
|
955
|
+
columnVisibilityProp ?? (remembered ? { ...ownColumnVisibility, ...remembered } : ownColumnVisibility);
|
|
956
|
+
const columnVisibilityRef = useRef(columnVisibility);
|
|
957
|
+
columnVisibilityRef.current = columnVisibility;
|
|
958
|
+
/** Every visibility write — TanStack's own, the Columns menu's, the checkbox column's. Computed
|
|
959
|
+
* outside a state updater for the same reason as `applySorting`: under a controlled value
|
|
960
|
+
* there is no state to update, and a notify inside an updater fires twice in StrictMode. */
|
|
961
|
+
const updateColumnVisibility = useCallback<OnChangeFn<VisibilityState>>(
|
|
962
|
+
(updater) => {
|
|
963
|
+
const next = typeof updater === 'function' ? updater(columnVisibilityRef.current) : updater;
|
|
964
|
+
columnVisibilityRef.current = next;
|
|
965
|
+
if (columnVisibilityProp === undefined) {
|
|
966
|
+
setOwnColumnVisibility(next);
|
|
967
|
+
if (columnsPersistKey) setRememberedColumns(JSON.stringify(next));
|
|
968
|
+
}
|
|
969
|
+
onColumnVisibilityChange?.(next);
|
|
970
|
+
},
|
|
971
|
+
[columnVisibilityProp, columnsPersistKey, onColumnVisibilityChange, setRememberedColumns],
|
|
972
|
+
);
|
|
881
973
|
const [pagination, setPagination] = useState<PaginationState>({ pageIndex: 0, pageSize });
|
|
882
974
|
// `null` = every column, and it STAYS null as columns come and go — see SearchBox.
|
|
883
975
|
const [searchScope, setSearchScope] = useState<string[] | null>(null);
|
|
@@ -936,10 +1028,12 @@ export function DataTable<TRow extends Record<string, unknown>>({
|
|
|
936
1028
|
*/
|
|
937
1029
|
const filteredData = useMemo(
|
|
938
1030
|
() =>
|
|
939
|
-
|
|
1031
|
+
// A `serverWindow` holds a hundred rows of thousands: narrowing THEM would answer a
|
|
1032
|
+
// question about the loaded page, not the list. The server applies it — the table reports.
|
|
1033
|
+
serverSide || serverWindow
|
|
940
1034
|
? data
|
|
941
1035
|
: data.filter((row) => evaluateFilterQuery(row, filterQuery, (r, key) => readPath(r, key))),
|
|
942
|
-
[data, filterQuery, serverSide],
|
|
1036
|
+
[data, filterQuery, serverSide, serverWindow],
|
|
943
1037
|
);
|
|
944
1038
|
|
|
945
1039
|
/**
|
|
@@ -981,6 +1075,7 @@ export function DataTable<TRow extends Record<string, unknown>>({
|
|
|
981
1075
|
// preference — `resolveColumnAlign` derives it; an explicit `align` overrides.
|
|
982
1076
|
meta: {
|
|
983
1077
|
label: col.label,
|
|
1078
|
+
menuLabel: col.menuLabel,
|
|
984
1079
|
align: resolveColumnAlign(col),
|
|
985
1080
|
width: col.width,
|
|
986
1081
|
minWidth: col.minWidth,
|
|
@@ -1042,7 +1137,9 @@ export function DataTable<TRow extends Record<string, unknown>>({
|
|
|
1042
1137
|
setFilterQuery,
|
|
1043
1138
|
// `alwaysVisible` columns are exempt from HIDING, not from filtering — the
|
|
1044
1139
|
// builder's universe is every column that carries data.
|
|
1045
|
-
filterColumns: liveColumns
|
|
1140
|
+
filterColumns: liveColumns
|
|
1141
|
+
.filter((col) => col.filterable ?? true)
|
|
1142
|
+
.map((col) => ({ key: col.key, label: col.label })),
|
|
1046
1143
|
searchScope,
|
|
1047
1144
|
setSearchScope,
|
|
1048
1145
|
searchColumns: liveColumns
|
|
@@ -1056,6 +1153,14 @@ export function DataTable<TRow extends Record<string, unknown>>({
|
|
|
1056
1153
|
labelOf,
|
|
1057
1154
|
viewMode,
|
|
1058
1155
|
setViewMode: onViewModeChange,
|
|
1156
|
+
selectionColumn:
|
|
1157
|
+
selectedIds !== undefined
|
|
1158
|
+
? {
|
|
1159
|
+
visible: columnVisibility[SELECT_COLUMN_ID] !== false,
|
|
1160
|
+
setVisible: (visible) =>
|
|
1161
|
+
updateColumnVisibility((prev) => ({ ...prev, [SELECT_COLUMN_ID]: visible })),
|
|
1162
|
+
}
|
|
1163
|
+
: undefined,
|
|
1059
1164
|
}),
|
|
1060
1165
|
[
|
|
1061
1166
|
applySorting,
|
|
@@ -1069,6 +1174,9 @@ export function DataTable<TRow extends Record<string, unknown>>({
|
|
|
1069
1174
|
setFilterQuery,
|
|
1070
1175
|
sorting,
|
|
1071
1176
|
viewMode,
|
|
1177
|
+
selectedIds,
|
|
1178
|
+
columnVisibility,
|
|
1179
|
+
updateColumnVisibility,
|
|
1072
1180
|
],
|
|
1073
1181
|
);
|
|
1074
1182
|
|
|
@@ -1095,6 +1203,10 @@ export function DataTable<TRow extends Record<string, unknown>>({
|
|
|
1095
1203
|
const paginationHidden = hidePagination || virtualEnabled;
|
|
1096
1204
|
|
|
1097
1205
|
const selectionEnabled = selectedIds !== undefined;
|
|
1206
|
+
/** The checkbox column is ON and not hidden through the Columns menu. Hiding it hides the
|
|
1207
|
+
* boxes, never the selection: a row click that selects still does, and the caller's
|
|
1208
|
+
* selection bar still counts it. */
|
|
1209
|
+
const selectionShown = selectionEnabled && columnVisibility[SELECT_COLUMN_ID] !== false;
|
|
1098
1210
|
/**
|
|
1099
1211
|
* Without `getRowId`, TanStack ids a row by its index in the array it was
|
|
1100
1212
|
* handed — which is `filteredData`, so the builder narrowing the rows would
|
|
@@ -1128,7 +1240,7 @@ export function DataTable<TRow extends Record<string, unknown>>({
|
|
|
1128
1240
|
const text = typeof next === 'string' ? next : ((next as TableSearchState | undefined)?.text ?? '');
|
|
1129
1241
|
setSearchText(text);
|
|
1130
1242
|
},
|
|
1131
|
-
onColumnVisibilityChange:
|
|
1243
|
+
onColumnVisibilityChange: updateColumnVisibility,
|
|
1132
1244
|
onPaginationChange: chainUpdater(setPagination, onPaginationChange),
|
|
1133
1245
|
// 🔴 Not `'includesString'` any more. That built-in is `String(value)`, and
|
|
1134
1246
|
// `String(new Date())` / `String(['rock','80s'])` are not the text the
|
|
@@ -1462,15 +1574,17 @@ export function DataTable<TRow extends Record<string, unknown>>({
|
|
|
1462
1574
|
// passes its own gets exactly that, and `TableToolbar` reads the same
|
|
1463
1575
|
// controls off `table.options.meta`, so composing a toolbar is never
|
|
1464
1576
|
// the thing that costs you the search box.
|
|
1465
|
-
(searchable ? (
|
|
1577
|
+
(searchable || toolbarOptions ? (
|
|
1466
1578
|
<ToolbarPart
|
|
1467
1579
|
table={table}
|
|
1468
1580
|
searchPlaceholder={searchPlaceholder}
|
|
1581
|
+
enableSearch={searchable}
|
|
1582
|
+
{...toolbarOptions}
|
|
1469
1583
|
fallback={<ToolbarPlaceholder />}
|
|
1470
1584
|
/>
|
|
1471
1585
|
) : null));
|
|
1472
1586
|
const visibleColumnCount =
|
|
1473
|
-
table.getVisibleLeafColumns().length + (hasActions ? 1 : 0) + (
|
|
1587
|
+
table.getVisibleLeafColumns().length + (hasActions ? 1 : 0) + (selectionShown ? 1 : 0);
|
|
1474
1588
|
// Cards track the SAME live visibility state as the grid (ViewOptionsMenu toggles
|
|
1475
1589
|
// apply to both) — not just the ColDefs' initial `hidden` flags.
|
|
1476
1590
|
const cardColumns = liveColumns.filter((c) => table.getColumn(c.key)?.getIsVisible() ?? true);
|
|
@@ -1485,7 +1599,7 @@ export function DataTable<TRow extends Record<string, unknown>>({
|
|
|
1485
1599
|
const tableMinWidth = fixedLayout
|
|
1486
1600
|
? Math.max(
|
|
1487
1601
|
minTableWidth ?? 0,
|
|
1488
|
-
declaredTrackFloor(cardColumns, hasActions) + (
|
|
1602
|
+
declaredTrackFloor(cardColumns, hasActions) + (selectionShown ? SELECT_TRACK_PX : 0),
|
|
1489
1603
|
)
|
|
1490
1604
|
: minTableWidth;
|
|
1491
1605
|
|
|
@@ -1536,7 +1650,7 @@ export function DataTable<TRow extends Record<string, unknown>>({
|
|
|
1536
1650
|
ladder inside it would only fight the container query. ── */}
|
|
1537
1651
|
{/* Select-all for the tiers with no header row to carry it: the phone
|
|
1538
1652
|
cards, and the card view at every width. */}
|
|
1539
|
-
{
|
|
1653
|
+
{selectionShown && (viewMode === 'card' || mobileCards) ? (
|
|
1540
1654
|
<div
|
|
1541
1655
|
data-cb-table-select-all=''
|
|
1542
1656
|
className={cn('flex items-center px-2 py-1', viewMode !== 'card' && '@xl:hidden')}
|
|
@@ -1552,7 +1666,7 @@ export function DataTable<TRow extends Record<string, unknown>>({
|
|
|
1552
1666
|
onRowClick={onRowClick}
|
|
1553
1667
|
dragPropsFor={getRowDrag ? dragPropsFor : undefined}
|
|
1554
1668
|
selectFor={
|
|
1555
|
-
|
|
1669
|
+
selectionShown
|
|
1556
1670
|
? (_row, index) => {
|
|
1557
1671
|
const tsRow = rows[index];
|
|
1558
1672
|
return tsRow ? rowCheckbox(tsRow) : null;
|
|
@@ -1575,7 +1689,7 @@ export function DataTable<TRow extends Record<string, unknown>>({
|
|
|
1575
1689
|
className={rowClassName?.(row.original)}
|
|
1576
1690
|
onRowClick={onRowClick}
|
|
1577
1691
|
dragProps={mergeRowProps(dragPropsFor(row.original), getRowProps?.(row.original, windowStart + row.index))}
|
|
1578
|
-
select={
|
|
1692
|
+
select={selectionShown ? rowCheckbox(row) : undefined}
|
|
1579
1693
|
actions={
|
|
1580
1694
|
hasActions ? (
|
|
1581
1695
|
<RowActions
|
|
@@ -1621,7 +1735,7 @@ export function DataTable<TRow extends Record<string, unknown>>({
|
|
|
1621
1735
|
// absent (the header) and every preceding lane drifts ~32px off
|
|
1622
1736
|
// its label — the v4 judge's tablet misalignment finding.
|
|
1623
1737
|
const template = [
|
|
1624
|
-
...(
|
|
1738
|
+
...(selectionShown ? ['44px'] : []),
|
|
1625
1739
|
'minmax(0,1fr)',
|
|
1626
1740
|
...rest.map(trackOf),
|
|
1627
1741
|
...(hasActions ? ['32px'] : []),
|
|
@@ -1636,7 +1750,7 @@ export function DataTable<TRow extends Record<string, unknown>>({
|
|
|
1636
1750
|
className='grid items-center gap-3 border-b border-border px-4 py-2 text-caption font-bold uppercase tracking-wider text-muted-foreground'
|
|
1637
1751
|
style={{ gridTemplateColumns: template }}
|
|
1638
1752
|
>
|
|
1639
|
-
{
|
|
1753
|
+
{selectionShown ? <span className='flex justify-center'>{headerCheckbox()}</span> : null}
|
|
1640
1754
|
<span>{title?.label ?? ''}</span>
|
|
1641
1755
|
{rest.map((col) => (
|
|
1642
1756
|
<span key={col.key} className={cn(resolveColumnAlign(col) === 'right' && 'text-right')}>
|
|
@@ -1665,7 +1779,7 @@ export function DataTable<TRow extends Record<string, unknown>>({
|
|
|
1665
1779
|
getRowProps?.(row.original, windowStart + row.index),
|
|
1666
1780
|
)}
|
|
1667
1781
|
>
|
|
1668
|
-
{
|
|
1782
|
+
{selectionShown ? (
|
|
1669
1783
|
<div {...selectCellProps(row)} className='-my-3 flex self-stretch items-center justify-center'>
|
|
1670
1784
|
{rowCheckbox(row)}
|
|
1671
1785
|
</div>
|
|
@@ -1781,7 +1895,7 @@ export function DataTable<TRow extends Record<string, unknown>>({
|
|
|
1781
1895
|
// next header slides over the frozen one. It is opaque
|
|
1782
1896
|
// for the same reason the body cells are.
|
|
1783
1897
|
frozen &&
|
|
1784
|
-
(
|
|
1898
|
+
(selectionShown
|
|
1785
1899
|
? 'sticky left-11 bg-card shadow-[1px_0_0_0_var(--color-border)]'
|
|
1786
1900
|
: 'sticky left-0 bg-card shadow-[1px_0_0_0_var(--color-border)]'),
|
|
1787
1901
|
frozen && (stickyHeader ? 'z-30' : 'z-20'),
|
|
@@ -1790,7 +1904,7 @@ export function DataTable<TRow extends Record<string, unknown>>({
|
|
|
1790
1904
|
>
|
|
1791
1905
|
{h.isPlaceholder
|
|
1792
1906
|
? null
|
|
1793
|
-
:
|
|
1907
|
+
: renderTemplate(h.column.columnDef.header, h.getContext())}
|
|
1794
1908
|
</th>
|
|
1795
1909
|
);
|
|
1796
1910
|
};
|
|
@@ -1798,7 +1912,7 @@ export function DataTable<TRow extends Record<string, unknown>>({
|
|
|
1798
1912
|
return (
|
|
1799
1913
|
<>
|
|
1800
1914
|
{hg.headers.filter(isLeading).map((h) => renderHeader(h, -1))}
|
|
1801
|
-
{
|
|
1915
|
+
{selectionShown ? (
|
|
1802
1916
|
<th
|
|
1803
1917
|
scope='col'
|
|
1804
1918
|
className={cn(
|
|
@@ -1929,7 +2043,7 @@ export function DataTable<TRow extends Record<string, unknown>>({
|
|
|
1929
2043
|
'px-4 align-middle',
|
|
1930
2044
|
cellPadY,
|
|
1931
2045
|
frozen &&
|
|
1932
|
-
(
|
|
2046
|
+
(selectionShown
|
|
1933
2047
|
? 'sticky left-11 z-10 bg-[var(--cb-frozen-bg)] shadow-[1px_0_0_0_var(--color-border)]'
|
|
1934
2048
|
: 'sticky left-0 z-10 bg-[var(--cb-frozen-bg)] shadow-[1px_0_0_0_var(--color-border)]'),
|
|
1935
2049
|
// A rigid track cannot widen for its content, so an
|
|
@@ -1945,14 +2059,14 @@ export function DataTable<TRow extends Record<string, unknown>>({
|
|
|
1945
2059
|
HIDE_BELOW_CLASS[cell.column.columnDef.meta.hideBelow],
|
|
1946
2060
|
)}
|
|
1947
2061
|
>
|
|
1948
|
-
{
|
|
2062
|
+
{renderTemplate(cell.column.columnDef.cell, cell.getContext())}
|
|
1949
2063
|
</td>
|
|
1950
2064
|
);
|
|
1951
2065
|
};
|
|
1952
2066
|
return (
|
|
1953
2067
|
<>
|
|
1954
2068
|
{cells.filter(isLeading).map((cell) => renderCell(cell, -1))}
|
|
1955
|
-
{
|
|
2069
|
+
{selectionShown ? (
|
|
1956
2070
|
<td
|
|
1957
2071
|
{...selectCellProps(row)}
|
|
1958
2072
|
style={
|
|
@@ -4,6 +4,7 @@ import { useState } from 'react';
|
|
|
4
4
|
import { Button } from '../Button.js';
|
|
5
5
|
import { Popover } from '../Popover.js';
|
|
6
6
|
import { ColumnChecklist } from './ColumnChecklist.js';
|
|
7
|
+
import { SELECT_COLUMN_ID } from './types.js';
|
|
7
8
|
|
|
8
9
|
export interface ViewOptionsMenuProps<TRow> {
|
|
9
10
|
table: Table<TRow>;
|
|
@@ -29,8 +30,12 @@ export interface ViewOptionsMenuProps<TRow> {
|
|
|
29
30
|
export function ViewOptionsMenu<TRow>({ table, label = 'Columns' }: ViewOptionsMenuProps<TRow>) {
|
|
30
31
|
const [open, setOpen] = useState(false);
|
|
31
32
|
const columns = table.getAllColumns().filter((c) => c.getCanHide());
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
// The checkbox column is not a TanStack column, so it comes off the table's controls — and it
|
|
34
|
+
// is listed FIRST, where it sits (owner, 2026-09-30: *"Allow hiding any column, like the
|
|
35
|
+
// checkbox column"*).
|
|
36
|
+
const selection = table.options.meta?.cbControls?.selectionColumn;
|
|
37
|
+
if (!columns.length && !selection) return null;
|
|
38
|
+
const hiddenCount = columns.filter((c) => !c.getIsVisible()).length + (selection && !selection.visible ? 1 : 0);
|
|
34
39
|
|
|
35
40
|
return (
|
|
36
41
|
<Popover
|
|
@@ -60,14 +65,24 @@ export function ViewOptionsMenu<TRow>({ table, label = 'Columns' }: ViewOptionsM
|
|
|
60
65
|
aria-label='Visible columns'
|
|
61
66
|
selectAllLabel='Show all'
|
|
62
67
|
selectNoneLabel='Hide all'
|
|
63
|
-
items={
|
|
64
|
-
key:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
items={[
|
|
69
|
+
...(selection ? [{ key: SELECT_COLUMN_ID, label: 'Checkboxes', checked: selection.visible }] : []),
|
|
70
|
+
...columns.map((col) => ({
|
|
71
|
+
key: col.id,
|
|
72
|
+
// A label-less column (a cover, a handle) still needs a name in this list.
|
|
73
|
+
label: col.columnDef.meta?.menuLabel || col.columnDef.meta?.label || col.id,
|
|
74
|
+
checked: col.getIsVisible(),
|
|
75
|
+
})),
|
|
76
|
+
]}
|
|
77
|
+
onToggle={(key) =>
|
|
78
|
+
key === SELECT_COLUMN_ID ? selection?.setVisible(!selection.visible) : table.getColumn(key)?.toggleVisibility()
|
|
79
|
+
}
|
|
80
|
+
onSelectAll={() => {
|
|
81
|
+
table.toggleAllColumnsVisible(true);
|
|
82
|
+
selection?.setVisible(true);
|
|
83
|
+
}}
|
|
70
84
|
onSelectNone={() => {
|
|
85
|
+
selection?.setVisible(false);
|
|
71
86
|
// Not `toggleAllColumnsVisible(false)`: that hides the pinned
|
|
72
87
|
// (`alwaysVisible`) columns too in TanStack's implementation, and a
|
|
73
88
|
// table with literally no columns is a blank rectangle the reader
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { afterEach, beforeAll, describe, expect, test } from 'bun:test';
|
|
2
|
-
import { cleanup, fireEvent, render } from '@testing-library/react';
|
|
2
|
+
import { cleanup, fireEvent, render, within } from '@testing-library/react';
|
|
3
3
|
import { useState } from 'react';
|
|
4
4
|
import { hoverHint } from '../../../testing/tooltip.js';
|
|
5
5
|
import { DataTable, type DataTableProps, preloadDataTableParts } from './DataTable.js';
|
|
@@ -191,3 +191,59 @@ describe('DataTable row selection', () => {
|
|
|
191
191
|
expect(list.querySelectorAll('[role="checkbox"]').length).toBe(GAMES.length + 1);
|
|
192
192
|
});
|
|
193
193
|
});
|
|
194
|
+
|
|
195
|
+
describe('the checkbox column can be hidden (owner, 2026-09-30)', () => {
|
|
196
|
+
test('🔴 the Columns menu lists "Checkboxes", and unticking it removes the boxes but keeps the selection', () => {
|
|
197
|
+
const reported: Record<string, boolean>[] = [];
|
|
198
|
+
const changes: string[][] = [];
|
|
199
|
+
const { container, getByRole, getByLabelText } = render(
|
|
200
|
+
<Harness
|
|
201
|
+
initial={['g2']}
|
|
202
|
+
onChange={(next) => changes.push([...next])}
|
|
203
|
+
onColumnVisibilityChange={(next) => reported.push(next)}
|
|
204
|
+
/>,
|
|
205
|
+
);
|
|
206
|
+
expect(headerBox(container)).not.toBeNull();
|
|
207
|
+
fireEvent.click(getByRole('button', { name: /^columns$/i }));
|
|
208
|
+
fireEvent.click(within(getByLabelText('Visible columns')).getByRole('menuitemcheckbox', { name: 'Checkboxes' }));
|
|
209
|
+
expect(reported.at(-1)).toEqual({ __select: false });
|
|
210
|
+
expect(grid(container).querySelector('[role="checkbox"]')).toBeNull();
|
|
211
|
+
expect(container.querySelectorAll('thead th').length).toBe(COLUMNS.length);
|
|
212
|
+
// Hidden, not cleared: nothing reported a new selection.
|
|
213
|
+
expect(changes).toEqual([]);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test('a CONTROLLED visibility is what the table draws — the app owns where it is kept', () => {
|
|
217
|
+
const { container } = render(<Harness columnVisibility={{ __select: false, sizeMb: false }} />);
|
|
218
|
+
expect(grid(container).querySelector('[role="checkbox"]')).toBeNull();
|
|
219
|
+
expect(container.querySelectorAll('thead th').length).toBe(1);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
describe('columnsPersistKey', () => {
|
|
224
|
+
test('a hidden column is still hidden after a remount, and the write is announced for the server', async () => {
|
|
225
|
+
const { onPreferenceWrite } = await import('../../media-gallery/preferenceBus.js');
|
|
226
|
+
const heard: [string, string | null][] = [];
|
|
227
|
+
const off = onPreferenceWrite((key, value) => heard.push([key, value]));
|
|
228
|
+
localStorage.removeItem('spec:columns');
|
|
229
|
+
const first = render(<Harness columnsPersistKey='spec:columns' />);
|
|
230
|
+
fireEvent.click(first.getByRole('button', { name: /^columns$/i }));
|
|
231
|
+
fireEvent.click(within(first.getByLabelText('Visible columns')).getByRole('menuitemcheckbox', { name: 'Size' }));
|
|
232
|
+
expect(heard.at(-1)).toEqual(['spec:columns', JSON.stringify({ sizeMb: false })]);
|
|
233
|
+
first.unmount();
|
|
234
|
+
const second = render(<Harness columnsPersistKey='spec:columns' />);
|
|
235
|
+
expect(second.container.querySelectorAll('thead th').length).toBe(COLUMNS.length); // checkbox + Title
|
|
236
|
+
off();
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
describe('toolbarOptions', () => {
|
|
241
|
+
test('searchable={false} with toolbarOptions keeps the Columns menu and the filter builder, without a search box', () => {
|
|
242
|
+
const { queryByRole, getByRole } = render(
|
|
243
|
+
<Harness searchable={false} toolbarOptions={{ enableSortStack: false }} />,
|
|
244
|
+
);
|
|
245
|
+
expect(getByRole('button', { name: /^columns$/i })).not.toBeNull();
|
|
246
|
+
expect(getByRole('button', { name: /filter/i })).not.toBeNull();
|
|
247
|
+
expect(queryByRole('searchbox')).toBeNull();
|
|
248
|
+
});
|
|
249
|
+
});
|
|
@@ -20,8 +20,12 @@ export interface ColDef<TRow = Record<string, unknown>> {
|
|
|
20
20
|
/** Registry type — dispatched to `CELL_REGISTRY` (display) + `FIELD_REGISTRY` (edit). */
|
|
21
21
|
type: string;
|
|
22
22
|
sortable?: boolean;
|
|
23
|
-
/** Include this column when the
|
|
23
|
+
/** Include this column when the search box or the filter builder narrows rows (default:
|
|
24
|
+
* true). `false` for a column that draws no data — a cover, a drag handle, a row control. */
|
|
24
25
|
filterable?: boolean;
|
|
26
|
+
/** The column's name in the Columns menu when its header has no text (a cover, a handle) —
|
|
27
|
+
* a checklist row with no name is a switch nobody can find. Default: `label`, then `key`. */
|
|
28
|
+
menuLabel?: string;
|
|
25
29
|
/** Start hidden (toggle back on via {@link ViewOptionsMenu}). */
|
|
26
30
|
hidden?: boolean;
|
|
27
31
|
/** Column track width. A number is px; a string is any CSS width — use
|
|
@@ -206,3 +210,10 @@ export interface DataTableHandle {
|
|
|
206
210
|
options?: { align?: 'start' | 'center' | 'end' | 'auto'; behavior?: 'auto' | 'smooth' },
|
|
207
211
|
): number | null;
|
|
208
212
|
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* The checkbox column's key in `columnVisibility`. It is not a TanStack column — it has no
|
|
216
|
+
* data and no header menu of its own — so it cannot be hidden through `getAllColumns()`; the
|
|
217
|
+
* Columns menu lists it by this key instead, beside the real ones.
|
|
218
|
+
*/
|
|
219
|
+
export const SELECT_COLUMN_ID = '__select';
|
|
@@ -144,3 +144,23 @@ describe('serverWindow (forge task 2300)', () => {
|
|
|
144
144
|
expect(screen.queryByRole('button', { name: /next page|previous page/i })).toBeNull();
|
|
145
145
|
});
|
|
146
146
|
});
|
|
147
|
+
|
|
148
|
+
describe('serverWindow + the filter builder', () => {
|
|
149
|
+
test('🔴 the builder is REPORTED, never applied to the loaded window', () => {
|
|
150
|
+
const rows = Array.from({ length: 3 }, (_, i) => ({ id: `r${i}`, name: `Row ${i}` }));
|
|
151
|
+
render(
|
|
152
|
+
<DataTable<Row>
|
|
153
|
+
columns={COLUMNS}
|
|
154
|
+
data={rows}
|
|
155
|
+
getRowId={(r) => r.id}
|
|
156
|
+
serverWindow={{ start: 0, total: 3, rowHeight: 50 }}
|
|
157
|
+
filterQuery={{ groups: [{ id: 'g', conditions: [{ id: 'c', column: 'name', op: 'contains', value: 'Row 1' }] }] }}
|
|
158
|
+
/>,
|
|
159
|
+
);
|
|
160
|
+
// A window is a page of what the SERVER matched; narrowing it again would hide rows the
|
|
161
|
+
// server already decided belong, and say nothing about the thousands not loaded.
|
|
162
|
+
// (Each row is drawn once per tier — grid, list, cards — hence `getAllByText`.)
|
|
163
|
+
expect(screen.getAllByText('Row 0').length).toBeGreaterThan(0);
|
|
164
|
+
expect(screen.getAllByText('Row 2').length).toBeGreaterThan(0);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
localDateToIso,
|
|
12
12
|
ThemedDayButton,
|
|
13
13
|
} from '../lib/daypicker.js';
|
|
14
|
-
import { type ControlSize, fieldControlClass } from '../lib/field.js';
|
|
14
|
+
import { type ControlSize, declareFieldWidth, fieldControlClass } from '../lib/field.js';
|
|
15
15
|
import { Drawer } from './Drawer.js';
|
|
16
16
|
import { Popover } from './Popover.js';
|
|
17
17
|
|
|
@@ -147,3 +147,5 @@ export function DatePicker({
|
|
|
147
147
|
</Popover>
|
|
148
148
|
);
|
|
149
149
|
}
|
|
150
|
+
|
|
151
|
+
declareFieldWidth(DatePicker, 'sm');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cn } from '../lib/cn.js';
|
|
2
|
-
import { type ControlSize, fieldControlClass } from '../lib/field.js';
|
|
2
|
+
import { type ControlSize, declareFieldWidth, fieldControlClass } from '../lib/field.js';
|
|
3
3
|
import { DatePicker } from './DatePicker.js';
|
|
4
4
|
|
|
5
5
|
export interface DateTimeFieldProps {
|
|
@@ -88,3 +88,5 @@ export function DateTimeField({
|
|
|
88
88
|
</div>
|
|
89
89
|
);
|
|
90
90
|
}
|
|
91
|
+
|
|
92
|
+
declareFieldWidth(DateTimeField, 'sm');
|