torch-glare 2.5.6 → 2.6.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/README.md +4 -3
- package/dist/bin/index.js +9 -24
- package/dist/bin/index.js.map +1 -1
- package/dist/src/commands/add.d.ts +2 -2
- package/dist/src/commands/add.d.ts.map +1 -1
- package/dist/src/commands/add.js +4 -70
- package/dist/src/commands/add.js.map +1 -1
- package/dist/src/commands/hook.d.ts +5 -4
- package/dist/src/commands/hook.d.ts.map +1 -1
- package/dist/src/commands/hook.js +7 -75
- package/dist/src/commands/hook.js.map +1 -1
- package/dist/src/commands/init.js +1 -1
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/layout.d.ts +5 -4
- package/dist/src/commands/layout.d.ts.map +1 -1
- package/dist/src/commands/layout.js +7 -75
- package/dist/src/commands/layout.js.map +1 -1
- package/dist/src/commands/provider.d.ts +5 -4
- package/dist/src/commands/provider.d.ts.map +1 -1
- package/dist/src/commands/provider.js +7 -75
- package/dist/src/commands/provider.js.map +1 -1
- package/dist/src/commands/update.d.ts.map +1 -1
- package/dist/src/commands/update.js +78 -43
- package/dist/src/commands/update.js.map +1 -1
- package/dist/src/commands/utils.d.ts +5 -4
- package/dist/src/commands/utils.d.ts.map +1 -1
- package/dist/src/commands/utils.js +7 -80
- package/dist/src/commands/utils.js.map +1 -1
- package/dist/src/shared/addFromRegistry.d.ts +16 -0
- package/dist/src/shared/addFromRegistry.d.ts.map +1 -0
- package/dist/src/shared/addFromRegistry.js +88 -0
- package/dist/src/shared/addFromRegistry.js.map +1 -0
- package/dist/src/shared/getInstallCommand.d.ts.map +1 -1
- package/dist/src/shared/getInstallCommand.js +5 -0
- package/dist/src/shared/getInstallCommand.js.map +1 -1
- package/dist/src/shared/installDependencies.d.ts +0 -10
- package/dist/src/shared/installDependencies.d.ts.map +1 -1
- package/dist/src/shared/installDependencies.js +0 -17
- package/dist/src/shared/installDependencies.js.map +1 -1
- package/dist/src/shared/installFromPlan.d.ts +7 -5
- package/dist/src/shared/installFromPlan.d.ts.map +1 -1
- package/dist/src/shared/installFromPlan.js +103 -36
- package/dist/src/shared/installFromPlan.js.map +1 -1
- package/dist/src/shared/loadRegistry.d.ts +12 -3
- package/dist/src/shared/loadRegistry.d.ts.map +1 -1
- package/dist/src/shared/loadRegistry.js +19 -20
- package/dist/src/shared/loadRegistry.js.map +1 -1
- package/dist/src/shared/registryClient.d.ts +19 -0
- package/dist/src/shared/registryClient.d.ts.map +1 -0
- package/dist/src/shared/registryClient.js +128 -0
- package/dist/src/shared/registryClient.js.map +1 -0
- package/dist/src/shared/resolveEntry.d.ts +10 -7
- package/dist/src/shared/resolveEntry.d.ts.map +1 -1
- package/dist/src/shared/resolveEntry.js +18 -13
- package/dist/src/shared/resolveEntry.js.map +1 -1
- package/dist/src/shared/suggestOtherCommand.d.ts +7 -1
- package/dist/src/shared/suggestOtherCommand.d.ts.map +1 -1
- package/dist/src/shared/suggestOtherCommand.js +29 -23
- package/dist/src/shared/suggestOtherCommand.js.map +1 -1
- package/dist/src/types/main.d.ts +31 -2
- package/dist/src/types/main.d.ts.map +1 -1
- package/package.json +6 -11
- package/apps/lib/components/ActionButton.tsx +0 -93
- package/apps/lib/components/ActionsGroup.tsx +0 -27
- package/apps/lib/components/AlertDialog.tsx +0 -204
- package/apps/lib/components/Avatar.tsx +0 -46
- package/apps/lib/components/Badge.tsx +0 -250
- package/apps/lib/components/BadgeField.tsx +0 -403
- package/apps/lib/components/Breadcrumb.tsx +0 -280
- package/apps/lib/components/Button.tsx +0 -325
- package/apps/lib/components/ButtonGroup.tsx +0 -190
- package/apps/lib/components/Calendar.tsx +0 -115
- package/apps/lib/components/Card.tsx +0 -95
- package/apps/lib/components/Checkbox.tsx +0 -42
- package/apps/lib/components/ColorPicker.tsx +0 -441
- package/apps/lib/components/ConclusionHeader.tsx +0 -148
- package/apps/lib/components/ContextMenu.tsx +0 -571
- package/apps/lib/components/CountBadge.tsx +0 -52
- package/apps/lib/components/DataTable.tsx +0 -213
- package/apps/lib/components/DataViews/badge.ts +0 -49
- package/apps/lib/components/DataViews/cell.tsx +0 -324
- package/apps/lib/components/DataViews/context.ts +0 -144
- package/apps/lib/components/DataViews/data-views.tsx +0 -395
- package/apps/lib/components/DataViews/filters/children.tsx +0 -98
- package/apps/lib/components/DataViews/filters/custom.tsx +0 -34
- package/apps/lib/components/DataViews/filters/filters.tsx +0 -157
- package/apps/lib/components/DataViews/filters/index.ts +0 -4
- package/apps/lib/components/DataViews/filters/labelled.tsx +0 -20
- package/apps/lib/components/DataViews/filters/presets.tsx +0 -65
- package/apps/lib/components/DataViews/filters/sync.tsx +0 -35
- package/apps/lib/components/DataViews/filters/values.ts +0 -173
- package/apps/lib/components/DataViews/header.tsx +0 -220
- package/apps/lib/components/DataViews/hooks/index.ts +0 -5
- package/apps/lib/components/DataViews/hooks/useActiveRow.ts +0 -22
- package/apps/lib/components/DataViews/hooks/useControllable.ts +0 -52
- package/apps/lib/components/DataViews/index.ts +0 -88
- package/apps/lib/components/DataViews/panel/columns.tsx +0 -153
- package/apps/lib/components/DataViews/panel/controls.tsx +0 -106
- package/apps/lib/components/DataViews/panel/index.ts +0 -3
- package/apps/lib/components/DataViews/panel/panel.tsx +0 -164
- package/apps/lib/components/DataViews/panel/saved-views.tsx +0 -67
- package/apps/lib/components/DataViews/panel/section.tsx +0 -81
- package/apps/lib/components/DataViews/panel/sort.tsx +0 -42
- package/apps/lib/components/DataViews/panel/tab.tsx +0 -31
- package/apps/lib/components/DataViews/slots.ts +0 -72
- package/apps/lib/components/DataViews/states.tsx +0 -73
- package/apps/lib/components/DataViews/types.ts +0 -510
- package/apps/lib/components/DataViews/views/board-view.tsx +0 -381
- package/apps/lib/components/DataViews/views/card-rows.tsx +0 -36
- package/apps/lib/components/DataViews/views/inbox-view.tsx +0 -260
- package/apps/lib/components/DataViews/views/pane-views.tsx +0 -196
- package/apps/lib/components/DataViews/views/table-view.tsx +0 -493
- package/apps/lib/components/DataViews/views/tree-view.tsx +0 -367
- package/apps/lib/components/DatePicker.tsx +0 -232
- package/apps/lib/components/Dialog.tsx +0 -116
- package/apps/lib/components/Divider.tsx +0 -28
- package/apps/lib/components/Drawer.tsx +0 -451
- package/apps/lib/components/DropdownMenu.tsx +0 -554
- package/apps/lib/components/FieldHint.tsx +0 -69
- package/apps/lib/components/Form.tsx +0 -182
- package/apps/lib/components/FormBuilder/context.ts +0 -85
- package/apps/lib/components/FormBuilder/field-kind.ts +0 -28
- package/apps/lib/components/FormBuilder/fields/ChoiceFields.tsx +0 -60
- package/apps/lib/components/FormBuilder/fields/ColorField.tsx +0 -59
- package/apps/lib/components/FormBuilder/fields/CustomField.tsx +0 -11
- package/apps/lib/components/FormBuilder/fields/DateField.tsx +0 -32
- package/apps/lib/components/FormBuilder/fields/FieldArray.tsx +0 -74
- package/apps/lib/components/FormBuilder/fields/FieldShell.tsx +0 -168
- package/apps/lib/components/FormBuilder/fields/FileField.tsx +0 -39
- package/apps/lib/components/FormBuilder/fields/OptionListFields.tsx +0 -132
- package/apps/lib/components/FormBuilder/fields/OtpField.tsx +0 -31
- package/apps/lib/components/FormBuilder/fields/PhoneField.tsx +0 -109
- package/apps/lib/components/FormBuilder/fields/RichTextEditorField.tsx +0 -31
- package/apps/lib/components/FormBuilder/fields/SelectField.tsx +0 -114
- package/apps/lib/components/FormBuilder/fields/SignatureField.tsx +0 -157
- package/apps/lib/components/FormBuilder/fields/SliderField.tsx +0 -67
- package/apps/lib/components/FormBuilder/fields/SwitchBoxField.tsx +0 -41
- package/apps/lib/components/FormBuilder/fields/TableField.tsx +0 -336
- package/apps/lib/components/FormBuilder/fields/TextField.tsx +0 -213
- package/apps/lib/components/FormBuilder/fields/TreeSelectField.tsx +0 -43
- package/apps/lib/components/FormBuilder/fields/countries.ts +0 -303
- package/apps/lib/components/FormBuilder/fields/index.ts +0 -25
- package/apps/lib/components/FormBuilder/form-builder.tsx +0 -198
- package/apps/lib/components/FormBuilder/index.ts +0 -31
- package/apps/lib/components/FormBuilder/numberFormat.ts +0 -16
- package/apps/lib/components/FormBuilder/submit.tsx +0 -67
- package/apps/lib/components/FormBuilder/types.ts +0 -316
- package/apps/lib/components/FormRenderer/FormDrawer.tsx +0 -250
- package/apps/lib/components/FormRenderer/detail.tsx +0 -256
- package/apps/lib/components/FormRenderer/form-renderer.tsx +0 -358
- package/apps/lib/components/FormRenderer/header.tsx +0 -88
- package/apps/lib/components/FormRenderer/index.ts +0 -17
- package/apps/lib/components/FormRenderer/notch-action.tsx +0 -64
- package/apps/lib/components/FormRenderer/section.tsx +0 -39
- package/apps/lib/components/FormRenderer/stepper.tsx +0 -393
- package/apps/lib/components/FormRenderer/types.ts +0 -115
- package/apps/lib/components/FormSummary.tsx +0 -282
- package/apps/lib/components/HeaderBar.tsx +0 -117
- package/apps/lib/components/ImageAttachment.tsx +0 -209
- package/apps/lib/components/InnerLabelField.tsx +0 -154
- package/apps/lib/components/Input.tsx +0 -224
- package/apps/lib/components/InputField.tsx +0 -147
- package/apps/lib/components/InputOTP.tsx +0 -84
- package/apps/lib/components/Label.tsx +0 -129
- package/apps/lib/components/LabelField.tsx +0 -76
- package/apps/lib/components/LabeledCheckBox.tsx +0 -51
- package/apps/lib/components/LabeledRadio.tsx +0 -51
- package/apps/lib/components/LinkButton.tsx +0 -94
- package/apps/lib/components/LoginButton.tsx +0 -51
- package/apps/lib/components/PasswordLevel.tsx +0 -58
- package/apps/lib/components/Popover.tsx +0 -312
- package/apps/lib/components/ProfileMenu.tsx +0 -85
- package/apps/lib/components/Radio.tsx +0 -59
- package/apps/lib/components/RadioCard.tsx +0 -50
- package/apps/lib/components/ScrollArea.tsx +0 -54
- package/apps/lib/components/SearchField.tsx +0 -62
- package/apps/lib/components/SearchableSelect.tsx +0 -318
- package/apps/lib/components/SearchableTable.tsx +0 -344
- package/apps/lib/components/SearchableTree.tsx +0 -485
- package/apps/lib/components/SearchableTreeDialog.tsx +0 -502
- package/apps/lib/components/SectionBlock.tsx +0 -168
- package/apps/lib/components/Select.tsx +0 -347
- package/apps/lib/components/SimpleSelect.tsx +0 -225
- package/apps/lib/components/Skeleton.tsx +0 -12
- package/apps/lib/components/SlideDatePicker.tsx +0 -255
- package/apps/lib/components/SpinLoading.tsx +0 -174
- package/apps/lib/components/Stepper.tsx +0 -480
- package/apps/lib/components/Switch.tsx +0 -82
- package/apps/lib/components/TabFormItem.tsx +0 -153
- package/apps/lib/components/TabSwitch.tsx +0 -172
- package/apps/lib/components/Table.tsx +0 -597
- package/apps/lib/components/TextEditor/ChartBlockTool.ts +0 -676
- package/apps/lib/components/TextEditor/RichTextField.tsx +0 -46
- package/apps/lib/components/TextEditor/TableDnDWrapper.ts +0 -462
- package/apps/lib/components/TextEditor/TextEditor.tsx +0 -1164
- package/apps/lib/components/TextEditor/TextEditorToolbar.tsx +0 -429
- package/apps/lib/components/TextEditor/editor-tools/AlignmentTune.ts +0 -70
- package/apps/lib/components/TextEditor/editor-tools/ColorInlineTool.ts +0 -50
- package/apps/lib/components/TextEditor/editor-tools/StrikethroughInlineTool.ts +0 -48
- package/apps/lib/components/TextEditor/editor-tools/inlineFormat.ts +0 -98
- package/apps/lib/components/TextEditor/editorjs.d.ts +0 -60
- package/apps/lib/components/TextEditor/index.ts +0 -7
- package/apps/lib/components/TextEditor/markdownParser.ts +0 -346
- package/apps/lib/components/Textarea.tsx +0 -117
- package/apps/lib/components/Timeline.tsx +0 -263
- package/apps/lib/components/Toast.tsx +0 -87
- package/apps/lib/components/Toggle.tsx +0 -125
- package/apps/lib/components/ToggleButton.tsx +0 -140
- package/apps/lib/components/Tooltip.tsx +0 -116
- package/apps/lib/components/TransparentLabel.tsx +0 -71
- package/apps/lib/components/TreeDropDown.tsx +0 -115
- package/apps/lib/components/TreeFolder/TreeFolder.tsx +0 -380
- package/apps/lib/components/TreeFolder/TreeFolderBreadcrumb.tsx +0 -75
- package/apps/lib/components/TreeFolder/TreeFolderRow.tsx +0 -354
- package/apps/lib/components/TreeFolder/TreeFolderStyles.tsx +0 -60
- package/apps/lib/components/TreeFolder/icons.tsx +0 -60
- package/apps/lib/components/TreeFolder/index.ts +0 -24
- package/apps/lib/components/TreeFolder/treeFolderUtils.ts +0 -109
- package/apps/lib/components/TreeFolder/types.ts +0 -77
- package/apps/lib/components/TreeFolder/useTreeFolderDnD.ts +0 -118
- package/apps/lib/hooks/useActiveTreeItem.ts +0 -61
- package/apps/lib/hooks/useClickOutside.ts +0 -27
- package/apps/lib/hooks/useDragDrop.tsx +0 -365
- package/apps/lib/hooks/useHtmlDir.ts +0 -31
- package/apps/lib/hooks/useInfiniteScroll.ts +0 -108
- package/apps/lib/hooks/useIsMobile.ts +0 -21
- package/apps/lib/hooks/useResize.ts +0 -67
- package/apps/lib/hooks/useTagSelection.ts +0 -300
- package/apps/lib/layouts/CNLayout.tsx +0 -372
- package/apps/lib/layouts/DataViewCard.tsx +0 -62
- package/apps/lib/layouts/FieldSection.tsx +0 -91
- package/apps/lib/layouts/TreeSubLayout.tsx +0 -173
- package/apps/lib/providers/ThemeProvider.tsx +0 -84
- package/apps/lib/registry.json +0 -1217
- package/apps/lib/tsconfig.json +0 -9
- package/apps/lib/tsconfig.tsbuildinfo +0 -1
- package/apps/lib/utils/cn.ts +0 -6
- package/apps/lib/utils/color.ts +0 -175
- package/apps/lib/utils/dataViews/path.ts +0 -67
- package/apps/lib/utils/dataViews/query.ts +0 -73
- package/apps/lib/utils/dataViews/types.ts +0 -187
- package/apps/lib/utils/dateFormat.ts +0 -108
- package/apps/lib/utils/resize.ts +0 -31
- package/apps/lib/utils/scroller.ts +0 -26
- package/apps/lib/utils/types.ts +0 -16
- package/docs/BLOCKS.md +0 -129
- package/docs/CHANGELOG-1.1.16.md +0 -108
- package/docs/Cover.png +0 -0
- package/docs/README.md +0 -215
- package/docs/components/action-button.md +0 -638
- package/docs/components/actions-group.md +0 -708
- package/docs/components/alert-dialog.md +0 -920
- package/docs/components/avatar.md +0 -709
- package/docs/components/badge-field.md +0 -738
- package/docs/components/badge.md +0 -301
- package/docs/components/breadcrumb.md +0 -930
- package/docs/components/button-group.md +0 -650
- package/docs/components/button.md +0 -639
- package/docs/components/calendar.md +0 -555
- package/docs/components/card.md +0 -622
- package/docs/components/chart-block-tool.md +0 -129
- package/docs/components/checkbox.md +0 -699
- package/docs/components/cn-layout.md +0 -854
- package/docs/components/color-picker.md +0 -101
- package/docs/components/conclusion-header.md +0 -80
- package/docs/components/context-menu.md +0 -483
- package/docs/components/count-badge.md +0 -560
- package/docs/components/data-table.md +0 -854
- package/docs/components/data-views/backend-response.md +0 -324
- package/docs/components/data-views/examples/a11y-rtl.md +0 -250
- package/docs/components/data-views/examples/api-orders-route.md +0 -130
- package/docs/components/data-views/examples/fields.md +0 -362
- package/docs/components/data-views/examples/filters.md +0 -307
- package/docs/components/data-views/examples/inbox-routing.md +0 -218
- package/docs/components/data-views/examples/index.md +0 -29
- package/docs/components/data-views/examples/overview.md +0 -244
- package/docs/components/data-views/examples/panel.md +0 -212
- package/docs/components/data-views/examples/scale.md +0 -231
- package/docs/components/data-views/examples/server-side.md +0 -210
- package/docs/components/data-views/examples/state.md +0 -250
- package/docs/components/data-views/examples/tree-custom.md +0 -388
- package/docs/components/data-views/examples/view-registry.md +0 -313
- package/docs/components/data-views/examples/views.md +0 -534
- package/docs/components/data-views/guide.md +0 -405
- package/docs/components/data-views/index.md +0 -1521
- package/docs/components/data-views/migration.md +0 -81
- package/docs/components/date-picker.md +0 -857
- package/docs/components/dialog.md +0 -929
- package/docs/components/divider.md +0 -766
- package/docs/components/drawer.md +0 -654
- package/docs/components/dropdown-menu.md +0 -668
- package/docs/components/field-hint.md +0 -898
- package/docs/components/field-section.md +0 -781
- package/docs/components/form-builder.md +0 -284
- package/docs/components/form-renderer.md +0 -396
- package/docs/components/form-summary.md +0 -138
- package/docs/components/form.md +0 -785
- package/docs/components/header-bar.md +0 -188
- package/docs/components/image-attachment.md +0 -906
- package/docs/components/inner-label-field.md +0 -819
- package/docs/components/input-field.md +0 -623
- package/docs/components/input-otp.md +0 -756
- package/docs/components/input.md +0 -634
- package/docs/components/label-field.md +0 -770
- package/docs/components/label.md +0 -761
- package/docs/components/labeled-check-box.md +0 -642
- package/docs/components/labeled-radio.md +0 -796
- package/docs/components/link-button.md +0 -685
- package/docs/components/login-button.md +0 -872
- package/docs/components/password-level.md +0 -803
- package/docs/components/popover.md +0 -649
- package/docs/components/profile-menu.md +0 -656
- package/docs/components/radio-card.md +0 -801
- package/docs/components/radio.md +0 -735
- package/docs/components/scroll-area.md +0 -691
- package/docs/components/search-field.md +0 -702
- package/docs/components/searchable-select.md +0 -368
- package/docs/components/searchable-table.md +0 -438
- package/docs/components/searchable-tree-dialog.md +0 -190
- package/docs/components/searchable-tree.md +0 -200
- package/docs/components/section-block.md +0 -483
- package/docs/components/select.md +0 -709
- package/docs/components/simple-select.md +0 -741
- package/docs/components/skeleton.md +0 -706
- package/docs/components/slide-date-picker.md +0 -837
- package/docs/components/spin-loading.md +0 -696
- package/docs/components/stepper.md +0 -316
- package/docs/components/switch.md +0 -770
- package/docs/components/tab-form-item.md +0 -773
- package/docs/components/tab-switch.md +0 -170
- package/docs/components/table-dnd-wrapper.md +0 -107
- package/docs/components/table.md +0 -1088
- package/docs/components/text-editor.md +0 -735
- package/docs/components/textarea.md +0 -681
- package/docs/components/timeline.md +0 -254
- package/docs/components/toast.md +0 -773
- package/docs/components/toggle-button.md +0 -643
- package/docs/components/toggle.md +0 -882
- package/docs/components/tooltip.md +0 -513
- package/docs/components/transparent-label.md +0 -721
- package/docs/components/tree-drop-down.md +0 -740
- package/docs/components/tree-folder.md +0 -110
- package/docs/components/tree-sub-layout.md +0 -707
- package/docs/explanation/architecture.md +0 -54
- package/docs/explanation/design-system.md +0 -52
- package/docs/how-to/form-and-list-recipes.md +0 -386
- package/docs/how-to/forms-with-form-builder.md +0 -472
- package/docs/how-to/guides.md +0 -1027
- package/docs/migration/changelog.md +0 -60
- package/docs/migration/form-builder-2.5.2.md +0 -113
- package/docs/reference/cli.md +0 -112
- package/docs/reference/components.md +0 -572
- package/docs/reference/hooks.md +0 -1523
- package/docs/reference/providers.md +0 -866
- package/docs/reference/tailwind-plugins.md +0 -942
- package/docs/reference/theme.md +0 -85
- package/docs/reference/types.md +0 -1016
- package/docs/reference/utilities.md +0 -726
- package/docs/tutorials/building-first-form.md +0 -846
- package/docs/tutorials/component-composition.md +0 -882
- package/docs/tutorials/getting-started.md +0 -302
- package/docs/tutorials/theming-basics.md +0 -785
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { createContext, useContext } from "react";
|
|
4
|
-
import type {
|
|
5
|
-
ColumnState,
|
|
6
|
-
FieldConfig,
|
|
7
|
-
FilterFieldDescriptor,
|
|
8
|
-
FilterState,
|
|
9
|
-
Row,
|
|
10
|
-
Sort,
|
|
11
|
-
} from "../../utils/dataViews/types";
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Every context DataViews uses. As in `FormBuilder`, this is the only file that calls
|
|
15
|
-
* `createContext` — a part never invents its own provider, so there is one place to look when
|
|
16
|
-
* asking "where does this part get its data".
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
// ─── Data ─────────────────────────────────────────────────────────────────────
|
|
20
|
-
|
|
21
|
-
export interface DataContextValue {
|
|
22
|
-
/** The rows to paint, exactly as handed in. Already filtered and sorted by the app. */
|
|
23
|
-
rows: readonly Row[];
|
|
24
|
-
/** How to paint each field. */
|
|
25
|
-
fields: readonly FieldConfig[];
|
|
26
|
-
/** The visible fields, in the order the user arranged them. Resolved once by the root. */
|
|
27
|
-
visibleFields: readonly FieldConfig[];
|
|
28
|
-
/** Stable identity for a row — selection and drag both key off this. */
|
|
29
|
-
getRowId: (row: Row, index: number) => string;
|
|
30
|
-
loading: boolean;
|
|
31
|
-
/** True while the *next* page is in flight — `loading` is the first one. */
|
|
32
|
-
loadingMore: boolean;
|
|
33
|
-
/** Whether there is a next page: derived from `rows.length < total`, never passed in. */
|
|
34
|
-
hasMore: boolean;
|
|
35
|
-
/** Ask for the next page. Undefined when the caller did not opt into scroll loading. */
|
|
36
|
-
onLoadMore?: () => void;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export const DataContext = createContext<DataContextValue | null>(null);
|
|
40
|
-
|
|
41
|
-
export function useDataViewsData(): DataContextValue {
|
|
42
|
-
const ctx = useContext(DataContext);
|
|
43
|
-
if (!ctx) throw new Error("DataViews parts must be rendered inside <DataViews>.");
|
|
44
|
-
return ctx;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// ─── View ─────────────────────────────────────────────────────────────────────
|
|
48
|
-
|
|
49
|
-
/** One entry in the switcher. Registered by rendering the view, never by a visibility map. */
|
|
50
|
-
export interface RegisteredView {
|
|
51
|
-
id: string;
|
|
52
|
-
label: string;
|
|
53
|
-
icon?: React.ReactNode;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** The root's interaction state — everything the user can change that is not a row. */
|
|
57
|
-
export interface ViewContextValue {
|
|
58
|
-
view: string;
|
|
59
|
-
setView: (id: string) => void;
|
|
60
|
-
/** In the order the views were rendered. */
|
|
61
|
-
views: readonly RegisteredView[];
|
|
62
|
-
search: string;
|
|
63
|
-
setSearch: (q: string) => void;
|
|
64
|
-
sort: Sort;
|
|
65
|
-
setSort: (sort: Sort) => void;
|
|
66
|
-
selection: readonly string[];
|
|
67
|
-
setSelection: (ids: readonly string[]) => void;
|
|
68
|
-
/** The row the detail pane is showing, for the views that have one. */
|
|
69
|
-
activeId: string | null;
|
|
70
|
-
setActiveId: (id: string | null) => void;
|
|
71
|
-
/** Column visibility and order. The panel edits these; the root paints from them. */
|
|
72
|
-
columns: readonly ColumnState[];
|
|
73
|
-
setColumns: (columns: readonly ColumnState[]) => void;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export const ViewContext = createContext<ViewContextValue | null>(null);
|
|
77
|
-
|
|
78
|
-
export function useDataViewsView(): ViewContextValue {
|
|
79
|
-
const ctx = useContext(ViewContext);
|
|
80
|
-
if (!ctx) throw new Error("DataViews parts must be rendered inside <DataViews>.");
|
|
81
|
-
return ctx;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// ─── Panel ────────────────────────────────────────────────────────────────────
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Whether the settings panel is showing. Provided by the **root**, not by `Panel` — the toggle
|
|
88
|
-
* lives in the header, which is `Panel`'s sibling, so neither can own the state the other needs.
|
|
89
|
-
*/
|
|
90
|
-
export interface PanelContextValue {
|
|
91
|
-
open: boolean;
|
|
92
|
-
setOpen: (open: boolean) => void;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export const PanelContext = createContext<PanelContextValue | null>(null);
|
|
96
|
-
|
|
97
|
-
export function useDataViewsPanel(): PanelContextValue {
|
|
98
|
-
const ctx = useContext(PanelContext);
|
|
99
|
-
if (!ctx) throw new Error("DataViews parts must be rendered inside <DataViews>.");
|
|
100
|
-
return ctx;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/** One entry in the panel's tab strip, registered by rendering a `DataViews.Panel.Tab`. */
|
|
104
|
-
export interface PanelTab {
|
|
105
|
-
value: string;
|
|
106
|
-
label: string;
|
|
107
|
-
icon?: React.ReactNode;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/** Which tab the panel is on. Owned by `Panel`, because only its own children care. */
|
|
111
|
-
export interface PanelTabsContextValue {
|
|
112
|
-
tab: string;
|
|
113
|
-
setTab: (tab: string) => void;
|
|
114
|
-
tabs: readonly PanelTab[];
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export const PanelTabsContext = createContext<PanelTabsContextValue | null>(null);
|
|
118
|
-
|
|
119
|
-
export function useDataViewsPanelTabs(): PanelTabsContextValue {
|
|
120
|
-
const ctx = useContext(PanelTabsContext);
|
|
121
|
-
if (!ctx) throw new Error("DataViews.Panel parts must be rendered inside <DataViews.Panel>.");
|
|
122
|
-
return ctx;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// ─── Filters ──────────────────────────────────────────────────────────────────
|
|
126
|
-
|
|
127
|
-
export interface FiltersContextValue {
|
|
128
|
-
filters: FilterState;
|
|
129
|
-
setFilters: (filters: FilterState) => void;
|
|
130
|
-
/**
|
|
131
|
-
* What each filter control is, read off the `FormBuilder` children of `DataViews.Filters` —
|
|
132
|
-
* never derived from the rows. The root collects them as well, so a consumer such as
|
|
133
|
-
* `Filters.Presets` can resolve a field by path even when it is rendered outside `Filters`.
|
|
134
|
-
*/
|
|
135
|
-
filterFields: readonly FilterFieldDescriptor[];
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export const FiltersContext = createContext<FiltersContextValue | null>(null);
|
|
139
|
-
|
|
140
|
-
export function useDataViewsFilters(): FiltersContextValue {
|
|
141
|
-
const ctx = useContext(FiltersContext);
|
|
142
|
-
if (!ctx) throw new Error("DataViews.Filters parts must be rendered inside <DataViews.Filters>.");
|
|
143
|
-
return ctx;
|
|
144
|
-
}
|
|
@@ -1,395 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import React, { Children, useCallback, useEffect, useMemo, useState } from "react";
|
|
4
|
-
import { cn } from "../../utils/cn";
|
|
5
|
-
import { defaultGetRowId } from "../../utils/dataViews/path";
|
|
6
|
-
import { emptyQuery } from "../../utils/dataViews/query";
|
|
7
|
-
import type {
|
|
8
|
-
ColumnState,
|
|
9
|
-
DataViewsQuery,
|
|
10
|
-
FieldConfig,
|
|
11
|
-
FilterState,
|
|
12
|
-
Sort,
|
|
13
|
-
} from "../../utils/dataViews/types";
|
|
14
|
-
import {
|
|
15
|
-
DataContext,
|
|
16
|
-
FiltersContext,
|
|
17
|
-
PanelContext,
|
|
18
|
-
ViewContext,
|
|
19
|
-
type RegisteredView,
|
|
20
|
-
} from "./context";
|
|
21
|
-
import { Actions, Header, PanelToggle, Search, ViewSwitch } from "./header";
|
|
22
|
-
import { Empty } from "./states";
|
|
23
|
-
import {
|
|
24
|
-
isEmptyElement,
|
|
25
|
-
isHeaderElement,
|
|
26
|
-
isPanelElement,
|
|
27
|
-
isViewElement,
|
|
28
|
-
viewMetaOf,
|
|
29
|
-
} from "./slots";
|
|
30
|
-
import { collectFilterFields, Filters } from "./filters";
|
|
31
|
-
import { Panel } from "./panel";
|
|
32
|
-
import { BoardView } from "./views/board-view";
|
|
33
|
-
import { Detail, InboxView } from "./views/inbox-view";
|
|
34
|
-
import { TableView } from "./views/table-view";
|
|
35
|
-
import { TreeView } from "./views/tree-view";
|
|
36
|
-
import { useControllable } from "./hooks/useControllable";
|
|
37
|
-
import type { DataViewsRootProps } from "./types";
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* A cheap identity for a node used only as a memo key. React elements have no stable identity
|
|
41
|
-
* across renders, so an inline `<i className="ri-table-line"/>` would look new every time; the
|
|
42
|
-
* element's props are what actually distinguish one icon from another.
|
|
43
|
-
*/
|
|
44
|
-
function iconKey(icon: React.ReactNode): string {
|
|
45
|
-
if (icon === null || icon === undefined || typeof icon === "boolean") return "";
|
|
46
|
-
if (React.isValidElement(icon)) return JSON.stringify(icon.props ?? {});
|
|
47
|
-
return String(icon);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// ─── Root ─────────────────────────────────────────────────────────────────────
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Resolve which fields to paint and in what order.
|
|
54
|
-
*
|
|
55
|
-
* The old code sorted `visibleFields` at five call sites with subtly different tie-breaks. There
|
|
56
|
-
* is one rule now: if `columns` is supplied it decides both visibility and order; anything not
|
|
57
|
-
* mentioned in it keeps its declaration order at the end.
|
|
58
|
-
*/
|
|
59
|
-
function resolveVisibleFields(
|
|
60
|
-
fields: readonly FieldConfig[],
|
|
61
|
-
columns: readonly ColumnState[] | undefined,
|
|
62
|
-
): FieldConfig[] {
|
|
63
|
-
if (!columns || columns.length === 0) {
|
|
64
|
-
return fields.filter((f) => f.visible !== false && f.type !== "hidden");
|
|
65
|
-
}
|
|
66
|
-
const byPath = new Map(fields.map((f) => [f.path, f]));
|
|
67
|
-
const ordered: FieldConfig[] = [];
|
|
68
|
-
for (const col of columns) {
|
|
69
|
-
const field = byPath.get(col.path);
|
|
70
|
-
if (field && col.visible) ordered.push(field);
|
|
71
|
-
byPath.delete(col.path);
|
|
72
|
-
}
|
|
73
|
-
for (const field of byPath.values()) {
|
|
74
|
-
if (field.visible !== false && field.type !== "hidden") ordered.push(field);
|
|
75
|
-
}
|
|
76
|
-
return ordered;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function DataViewsRoot({
|
|
80
|
-
rows,
|
|
81
|
-
fields,
|
|
82
|
-
children,
|
|
83
|
-
getRowId = defaultGetRowId,
|
|
84
|
-
total = 0,
|
|
85
|
-
loading = false,
|
|
86
|
-
onLoadMore,
|
|
87
|
-
loadingMore = false,
|
|
88
|
-
query,
|
|
89
|
-
onQueryChange,
|
|
90
|
-
defaultQuery,
|
|
91
|
-
defaultView,
|
|
92
|
-
defaultPanelOpen = false,
|
|
93
|
-
onViewChange,
|
|
94
|
-
onSelectionChange,
|
|
95
|
-
onActiveIdChange,
|
|
96
|
-
theme,
|
|
97
|
-
className,
|
|
98
|
-
}: DataViewsRootProps) {
|
|
99
|
-
const childArray = Children.toArray(children);
|
|
100
|
-
|
|
101
|
-
// A view exists because you rendered it. `Children.toArray` drops `false`/`null`, so
|
|
102
|
-
// `{canSeeTree && <DataViews.Tree/>}` un-registers that view — which is the intent.
|
|
103
|
-
const viewElements = childArray.filter(isViewElement);
|
|
104
|
-
const headerEl = childArray.find(isHeaderElement);
|
|
105
|
-
const panelEl = childArray.find(isPanelElement);
|
|
106
|
-
// Anything the root does not position itself — a `Filters` bar, a toolbar of your own — sits
|
|
107
|
-
// between the header and the views, in the order you wrote it.
|
|
108
|
-
// LOCAL PATCH (Contact Center): the empty slot. It must be excluded from `extras` too --
|
|
109
|
-
// `extras` is the negative-space bucket, so without this the element would render twice:
|
|
110
|
-
// once above the view and once as the body.
|
|
111
|
-
const emptyEl = childArray.find(isEmptyElement);
|
|
112
|
-
const extras = childArray.filter(
|
|
113
|
-
(n) =>
|
|
114
|
-
!isViewElement(n) && !isHeaderElement(n) && !isPanelElement(n) && !isEmptyElement(n),
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
// `viewElements` is a fresh array on every render, so memoising on its identity would never
|
|
118
|
-
// hit. Key on what actually matters instead: which views are rendered and how they present.
|
|
119
|
-
// `icon` is part of that — leave it out and swapping only an icon leaves a stale one showing.
|
|
120
|
-
const viewKey = viewElements
|
|
121
|
-
.map((el) => `${el.props.id}|${el.props.label}|${iconKey(el.props.icon)}`)
|
|
122
|
-
.join(",");
|
|
123
|
-
const registered = useMemo<RegisteredView[]>(
|
|
124
|
-
() =>
|
|
125
|
-
viewElements.map((el) => {
|
|
126
|
-
const meta = viewMetaOf(el)!;
|
|
127
|
-
return {
|
|
128
|
-
id: el.props.id ?? meta.defaultId,
|
|
129
|
-
label: el.props.label ?? meta.defaultLabel,
|
|
130
|
-
icon: el.props.icon,
|
|
131
|
-
};
|
|
132
|
-
}),
|
|
133
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps -- keyed on `viewKey`, see above
|
|
134
|
-
[viewKey],
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
const firstId = registered[0]?.id ?? "";
|
|
138
|
-
|
|
139
|
-
// ── The query: the only state that leaves ──────────────────────────────────
|
|
140
|
-
const [currentQuery, setQuery] = useControllable<DataViewsQuery>(
|
|
141
|
-
query,
|
|
142
|
-
onQueryChange,
|
|
143
|
-
emptyQuery(defaultQuery),
|
|
144
|
-
);
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Change part of the query.
|
|
148
|
-
*
|
|
149
|
-
* Anything that changes *which* rows match starts a new result set, so the page goes back to 1 —
|
|
150
|
-
* filter down to three rows while sitting on page 4 and there is no page 4 to stay on. Only the
|
|
151
|
-
* component can do this: by the time a caller sees the change, the two have already been
|
|
152
|
-
* conflated into one object.
|
|
153
|
-
*/
|
|
154
|
-
const patchQuery = useCallback(
|
|
155
|
-
(patch: Partial<DataViewsQuery>) => {
|
|
156
|
-
setQuery((prev) => {
|
|
157
|
-
const changesResultSet =
|
|
158
|
-
("search" in patch && patch.search !== prev.search) ||
|
|
159
|
-
("filters" in patch && patch.filters !== prev.filters) ||
|
|
160
|
-
("sort" in patch && patch.sort !== prev.sort) ||
|
|
161
|
-
("pageSize" in patch && patch.pageSize !== prev.pageSize);
|
|
162
|
-
return { ...prev, ...patch, ...(changesResultSet ? { page: 1 } : null) };
|
|
163
|
-
});
|
|
164
|
-
},
|
|
165
|
-
[setQuery],
|
|
166
|
-
);
|
|
167
|
-
|
|
168
|
-
// ── UI state: never leaves, so it is held rather than passed ───────────────
|
|
169
|
-
const [currentView, setCurrentView] = useState(defaultView ?? firstId);
|
|
170
|
-
const [currentColumns, setColumns] = useState<readonly ColumnState[]>([]);
|
|
171
|
-
const [currentSelection, setSelectionState] = useState<readonly string[]>([]);
|
|
172
|
-
const [currentActiveId, setActiveIdState] = useState<string | null>(null);
|
|
173
|
-
const [isPanelOpen, setPanelOpen] = useState(defaultPanelOpen);
|
|
174
|
-
|
|
175
|
-
// The observers are told, not asked: they report what the component decided, so an app can act
|
|
176
|
-
// on a selection or follow the open row without owning either.
|
|
177
|
-
const setSelection = useCallback(
|
|
178
|
-
(ids: readonly string[]) => {
|
|
179
|
-
setSelectionState(ids);
|
|
180
|
-
onSelectionChange?.(ids);
|
|
181
|
-
},
|
|
182
|
-
[onSelectionChange],
|
|
183
|
-
);
|
|
184
|
-
const setActiveId = useCallback(
|
|
185
|
-
(id: string | null) => {
|
|
186
|
-
setActiveIdState(id);
|
|
187
|
-
onActiveIdChange?.(id);
|
|
188
|
-
},
|
|
189
|
-
[onActiveIdChange],
|
|
190
|
-
);
|
|
191
|
-
const setView = useCallback(
|
|
192
|
-
(id: string) => {
|
|
193
|
-
setCurrentView(id);
|
|
194
|
-
onViewChange?.(id);
|
|
195
|
-
},
|
|
196
|
-
[onViewChange],
|
|
197
|
-
);
|
|
198
|
-
|
|
199
|
-
// If the active view stops being rendered, converge on the first one — in an effect, never
|
|
200
|
-
// during render, so a controlled consumer's state actually catches up.
|
|
201
|
-
const known = registered.some((v) => v.id === currentView);
|
|
202
|
-
useEffect(() => {
|
|
203
|
-
if (!known && firstId) setView(firstId);
|
|
204
|
-
}, [known, firstId, setView]);
|
|
205
|
-
|
|
206
|
-
const activeView = known ? currentView : firstId;
|
|
207
|
-
const activeElement = viewElements.find(
|
|
208
|
-
(el) => (el.props.id ?? viewMetaOf(el)!.defaultId) === activeView,
|
|
209
|
-
);
|
|
210
|
-
|
|
211
|
-
const visibleFields = useMemo(
|
|
212
|
-
() => resolveVisibleFields(fields, currentColumns),
|
|
213
|
-
[fields, currentColumns],
|
|
214
|
-
);
|
|
215
|
-
|
|
216
|
-
const dataValue = useMemo(
|
|
217
|
-
() => ({
|
|
218
|
-
rows,
|
|
219
|
-
fields,
|
|
220
|
-
visibleFields,
|
|
221
|
-
getRowId,
|
|
222
|
-
loading,
|
|
223
|
-
loadingMore,
|
|
224
|
-
// Derived, never passed: the component has both halves already, and a `hasMore` prop that
|
|
225
|
-
// disagreed with the rows on screen would be a second source of truth for the same fact.
|
|
226
|
-
hasMore: rows.length < total,
|
|
227
|
-
onLoadMore,
|
|
228
|
-
}),
|
|
229
|
-
[rows, fields, visibleFields, getRowId, loading, loadingMore, total, onLoadMore],
|
|
230
|
-
);
|
|
231
|
-
|
|
232
|
-
const setSearch = useCallback((search: string) => patchQuery({ search }), [patchQuery]);
|
|
233
|
-
const setSort = useCallback((sort: Sort) => patchQuery({ sort }), [patchQuery]);
|
|
234
|
-
|
|
235
|
-
const viewValue = useMemo(
|
|
236
|
-
() => ({
|
|
237
|
-
view: activeView,
|
|
238
|
-
setView,
|
|
239
|
-
views: registered,
|
|
240
|
-
search: currentQuery.search,
|
|
241
|
-
setSearch,
|
|
242
|
-
sort: currentQuery.sort,
|
|
243
|
-
setSort,
|
|
244
|
-
selection: currentSelection,
|
|
245
|
-
setSelection,
|
|
246
|
-
activeId: currentActiveId,
|
|
247
|
-
setActiveId,
|
|
248
|
-
columns: currentColumns,
|
|
249
|
-
setColumns,
|
|
250
|
-
}),
|
|
251
|
-
[
|
|
252
|
-
activeView,
|
|
253
|
-
setView,
|
|
254
|
-
registered,
|
|
255
|
-
currentQuery.search,
|
|
256
|
-
setSearch,
|
|
257
|
-
currentQuery.sort,
|
|
258
|
-
setSort,
|
|
259
|
-
currentSelection,
|
|
260
|
-
setSelection,
|
|
261
|
-
currentActiveId,
|
|
262
|
-
setActiveId,
|
|
263
|
-
currentColumns,
|
|
264
|
-
setColumns,
|
|
265
|
-
],
|
|
266
|
-
);
|
|
267
|
-
|
|
268
|
-
const panelValue = useMemo(
|
|
269
|
-
() => ({ open: isPanelOpen, setOpen: setPanelOpen }),
|
|
270
|
-
[isPanelOpen, setPanelOpen],
|
|
271
|
-
);
|
|
272
|
-
|
|
273
|
-
// The descriptors as well as the value: a consumer such as `Filters.Presets` resolves a field by
|
|
274
|
-
// path, and may be rendered outside `Filters` — above the table, in a toolbar — where it cannot
|
|
275
|
-
// reach the context `Filters` provides to its own children.
|
|
276
|
-
const filterFields = useMemo(() => collectFilterFields(children), [children]);
|
|
277
|
-
|
|
278
|
-
const setFilters = useCallback(
|
|
279
|
-
(filters: FilterState) => patchQuery({ filters }),
|
|
280
|
-
[patchQuery],
|
|
281
|
-
);
|
|
282
|
-
|
|
283
|
-
const filtersValue = useMemo(
|
|
284
|
-
() => ({ filters: currentQuery.filters, setFilters, filterFields }),
|
|
285
|
-
[currentQuery.filters, setFilters, filterFields],
|
|
286
|
-
);
|
|
287
|
-
|
|
288
|
-
// The active view is what renders, unless a `DataViews.Empty` was supplied and the query has
|
|
289
|
-
// SETTLED on nothing — then the caller's empty state takes the view's place entirely.
|
|
290
|
-
//
|
|
291
|
-
// LOCAL PATCH (Contact Center). `!loading` is the whole reason this is safe: rows are empty
|
|
292
|
-
// while the first page is still in flight, so without it the empty state would replace the
|
|
293
|
-
// view's skeleton and announce "nothing matched" before anything had been asked for — the
|
|
294
|
-
// exact failure that made upstream ship no `Empty` at all (see `states.tsx`). Loading is still
|
|
295
|
-
// answered by the view's own skeleton; only a settled empty result swaps the body.
|
|
296
|
-
const body = emptyEl && !loading && rows.length === 0 ? emptyEl : activeElement;
|
|
297
|
-
|
|
298
|
-
return (
|
|
299
|
-
<DataContext.Provider value={dataValue}>
|
|
300
|
-
<ViewContext.Provider value={viewValue}>
|
|
301
|
-
<PanelContext.Provider value={panelValue}>
|
|
302
|
-
<FiltersContext.Provider value={filtersValue}>
|
|
303
|
-
<div
|
|
304
|
-
data-theme={theme}
|
|
305
|
-
className={cn(
|
|
306
|
-
// The shell is always black: the dark header and the config rail sit on it, and
|
|
307
|
-
// the Master Container is the light surface inside. `overflow-hidden` traps child
|
|
308
|
-
// overflow — without it a tall panel body escapes and adds a page-level scrollbar
|
|
309
|
-
// on top of the panel's own.
|
|
310
|
-
"flex h-full min-h-0 gap-2 overflow-hidden bg-black",
|
|
311
|
-
"text-content-presentation-global-primary",
|
|
312
|
-
className,
|
|
313
|
-
)}
|
|
314
|
-
>
|
|
315
|
-
{/* Left column: header + content. Shrinks as the panel expands. */}
|
|
316
|
-
<div className="flex min-w-0 flex-1 flex-col gap-2">
|
|
317
|
-
{headerEl}
|
|
318
|
-
<main className="flex min-h-0 flex-1 overflow-hidden">
|
|
319
|
-
{/* Master Container. It carries the surface — a form base and a 16px radius —
|
|
320
|
-
so views paint over it where they want a different colour (the board reads
|
|
321
|
-
grey, the split views black) and anything the root positions around one
|
|
322
|
-
inherits it rather than landing on the black shell.
|
|
323
|
-
|
|
324
|
-
No border: Figma draws one, but every view already brings its own edge — the
|
|
325
|
-
table and the split views each draw their own panel border — so it only ever
|
|
326
|
-
read as a second outline around the first. */}
|
|
327
|
-
<div className="bg-background-presentation-form-base flex flex-1 overflow-hidden rounded-[16px]">
|
|
328
|
-
{/* Clip the scrollable surface to the parent radius minus its 1px border
|
|
329
|
-
(16 − 1). At the full 16px the opaque view background sits flush with the
|
|
330
|
-
outer edge and bleeds past it as a hairline on the straight sides. */}
|
|
331
|
-
<div className="flex min-h-0 flex-1 flex-col overflow-hidden rounded-[15px]">
|
|
332
|
-
{/* Anything the root does not position itself — a `Filters` bar, a toolbar
|
|
333
|
-
of your own — sits above the view and *inside* the light surface, since
|
|
334
|
-
its controls are host-themed and would be stranded on the black shell. */}
|
|
335
|
-
{extras.length > 0 && (
|
|
336
|
-
<div className="bg-background-presentation-form-base shrink-0">{extras}</div>
|
|
337
|
-
)}
|
|
338
|
-
{/* A column, so the active view stretches to the full width. As a row it
|
|
339
|
-
would size each view to its content and leave the shell showing beside
|
|
340
|
-
a narrow one. */}
|
|
341
|
-
<div className="flex min-h-0 flex-1 flex-col overflow-auto">{body}</div>
|
|
342
|
-
</div>
|
|
343
|
-
</div>
|
|
344
|
-
</main>
|
|
345
|
-
</div>
|
|
346
|
-
|
|
347
|
-
{/* The rail is a sibling of the content, not a layer over it, so opening it narrows
|
|
348
|
-
the view rather than covering it. */}
|
|
349
|
-
{panelEl}
|
|
350
|
-
</div>
|
|
351
|
-
</FiltersContext.Provider>
|
|
352
|
-
</PanelContext.Provider>
|
|
353
|
-
</ViewContext.Provider>
|
|
354
|
-
</DataContext.Provider>
|
|
355
|
-
);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* DataViews — a compound, composition-based multi-view data surface. Author a screen as JSX:
|
|
360
|
-
*
|
|
361
|
-
* ```tsx
|
|
362
|
-
* <DataViews rows={rows} fields={fields} total={total} onQueryChange={setQuery}>
|
|
363
|
-
* <DataViews.Header title="Orders">
|
|
364
|
-
* <DataViews.ViewSwitch />
|
|
365
|
-
* <DataViews.Search />
|
|
366
|
-
* </DataViews.Header>
|
|
367
|
-
* <DataViews.Table />
|
|
368
|
-
* <DataViews.Board groups={groups} />
|
|
369
|
-
* </DataViews>
|
|
370
|
-
* ```
|
|
371
|
-
*
|
|
372
|
-
* A view exists **iff you render it** — there is no visibility map. DataViews is pure UI: it
|
|
373
|
-
* never filters, searches, sorts, groups or mutates. Hand it rows and it paints them; it hands
|
|
374
|
-
* you back what the user did so you can go and query.
|
|
375
|
-
*/
|
|
376
|
-
export const DataViews = Object.assign(DataViewsRoot, {
|
|
377
|
-
// shell
|
|
378
|
-
Header,
|
|
379
|
-
ViewSwitch,
|
|
380
|
-
Search,
|
|
381
|
-
Actions,
|
|
382
|
-
PanelToggle,
|
|
383
|
-
// panels
|
|
384
|
-
Panel,
|
|
385
|
-
Filters,
|
|
386
|
-
// views
|
|
387
|
-
Table: TableView,
|
|
388
|
-
Board: BoardView,
|
|
389
|
-
Inbox: InboxView,
|
|
390
|
-
Tree: TreeView,
|
|
391
|
-
Detail,
|
|
392
|
-
// states
|
|
393
|
-
Empty,
|
|
394
|
-
// paging
|
|
395
|
-
});
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import React from "react";
|
|
4
|
-
import { fieldKindOf } from "../../FormBuilder";
|
|
5
|
-
import type { FilterFieldDescriptor } from "../../../utils/dataViews/types";
|
|
6
|
-
import { toName } from "./values";
|
|
7
|
-
import { Labelled } from "./labelled";
|
|
8
|
-
|
|
9
|
-
/** How a FormBuilder field's value becomes a filter. Fields not listed here are not filters. */
|
|
10
|
-
const AS_FILTER = {
|
|
11
|
-
text: { kind: "text" } as const,
|
|
12
|
-
choice: { kind: "choice", single: true } as const,
|
|
13
|
-
multiChoice: { kind: "choice", single: false } as const,
|
|
14
|
-
date: { kind: "date" } as const,
|
|
15
|
-
slider: { kind: "number" } as const,
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Walk the children and describe every FormBuilder field among them.
|
|
20
|
-
*
|
|
21
|
-
* This is what `fields` used to be, except it is read from the controls you wrote rather than
|
|
22
|
-
* declared twice. The walk follows `props.children`, so a field stays visible through a plain
|
|
23
|
-
* wrapper; it cannot see through a component that builds the element itself, which is the same
|
|
24
|
-
* limitation the layout markers have.
|
|
25
|
-
*
|
|
26
|
-
* `Checkbox`, `Number`, `File` and friends have no filter meaning — a boolean is not a query and a
|
|
27
|
-
* file is not a value you can filter by — so they render but contribute nothing. `Filters.Custom`
|
|
28
|
-
* is the way to drive a filter no FormBuilder field covers.
|
|
29
|
-
*/
|
|
30
|
-
export function collectFilterFields(children: React.ReactNode): FilterFieldDescriptor[] {
|
|
31
|
-
const out: FilterFieldDescriptor[] = [];
|
|
32
|
-
const seen = new Set<string>();
|
|
33
|
-
|
|
34
|
-
const walk = (node: React.ReactNode) => {
|
|
35
|
-
if (Array.isArray(node)) {
|
|
36
|
-
node.forEach(walk);
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
if (!React.isValidElement(node)) return;
|
|
40
|
-
|
|
41
|
-
const props = node.props as {
|
|
42
|
-
name?: string;
|
|
43
|
-
label?: React.ReactNode;
|
|
44
|
-
min?: number;
|
|
45
|
-
max?: number;
|
|
46
|
-
children?: React.ReactNode;
|
|
47
|
-
};
|
|
48
|
-
const kind = fieldKindOf(node);
|
|
49
|
-
const as = kind ? AS_FILTER[kind as keyof typeof AS_FILTER] : undefined;
|
|
50
|
-
|
|
51
|
-
if (as && props.name && !seen.has(props.name)) {
|
|
52
|
-
seen.add(props.name);
|
|
53
|
-
out.push({
|
|
54
|
-
path: props.name,
|
|
55
|
-
label: typeof props.label === "string" ? props.label : undefined,
|
|
56
|
-
...as,
|
|
57
|
-
...(as.kind === "number" ? { min: props.min ?? 0, max: props.max ?? 100 } : null),
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
walk(props.children);
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
walk(children);
|
|
65
|
-
return out;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Render the children, with two changes to every field.
|
|
70
|
-
*
|
|
71
|
-
* The `name` is escaped, because RHF reads `customer.name` as object nesting — the author writes
|
|
72
|
-
* the real path and `FilterState` stays keyed by it. Anything calling `setValue` on a filter has
|
|
73
|
-
* to use `toName(path)` for the same reason.
|
|
74
|
-
*
|
|
75
|
-
* The `label` moves out of the field and into `Labelled` above it: `"bare"` mode exists precisely
|
|
76
|
-
* to drop `FieldSection`'s label row, so a label left on the field would simply vanish.
|
|
77
|
-
*/
|
|
78
|
-
export function renderFields(children: React.ReactNode): React.ReactNode {
|
|
79
|
-
return React.Children.map(children, (child) => {
|
|
80
|
-
if (!React.isValidElement(child)) return child;
|
|
81
|
-
|
|
82
|
-
const props = child.props as { name?: string; label?: React.ReactNode; children?: React.ReactNode };
|
|
83
|
-
|
|
84
|
-
if (fieldKindOf(child) && props.name) {
|
|
85
|
-
return (
|
|
86
|
-
<Labelled label={props.label}>
|
|
87
|
-
{React.cloneElement(child, { name: toName(props.name), label: undefined } as never)}
|
|
88
|
-
</Labelled>
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Not a field — recurse so a control inside a wrapper still gets its name escaped.
|
|
93
|
-
if (props.children) {
|
|
94
|
-
return React.cloneElement(child, {} as never, renderFields(props.children));
|
|
95
|
-
}
|
|
96
|
-
return child;
|
|
97
|
-
});
|
|
98
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { formatPathLabel } from "../../../utils/dataViews/path";
|
|
4
|
-
import type { FilterValue } from "../../../utils/dataViews/types";
|
|
5
|
-
import { useDataViewsFilters } from "../context";
|
|
6
|
-
import { Labelled } from "./labelled";
|
|
7
|
-
import type { FilterCustomProps } from "../types";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* `Filters.Custom` — the escape hatch. Renders whatever you return against this filter's slot in
|
|
11
|
-
* `FilterState`, bypassing the form entirely.
|
|
12
|
-
*
|
|
13
|
-
* Use it when no FormBuilder field produces the value you need — a bucket picker, a map region, a
|
|
14
|
-
* tri-state toggle.
|
|
15
|
-
*
|
|
16
|
-
* ```tsx
|
|
17
|
-
* <DataViews.Filters.Custom path="tags" render={({ value, setValue }) => (
|
|
18
|
-
* <MyTagPicker value={value as string[]} onChange={setValue} />
|
|
19
|
-
* )} />
|
|
20
|
-
* ```
|
|
21
|
-
*/
|
|
22
|
-
export function Custom({ path, label, render }: FilterCustomProps) {
|
|
23
|
-
const { filters, setFilters } = useDataViewsFilters();
|
|
24
|
-
const text = label ?? formatPathLabel(path);
|
|
25
|
-
|
|
26
|
-
const setValue = (next: FilterValue | undefined) => {
|
|
27
|
-
const copy = { ...filters };
|
|
28
|
-
if (next === undefined) delete copy[path];
|
|
29
|
-
else copy[path] = next;
|
|
30
|
-
setFilters(copy);
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
return <Labelled label={text}>{render({ value: filters[path], setValue })}</Labelled>;
|
|
34
|
-
}
|