torch-glare 2.4.4 → 2.5.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.
Files changed (147) hide show
  1. package/apps/lib/components/DataViews/{badgeAdapter.ts → badge.ts} +2 -2
  2. package/apps/lib/components/DataViews/cell.tsx +324 -0
  3. package/apps/lib/components/DataViews/context.ts +144 -0
  4. package/apps/lib/components/DataViews/data-views.tsx +383 -0
  5. package/apps/lib/components/DataViews/filters/children.tsx +98 -0
  6. package/apps/lib/components/DataViews/filters/custom.tsx +34 -0
  7. package/apps/lib/components/DataViews/filters/filters.tsx +163 -0
  8. package/apps/lib/components/DataViews/filters/index.ts +4 -0
  9. package/apps/lib/components/DataViews/filters/labelled.tsx +20 -0
  10. package/apps/lib/components/DataViews/filters/presets.tsx +65 -0
  11. package/apps/lib/components/DataViews/filters/summary.tsx +65 -0
  12. package/apps/lib/components/DataViews/filters/sync.tsx +35 -0
  13. package/apps/lib/components/DataViews/filters/values.ts +173 -0
  14. package/apps/lib/components/DataViews/header.tsx +217 -0
  15. package/apps/lib/components/DataViews/hooks/index.ts +5 -0
  16. package/apps/lib/components/DataViews/hooks/useActiveRow.ts +22 -0
  17. package/apps/lib/components/DataViews/hooks/useControllable.ts +52 -0
  18. package/apps/lib/components/DataViews/index.ts +74 -26
  19. package/apps/lib/components/DataViews/panel/columns.tsx +153 -0
  20. package/apps/lib/components/DataViews/panel/controls.tsx +106 -0
  21. package/apps/lib/components/DataViews/panel/index.ts +3 -0
  22. package/apps/lib/components/DataViews/panel/panel.tsx +164 -0
  23. package/apps/lib/components/DataViews/panel/saved-views.tsx +67 -0
  24. package/apps/lib/components/DataViews/panel/section.tsx +79 -0
  25. package/apps/lib/components/DataViews/panel/sort.tsx +42 -0
  26. package/apps/lib/components/DataViews/panel/tab.tsx +31 -0
  27. package/apps/lib/components/DataViews/slots.ts +63 -0
  28. package/apps/lib/components/DataViews/states.tsx +38 -0
  29. package/apps/lib/components/DataViews/types.ts +485 -178
  30. package/apps/lib/components/DataViews/views/board-view.tsx +379 -0
  31. package/apps/lib/components/DataViews/views/card-rows.tsx +36 -0
  32. package/apps/lib/components/DataViews/views/inbox-view.tsx +257 -0
  33. package/apps/lib/components/DataViews/views/pane-views.tsx +192 -0
  34. package/apps/lib/components/DataViews/views/table-view.tsx +426 -0
  35. package/apps/lib/components/DataViews/views/tree-view.tsx +365 -0
  36. package/apps/lib/components/FormBuilder/context.ts +20 -6
  37. package/apps/lib/components/FormBuilder/field-kind.ts +28 -0
  38. package/apps/lib/components/FormBuilder/fields/DateField.tsx +3 -3
  39. package/apps/lib/components/FormBuilder/fields/FieldShell.tsx +7 -6
  40. package/apps/lib/components/FormBuilder/fields/PhoneField.tsx +30 -4
  41. package/apps/lib/components/FormBuilder/fields/SelectField.tsx +7 -7
  42. package/apps/lib/components/FormBuilder/fields/TableField.tsx +80 -52
  43. package/apps/lib/components/FormBuilder/fields/TextField.tsx +9 -9
  44. package/apps/lib/components/FormBuilder/form-builder.tsx +66 -6
  45. package/apps/lib/components/FormBuilder/index.ts +3 -1
  46. package/apps/lib/components/FormBuilder/types.ts +40 -0
  47. package/apps/lib/components/Input.tsx +3 -0
  48. package/apps/lib/components/SearchableTable.tsx +5 -4
  49. package/apps/lib/components/SectionBlock.tsx +58 -11
  50. package/apps/lib/components/Select.tsx +3 -1
  51. package/apps/lib/components/TabSwitch.tsx +16 -4
  52. package/apps/lib/components/Table.tsx +265 -67
  53. package/apps/lib/components/TreeFolder/TreeFolder.tsx +6 -3
  54. package/apps/lib/components/TreeFolder/TreeFolderRow.tsx +16 -14
  55. package/apps/lib/components/TreeFolder/index.ts +1 -1
  56. package/apps/lib/components/TreeFolder/useTreeFolderDnD.ts +70 -207
  57. package/apps/lib/hooks/useDragDrop.tsx +365 -0
  58. package/apps/lib/hooks/useInfiniteScroll.ts +108 -0
  59. package/apps/lib/registry.json +159 -4
  60. package/apps/lib/tsconfig.tsbuildinfo +1 -1
  61. package/apps/lib/utils/dataViews/path.ts +67 -0
  62. package/apps/lib/utils/dataViews/query.ts +73 -0
  63. package/apps/lib/utils/dataViews/types.ts +187 -0
  64. package/docs/components/breadcrumb.md +1 -1
  65. package/docs/components/button-group.md +1 -1
  66. package/docs/components/button.md +1 -1
  67. package/docs/components/card.md +1 -1
  68. package/docs/components/checkbox.md +1 -1
  69. package/docs/components/data-views/backend-response.md +324 -0
  70. package/docs/components/data-views/examples/a11y-rtl.md +250 -0
  71. package/docs/components/data-views/examples/api-orders-route.md +130 -0
  72. package/docs/components/data-views/examples/fields.md +362 -0
  73. package/docs/components/data-views/examples/filters.md +308 -0
  74. package/docs/components/data-views/examples/inbox-routing.md +218 -0
  75. package/docs/components/data-views/examples/index.md +29 -0
  76. package/docs/components/data-views/examples/overview.md +244 -0
  77. package/docs/components/data-views/examples/panel.md +212 -0
  78. package/docs/components/data-views/examples/scale.md +231 -0
  79. package/docs/components/data-views/examples/server-side.md +210 -0
  80. package/docs/components/data-views/examples/state.md +250 -0
  81. package/docs/components/data-views/examples/tree-custom.md +388 -0
  82. package/docs/components/data-views/examples/view-registry.md +313 -0
  83. package/docs/components/data-views/examples/views.md +534 -0
  84. package/docs/components/data-views/guide.md +405 -0
  85. package/docs/components/data-views/index.md +1504 -0
  86. package/docs/components/data-views/migration.md +79 -0
  87. package/docs/components/date-picker.md +0 -1
  88. package/docs/components/form-builder.md +19 -8
  89. package/docs/components/form-renderer.md +2 -1
  90. package/docs/components/form.md +1 -1
  91. package/docs/components/input-field.md +1 -1
  92. package/docs/components/input-otp.md +1 -1
  93. package/docs/components/input.md +1 -1
  94. package/docs/components/labeled-check-box.md +1 -1
  95. package/docs/components/labeled-radio.md +1 -1
  96. package/docs/components/radio-card.md +1 -1
  97. package/docs/components/radio.md +1 -1
  98. package/docs/components/search-field.md +1 -1
  99. package/docs/components/section-block.md +79 -3
  100. package/docs/components/select.md +1 -1
  101. package/docs/components/simple-select.md +1 -1
  102. package/docs/components/switch.md +1 -1
  103. package/docs/components/tab-switch.md +1 -1
  104. package/docs/components/table.md +45 -8
  105. package/docs/components/text-editor.md +1 -1
  106. package/docs/components/textarea.md +1 -1
  107. package/docs/components/toggle-button.md +1 -1
  108. package/docs/components/toggle.md +1 -1
  109. package/docs/components/tree-folder.md +110 -0
  110. package/docs/how-to/forms-with-form-builder.md +6 -4
  111. package/docs/reference/components.md +16 -6
  112. package/docs/tutorials/component-composition.md +11 -13
  113. package/package.json +3 -2
  114. package/apps/lib/components/DataViews/DataViewRadio.tsx +0 -49
  115. package/apps/lib/components/DataViews/DataViewsConfigPanel.tsx +0 -393
  116. package/apps/lib/components/DataViews/DataViewsHeader.tsx +0 -207
  117. package/apps/lib/components/DataViews/DataViewsLayout.tsx +0 -332
  118. package/apps/lib/components/DataViews/FilterPanel.tsx +0 -493
  119. package/apps/lib/components/DataViews/HeaderSearch.tsx +0 -93
  120. package/apps/lib/components/DataViews/InboxView.tsx +0 -463
  121. package/apps/lib/components/DataViews/InboxViewCard.tsx +0 -127
  122. package/apps/lib/components/DataViews/KanbanView.tsx +0 -336
  123. package/apps/lib/components/DataViews/PanelControls.tsx +0 -39
  124. package/apps/lib/components/DataViews/SettingsPanel.tsx +0 -279
  125. package/apps/lib/components/DataViews/TableView.tsx +0 -212
  126. package/apps/lib/components/DataViews/TreeView.tsx +0 -364
  127. package/apps/lib/components/DataViews/fieldRenderers.tsx +0 -299
  128. package/apps/lib/components/DataViews/filters/DatePickerRangeFilter.tsx +0 -87
  129. package/apps/lib/components/DataViews/filters/DateRangePopover.tsx +0 -120
  130. package/apps/lib/components/DataViews/filters/PresetChips.tsx +0 -45
  131. package/apps/lib/components/DataViews/filters/RangeSliderWithInputs.tsx +0 -165
  132. package/apps/lib/components/DataViews/tree/TreeDrawer.tsx +0 -50
  133. package/apps/lib/components/DataViews/tree/TreeSidebar.tsx +0 -74
  134. package/apps/lib/hooks/useDataViewsState.ts +0 -175
  135. package/apps/lib/utils/dataViews/columnUtils.ts +0 -132
  136. package/apps/lib/utils/dataViews/fieldUtils.ts +0 -197
  137. package/apps/lib/utils/dataViews/nestedDataUtils.tsx +0 -371
  138. package/apps/lib/utils/dataViews/pathUtils.ts +0 -139
  139. package/apps/lib/utils/dataViews/rangeUtils.ts +0 -234
  140. package/apps/lib/utils/dataViews/treeUtils.ts +0 -396
  141. package/docs/components/data-views-config-panel.md +0 -208
  142. package/docs/components/data-views-layout.md +0 -291
  143. package/docs/components/inbox-view.md +0 -170
  144. package/docs/components/kanban-view.md +0 -135
  145. package/docs/components/table-view.md +0 -141
  146. package/docs/components/tree-view.md +0 -147
  147. package/docs/how-to/data-views-from-backend-response.md +0 -194
@@ -1,208 +0,0 @@
1
- ---
2
- title: DataViewsConfigPanel
3
- description: Slide-in side panel for DataViews — Saved View list, show/hide & drag-reorder columns, default sort, and a Filters tab. Used automatically by DataViewsLayout, or rendered standalone in composable mode.
4
- group: Data Display
5
- keywords: [data-views, config panel, config-panel, settings panel, saved view, saved-view, column visibility, reorder columns, default sort, filters tab, side panel, dark panel]
6
- ---
7
-
8
- # DataViewsConfigPanel
9
-
10
- > The settings/filters side panel for DataViews. In tab mode `DataViewsLayout`
11
- > mounts and animates this for you. Render it yourself only in composable mode
12
- > (custom layouts) or when you need real Saved View persistence.
13
-
14
- ## Installation
15
-
16
- TORCH Glare is a copy-in library: the CLI copies this component's source into your project
17
- (you do **not** install it from the npm package). Run `init` once, then `add`:
18
-
19
- ```bash
20
- npx torch-glare@latest init
21
- npx torch-glare@latest add DataViewsConfigPanel
22
- ```
23
-
24
- `add` also copies any components, hooks, and utilities that `DataViewsConfigPanel` depends on.
25
-
26
- ## Import
27
-
28
- Import from your project's local path — the alias configured in `glare.json` (e.g. `@/*`):
29
-
30
- ```tsx
31
- import { DataViewsConfigPanel } from "@/components/DataViewsConfigPanel";
32
- ```
33
-
34
- ## When to use it directly
35
-
36
- | Situation | Use |
37
- |---|---|
38
- | Standard tabbed dashboard | **Don't.** Use `DataViewsLayout` — it renders this panel for you via the header settings cog. |
39
- | Custom composed layout (e.g. Table + Kanban side by side) | Render `DataViewsConfigPanel` yourself alongside `useDataViewsState`. |
40
- | You need working Saved View persistence | Render it yourself and pass `savedViews` + `onSavedViewChange` + `onSaveNewView` wired to your store. (Not possible through `DataViewsLayout` today — see "Saved View status".) |
41
-
42
- ## Standalone Example (composable mode)
43
-
44
- ```tsx
45
- import { DataViewsConfigPanel } from "@/components/DataViewsConfigPanel";
46
- import { TableView } from "@/components/TableView";
47
- import { useDataViewsState } from "@/hooks/useDataViewsState";
48
- import { useState } from "react"
49
-
50
- function CustomScreen({ data, fields }) {
51
- const s = useDataViewsState({ data, fields })
52
- const [panelOpen, setPanelOpen] = useState(true)
53
-
54
- // Your own saved-view persistence:
55
- const [savedViews] = useState([
56
- { id: "all", label: "All Records" },
57
- { id: "mine", label: "Assigned to Me" },
58
- ])
59
- const [activeView, setActiveView] = useState("all")
60
-
61
- return (
62
- <div className="flex h-screen gap-2 bg-black p-2">
63
- <div className="min-w-0 flex-1">
64
- <TableView
65
- data={s.flatItems}
66
- fields={s.resolvedFields}
67
- config={s.config}
68
- filterState={s.filterState}
69
- onFilterChange={s.setFilterState}
70
- showFilters={false}
71
- />
72
- </div>
73
-
74
- {panelOpen && (
75
- <DataViewsConfigPanel
76
- state="open"
77
- config={s.config}
78
- onConfigChange={s.setConfig}
79
- onClose={() => setPanelOpen(false)}
80
- currentView={s.currentView}
81
- fields={s.resolvedFields}
82
- data={s.flatItems}
83
- filterState={s.filterState}
84
- onFilterChange={s.setFilterState}
85
- savedViews={savedViews}
86
- activeSavedView={activeView}
87
- onSavedViewChange={setActiveView}
88
- onSaveNewView={() => {
89
- /* open your "name this view" modal, then persist */
90
- }}
91
- />
92
- )}
93
- </div>
94
- )
95
- }
96
- ```
97
-
98
- ## API Reference
99
-
100
- ### `DataViewsConfigPanelProps`
101
-
102
- | Prop | Type | Required | Description |
103
- |---|---|---|---|
104
- | `config` | `ViewConfig` | ✅ | Current view config. `tableColumns` drives the column list; `sortBy`/`sortOrder` drive Default Sort. |
105
- | `onConfigChange` | `(config: Partial<ViewConfig>) => void` | ✅ | Called with a partial patch when columns are toggled/reordered or a sort field is picked. Merge it into your config state. |
106
- | `onClose` | `() => void` | ✅ | Fires when the panel's close (X) button is pressed. |
107
- | `currentView` | `ViewType` | ✅ | **Currently unused inside the panel body** (declared on the prop type but not read). Still required by the type; pass the active view for forward-compatibility. |
108
- | `fields` | `FieldConfig[]` | ✅ | Same field map you pass to the views. Drives the Filters tab and the column labels. |
109
- | `data` | `DynamicRecord[]` | ✅ | Flat records — passed to the Filters tab (`FilterPanel`) to compute filter options. |
110
- | `filterState` | `FilterState` | ✅ | Current filter values. The Filters tab reads/writes this. |
111
- | `onFilterChange` | `(filters: FilterState) => void` | ✅ | Called with the full next filter object on any filter change (and on "Clear all" → `{}`). |
112
- | `filterConfig` | `DynamicFilterConfig[]` | — | Optional explicit filter config forwarded to `FilterPanel`. |
113
- | `savedViews` | `{ id: string; label: string }[]` | — | Saved View radio list. Defaults to a single `{ id: "default", label: "Default View" }`. |
114
- | `activeSavedView` | `string` | — | Controlled selected saved-view id. If supplied, the panel is controlled (see "Controlled vs uncontrolled"). |
115
- | `onSavedViewChange` | `(id: string) => void` | — | Called when a saved-view radio is selected. Required for controlled behaviour. |
116
- | `onSaveNewView` | `() => void` | — | Called when "Save a New View" is clicked. No-op if omitted. |
117
- | `state` | `"open" \| "closed"` | — | Drives the slide/opacity animation. Default `"open"`. Keep the panel mounted through the close animation, then unmount. |
118
-
119
- > Note: `DataViewsConfigPanelProps` is a plain `type` (not extending HTML
120
- > attributes). There is no `className`/`theme` passthrough — the panel is
121
- > intentionally always-dark chrome (see "Theming").
122
-
123
- ### Controlled vs uncontrolled Saved View
124
-
125
- The Saved View list follows the standard controlled/uncontrolled pattern:
126
-
127
- - **Controlled** — pass both `activeSavedView` and `onSavedViewChange`. You own
128
- the selected id; the panel reflects it.
129
- - **Uncontrolled** — omit them. The panel keeps internal state (initialised to
130
- `savedViews[0]?.id`) so the radios are still interactive, but nothing
131
- persists and it resets on unmount.
132
-
133
- ```tsx
134
- // Controlled
135
- <DataViewsConfigPanel activeSavedView={id} onSavedViewChange={setId} ... />
136
-
137
- // Uncontrolled (still clickable, local only)
138
- <DataViewsConfigPanel savedViews={views} ... />
139
- ```
140
-
141
- ### Saved View status
142
-
143
- Through `DataViewsLayout` (tab mode) the four `savedView*` props are **not
144
- forwarded**, so Saved View there is presentational only. To get real behaviour,
145
- render `DataViewsConfigPanel` yourself (example above) or extend the layout
146
- (below).
147
-
148
- ## Sections
149
-
150
- | Section | Backed by | Behaviour |
151
- |---|---|---|
152
- | Saved View | `savedViews` / `activeSavedView` | Radio list + "Save a New View" button. |
153
- | Table Columns | `config.tableColumns` | Green `Switch` per column toggles `visible`; rows are drag-reorderable (HTML5 DnD) and patch `order`. |
154
- | Default Sort | `config.sortBy` | Single-choice radio; selecting sets `sortBy`. Direction stays on `config.sortOrder`. |
155
- | Filters tab | `filterState` + `fields` | Renders `FilterPanel` restyled full-width/transparent. Categorical fields render as checkbox/radio lists, or a `SearchableSelect` dropdown when a field sets `filterVariant: "searchable-select"`. |
156
-
157
- ## Internal: PanelControls
158
-
159
- The panel's radio rows and column toggle are a colocated, **non-exported**
160
- file: `DataViews/PanelControls.tsx` (`RadioRow`, `DataViewsSwitch`). They are
161
- deliberately **not** shared library components — the panel chrome is always
162
- dark and hardcodes Figma hex values, which conflicts with the design-system
163
- token / `data-theme` convention used by public components.
164
-
165
- You normally never import these. They ship automatically because the CLI
166
- copies the entire `DataViews/` folder recursively, so the relative import
167
- `./PanelControls` resolves in the consumer's copy with no registry wiring.
168
-
169
- If you need a themed radio elsewhere, use the shared `Radio` component instead
170
- — not `PanelControls`.
171
-
172
- ## Extending the panel
173
-
174
- Common changes and where to make them:
175
-
176
- | You want to… | Do this |
177
- |---|---|
178
- | Make Saved View work through `DataViewsLayout` | Add `savedViews` / `activeSavedView` / `onSavedViewChange` / `onSaveNewView` to `DataViewsLayoutProps`, hold them in layout state (or accept from the host), and forward them to `<DataViewsConfigPanel>` at its render site in `DataViewsLayout.tsx`. |
179
- | Add a new Config section | Add a new block inside the Config. tab in `DataViewsConfigPanel.tsx`, backed by a `config.*` field so `onConfigChange` persists it. |
180
- | Restyle a radio/toggle | Edit the radio ring in `DataViewRadio.tsx` (the `RadioRow` in `PanelControls.tsx` just wraps it) or the switch in `PanelControls.tsx`. Keep the hardcoded hex matching the Figma spec; do not swap in the shared `Radio`/`Label` (they impose theming/layout that fights the dark panel — this was deliberate). |
181
- | Change the dark chrome | The root forces `data-theme="dark"` and uses hardcoded hex (`#1C1D1F`, `#252729`, `#005ECC`, `#0075FF`, `#626467`, `#0AC713`). These are intentional Figma values, not tokens — the radio/checkbox rings hardcode them (`#626467` border, `rgba(255,255,255,0.05)` fill, `#0075FF` selected) so the panel always renders dark regardless of host `data-theme`. |
182
-
183
- After any change to the panel docs or component, update this file and rebuild
184
- the MCP server (`cd mcp && pnpm build`) so the docs the server serves stay in
185
- sync.
186
-
187
- ## Accessibility
188
-
189
- - Saved View / Default Sort use Radix `RadioGroup`; the whole row is the click
190
- target with keyboard support.
191
- - Column toggles are accessible `Switch`es; reorder is pointer-based HTML5 DnD
192
- (provide a non-DnD path if your audience needs keyboard reordering).
193
- - The close button has `aria-label="Close panel"`; tab buttons expose
194
- `aria-pressed`.
195
-
196
- ## Theming
197
-
198
- Intentionally **always dark**. The root sets `data-theme="dark"` so child
199
- themed components resolve dark tokens even when the host app runs light, and
200
- the panel-specific chrome uses hardcoded Figma hex values rather than design
201
- tokens. There is no `theme` prop — this is by design to match the Figma
202
- "Cun" (#000000) panel spec.
203
-
204
- ## Related
205
-
206
- - [`DataViewsLayout`](./data-views-layout.md) — renders this panel for you in tab mode
207
- - [`Radio`](./radio.md) — the themed radio to use **outside** this panel
208
- - [`Switch`](./switch.md) — the shared switch the column toggles wrap
@@ -1,291 +0,0 @@
1
- ---
2
- title: DataViewsLayout
3
- description: Composable multi-view layout that renders any backend response as Table, Kanban, Inbox, and/or Tree, with per-screen selection of which views to show.
4
- group: Data Display
5
- keywords: [data-views, layout, table, kanban, inbox, tree, multi-view, filter, switcher, dashboard, dynamic-data, fields]
6
- ---
7
-
8
- # DataViewsLayout
9
-
10
- > One backend response → many UI shapes. Pass an array of records plus a declarative list of fields, then pick which views the screen needs (`{ table: true, kanban: true }`, etc.). Filters and the settings cog are togglable per-screen.
11
-
12
- ## Installation
13
-
14
- TORCH Glare is a copy-in library: the CLI copies this component's source into your project
15
- (you do **not** install it from the npm package). Run `init` once, then `add`:
16
-
17
- ```bash
18
- npx torch-glare@latest init
19
- npx torch-glare@latest add DataViewsLayout
20
- ```
21
-
22
- `add` also copies any components, hooks, and utilities that `DataViewsLayout` depends on.
23
-
24
- ## Import
25
-
26
- Import from your project's local path — the alias configured in `glare.json` (e.g. `@/*`):
27
-
28
- ```tsx
29
- import { DataViewsLayout } from "@/components/DataViewsLayout";
30
- ```
31
-
32
- ## Quick Examples
33
-
34
- ### 1. All views, auto-detected
35
-
36
- Just pass `data` — every primitive field becomes a column, all four views appear, the Tree tab auto-hides if no `children`/`parentId` is found.
37
-
38
- ```tsx
39
- const employees = [
40
- { id: 1, name: "Ada Lovelace", role: "Engineer", salary: 120000, joinDate: "2024-04-12" },
41
- { id: 2, name: "Linus Torvalds", role: "Engineer", salary: 145000, joinDate: "2023-09-01" },
42
- ]
43
-
44
- <DataViewsLayout title="Employees" data={employees} />
45
- ```
46
-
47
- ### 2. Pick specific views only
48
-
49
- Use `views` to enable only what this screen needs. Tabs for the others are hidden.
50
-
51
- ```tsx
52
- <DataViewsLayout
53
- title="Orders"
54
- data={orders}
55
- views={{ table: true, kanban: true }} // only Table + Kanban tabs render
56
- kanbanGroupBy="status"
57
- />
58
- ```
59
-
60
- ### 3. Declarative fields with badges and filters
61
-
62
- ```tsx
63
- const fields: FieldConfig[] = [
64
- { path: "id", label: "Order #", type: "number" },
65
- { path: "customer", type: "text" },
66
- {
67
- path: "status",
68
- type: "enum-badge",
69
- variants: { Pending: "yellow", Shipped: "blue", Delivered: "green" },
70
- filterable: true,
71
- },
72
- { path: "total", type: "currency", currency: "USD", filterable: true },
73
- { path: "createdAt", type: "date-format", dateFormat: "YYYY-MM-DD", filterable: true },
74
- ]
75
-
76
- <DataViewsLayout
77
- title="Orders"
78
- data={orders}
79
- fields={fields}
80
- views={{ table: true, kanban: true }}
81
- kanbanGroupBy="status"
82
- />
83
- ```
84
-
85
- ### 4. Disable filters and the settings cog
86
-
87
- ```tsx
88
- <DataViewsLayout
89
- data={orders}
90
- fields={fields}
91
- views={{ table: true }}
92
- showFilters={false}
93
- showSettings={false}
94
- />
95
- ```
96
-
97
- ### 5. Controlled filter state (wire to backend pagination)
98
-
99
- ```tsx
100
- const [filterState, setFilterState] = useState<FilterState>({})
101
-
102
- useEffect(() => {
103
- fetchFromBackend({ filters: filterState }).then(setRows)
104
- }, [filterState])
105
-
106
- <DataViewsLayout
107
- data={rows}
108
- fields={fields}
109
- filterState={filterState}
110
- onFilterChange={setFilterState}
111
- />
112
- ```
113
-
114
- ### 6. Hierarchical data
115
-
116
- When records carry a `children: []` array (or a `parentId` reference), the Tree tab auto-appears. Override the detection with `treeConfig`.
117
-
118
- ```tsx
119
- <DataViewsLayout
120
- data={departments} // each has children[]
121
- treeConfig={{ childrenField: "children", nodeLabel: "name", defaultExpanded: "roots" }}
122
- fields={fields}
123
- />
124
- ```
125
-
126
- ### 7. Inbox shape (read/starred/priority)
127
-
128
- Inbox auto-detects `isRead`, `isStarred`, `hasAttachment`, `priority`. Override with `inboxConfig`.
129
-
130
- ```tsx
131
- <DataViewsLayout
132
- data={messages}
133
- fields={fields}
134
- views={{ inbox: true }}
135
- inboxConfig={{ titlePath: "subject", previewPath: "from.name" }}
136
- />
137
- ```
138
-
139
- ## API Reference
140
-
141
- ### `DataViewsLayoutProps`
142
-
143
- | Prop | Type | Default | Description |
144
- |---|---|---|---|
145
- | `data` | `DynamicRecord[]` | `[]` | Array of records of any shape. Fields are auto-detected from the first records. |
146
- | `fields` | `FieldConfig[]` | auto-detected | Declarative field map: `path`, `type`, `filterable`, `variants`, `currency`, etc. |
147
- | `title` | `string` | `"Data Views"` | Header title (hidden when `showTitle={false}`). |
148
- | `description` | `string` | `"Unified data visualization across multiple views"` | Subtitle under the title. |
149
- | `views` | `ViewVisibility` | all on (tree auto) | Per-view toggle: `{ table?, kanban?, inbox?, tree? }`. Omitted keys default to `true` (Tree auto-hides without hierarchy). |
150
- | `kanbanGroupBy` | `string` | `"status"` | Dot-path to the field used for Kanban columns. |
151
- | `inboxConfig` | `InboxConfig` | auto-detected | Map of starred/read/attachment/priority field paths for Inbox. |
152
- | `inboxItemHref` | `(item, id) => string` | — | When set, each Inbox row becomes a link to the returned href. |
153
- | `inboxLinkComponent` | `ElementType` | `"a"` | Component used to render Inbox item links when `inboxItemHref` is set. Pass your router's link (Next.js `Link`, React Router `Link`) for client-side navigation; defaults to a plain `<a>` (full-page nav). |
154
- | `treeConfig` | `TreeConfig` | auto-detected | `childrenField`, `parentField`, `idField`, `nodeLabel`, `defaultExpanded`. |
155
- | `filterState` | `FilterState` | uncontrolled | Controlled filter state. Pair with `onFilterChange`. |
156
- | `onFilterChange` | `(state: FilterState) => void` | — | Fires when any filter changes. When provided, the layout is controlled. |
157
- | `showFilters` | `boolean` | `true` | Hide the filter panel inside every view. |
158
- | `showSettings` | `boolean` | `true` | Hide the settings cog + side panel. |
159
- | `showTitle` | `boolean` | `true` | Hide the header bar entirely. Useful when embedding. |
160
- | `config` | `Partial<ViewConfig>` | — | Initial config: `defaultView`, `sortBy`, `sortOrder`, etc. |
161
- | `className` | `string` | — | Forwarded to the root `<div>`. |
162
- | `theme` | `"dark" \| "light" \| "default"` | — | Applied as `data-theme` on the root. |
163
-
164
- ### `FieldConfig`
165
-
166
- | Prop | Type | Description |
167
- |---|---|---|
168
- | `path` | `string` | Dot-path into the record (`"contact.email"`). |
169
- | `label` | `string` | Display label. Auto-formatted from path tail if omitted. |
170
- | `type` | `FieldType` | Renderer key (see below). Auto-inferred if omitted. |
171
- | `visible` | `boolean` | Show in cells. Default `true`. |
172
- | `order` | `number` | Display order. |
173
- | `filterable` | `boolean` | Surface this field in the filter panel. The control adapts to the field `type`: categorical fields render checkboxes/radios (or a searchable dropdown via `filterVariant`), numeric fields a range slider, and **date / date-format fields a From + To pair of Glare `DatePicker`s** (two single-date pickers bounding the range). Set `false` to explicitly exclude a field the panel would otherwise auto-detect (e.g. an `id` or `name` with few unique values). |
174
- | `filterLabel` | `string` | Override the label shown above this field's filter (defaults to `label`). |
175
- | `filterMode` | `"single" \| "multi"` | Categorical selection mode. `"multi"` (default) renders checkboxes; `"single"` renders radios. |
176
- | `filterVariant` | `"checkbox" \| "searchable-select"` | Categorical control style. `"checkbox"` (default) is the inline checkbox/radio list; `"searchable-select"` renders a single-select `SearchableSelect` dropdown — useful when a field has many options. Implies single-select. |
177
- | `filterOptions` | `string[] \| { label: string; value: string }[]` | Explicit option list for the categorical filter (otherwise options are collected from the data). |
178
- | `variants` | `Record<string, BadgeVariant>` | For `enum-badge`: per-value color map. |
179
- | `currency` | `string \| CurrencyOptions` | For `currency`: ISO code or `{ symbol, locale, decimals, code }`. |
180
- | `thresholds` | `[number, number]` | For `progress-bar`: warning/ok thresholds. |
181
- | `max` | `number` | For `star-rating`: stars displayed. |
182
- | `dateFormat` | `string \| Intl.DateTimeFormatOptions` | For `date-format`: token like `"YYYY-MM-DD"` or Intl options. |
183
- | `render` | `(value, row) => ReactNode` | Escape hatch for custom JSX. |
184
-
185
- ### `FieldType` (built-in renderers)
186
-
187
- `text` · `number` · `date` · `date-format` · `boolean` · `currency` · `number-format` · `enum-badge` · `badge-array` · `progress-bar` · `star-rating` · `icon-text` · `two-line` · `avatar` · `link` · `image` · `hidden`
188
-
189
- > **`hidden` vs `filterable: false`** — use `type: "hidden"` to drop a field from the UI **entirely** (no column, no column-toggle in the config panel, no filter) while it stays in the data for row identity — e.g. an `id` you key rows by but never want shown. Use `filterable: false` to keep a field as a **column** but remove only its **filter**.
190
-
191
- ### `BadgeVariant`
192
-
193
- `green` · `greenLight` · `cocktailGreen` · `yellow` · `redOrange` · `redLight` · `rose` · `purple` · `bluePurple` · `blue` · `navy` · `gray` · `highlight`
194
-
195
- ### `ViewVisibility`
196
-
197
- ```ts
198
- type ViewVisibility = {
199
- table?: boolean
200
- kanban?: boolean
201
- inbox?: boolean
202
- tree?: boolean
203
- }
204
- ```
205
-
206
- ## Config & Filters Panel
207
-
208
- The settings cog in the header (shown unless `showSettings={false}`) opens a
209
- slide-in side panel — [`DataViewsConfigPanel`](./data-views-config-panel.md).
210
- `DataViewsLayout` mounts and animates it for you; you do not render it yourself
211
- in tab mode. It has two tabs:
212
-
213
- | Tab | Section | What it does | Wired through `DataViewsLayout`? |
214
- |---|---|---|---|
215
- | **Config.** | Saved View | Radio list of named views + "Save a New View" button. | ❌ **Presentational only.** The layout does not pass `savedViews` / `onSavedViewChange` / `onSaveNewView`, so the panel shows a single fallback "Default View" and selection is local-state only (it does not persist or change `config`). See "Saved View status" below. |
216
- | **Config.** | Table Columns | Show/hide each column (green `Switch`) and drag-reorder them. | ✅ Reads/writes `config.tableColumns` via the layout's internal config state. |
217
- | **Config.** | Default Sort | Single-choice radio list that sets `config.sortBy`. | ✅ Writes `config.sortBy` (sort direction stays on `config.sortOrder`). |
218
- | **Filters** | — | Delegates to `FilterPanel` for the same `fields` you passed. | ✅ Reads/writes the layout's `filterState` (and your `onFilterChange` if controlled). |
219
-
220
- ### Saved View status (read this before relying on it)
221
-
222
- Saved View is currently a **presentational shell**. The props exist on
223
- `DataViewsConfigPanel` (`savedViews`, `activeSavedView`, `onSavedViewChange`,
224
- `onSaveNewView`) but `DataViewsLayout` does **not** thread them through. In tab
225
- mode you therefore cannot supply or persist saved views today — clicking a row
226
- updates the panel's internal state only and resets on unmount.
227
-
228
- To get real saved-view behaviour you have two options:
229
-
230
- 1. Use **Composable Mode** (below) and render `DataViewsConfigPanel` yourself,
231
- passing `savedViews` + `onSavedViewChange` wired to your own persistence.
232
- 2. Extend `DataViewsLayout` to forward the four `savedView*` props down to the
233
- panel (see "Extending the panel" in the
234
- [`DataViewsConfigPanel` doc](./data-views-config-panel.md)).
235
-
236
- This limitation is documented honestly so an agent does not generate code that
237
- passes `savedViews` to `DataViewsLayout` expecting it to work.
238
-
239
- ## Composable Mode (no tabs)
240
-
241
- When you want a custom layout (e.g. Table on the left, Kanban on the right), bypass `DataViewsLayout` and compose the views directly.
242
-
243
- ```tsx
244
- import { FilterPanel } from "@/components/FilterPanel";
245
- import { KanbanView } from "@/components/KanbanView";
246
- import { TableView } from "@/components/TableView";
247
- import { useDataViewsState } from "@/hooks/useDataViewsState";
248
-
249
- function CustomScreen({ data, fields }: Props) {
250
- const state = useDataViewsState({ data, fields })
251
- return (
252
- <div className="grid grid-cols-2 gap-4 h-screen">
253
- <TableView
254
- data={state.flatItems}
255
- fields={state.resolvedFields}
256
- config={state.config}
257
- filterState={state.filterState}
258
- onFilterChange={state.setFilterState}
259
- showFilters={false}
260
- />
261
- <KanbanView
262
- data={state.flatItems}
263
- fields={state.resolvedFields}
264
- config={state.config}
265
- groupByField="status"
266
- />
267
- </div>
268
- )
269
- }
270
- ```
271
-
272
- ## Accessibility
273
-
274
- - The view-switcher uses [`TabSwitch`](./tab-switch.md) — a segmented `role="tablist"` control (each view a `role="tab"` button, full keyboard support via Tab/Enter/Space). Installing DataViews pulls in `TabSwitch` automatically.
275
- - Tree rows expose `role="treeitem"` with `aria-expanded` and `aria-selected`.
276
- - Filter checkboxes carry labels and `htmlFor` linkage.
277
- - Settings panel buttons have `aria-pressed` for sort direction.
278
-
279
- ## Theming
280
-
281
- The component uses only `*-presentation-*` design tokens. Wrap with `ThemeProvider` or pass `theme="dark" | "light" | "default"` to control color scheme.
282
-
283
- ## Related
284
-
285
- - [`DataViewsConfigPanel`](./data-views-config-panel.md) — the settings/filters side panel (Saved View, columns, sort)
286
- - [`TableView`](./table-view.md) — standalone table
287
- - [`KanbanView`](./kanban-view.md) — standalone kanban
288
- - [`InboxView`](./inbox-view.md) — standalone inbox
289
- - [`TreeView`](./tree-view.md) — standalone tree
290
- - [`TabSwitch`](./tab-switch.md) — the segmented view-switcher in the header (reusable on its own)
291
- - [How-to: Render a backend response with DataViews](../how-to/data-views-from-backend-response.md) — recipes by data shape.
@@ -1,170 +0,0 @@
1
- ---
2
- title: InboxView
3
- description: Standalone inbox/list view for DataViews — a master list with read/starred/priority states and an optional detail pane. Use inside DataViewsLayout (tab mode) or directly in Composable Mode.
4
- group: Data Display
5
- keywords: [data-views, inbox-view, inbox, list, master-detail, read, starred, priority, attachment, composable, dynamic-data]
6
- ---
7
-
8
- # InboxView
9
-
10
- > The inbox renderer behind `DataViewsLayout`'s "Inbox" tab. It renders records as a scannable list with read / starred / priority / attachment affordances, plus an optional detail pane. In tab mode the layout renders it for you; render it directly only in **Composable Mode**.
11
-
12
- ## Installation
13
-
14
- TORCH Glare is a copy-in library: the CLI copies this component's source into your project
15
- (you do **not** install it from the npm package). Run `init` once, then `add`:
16
-
17
- ```bash
18
- npx torch-glare@latest init
19
- npx torch-glare@latest add InboxView
20
- ```
21
-
22
- `add` also copies any components, hooks, and utilities that `InboxView` depends on.
23
-
24
- ## Import
25
-
26
- Import from your project's local path — the alias configured in `glare.json` (e.g. `@/*`):
27
-
28
- ```tsx
29
- import { InboxView } from "@/components/InboxView";
30
- ```
31
-
32
- ## When to use it directly
33
-
34
- | Situation | Use |
35
- |---|---|
36
- | You want the standard tabbed multi-view UI | `DataViewsLayout` with `views={{ inbox: true }}` — it mounts `InboxView` for you. |
37
- | You want a custom master-detail layout | Render `InboxView` directly with state from `useDataViewsState`, and supply `renderDetail`. |
38
-
39
- ## Field auto-detection
40
-
41
- InboxView auto-detects these record fields and maps them to UI affordances.
42
- Override any of them with `inboxConfig`.
43
-
44
- | Detected field | Affordance |
45
- |---|---|
46
- | `isRead` | Read/unread weight |
47
- | `isStarred` | Star toggle |
48
- | `hasAttachment` | Paperclip icon |
49
- | `priority` | Priority flag |
50
-
51
- ## Composable Mode example
52
-
53
- ```tsx
54
- import { InboxView } from "@/components/InboxView";
55
- import { useDataViewsState } from "@/hooks/useDataViewsState";
56
- import type { FieldConfig } from "@/components/FieldConfig";
57
- import type { InboxConfig } from "@/components/InboxConfig";
58
-
59
- const messages = [
60
- { id: 1, subject: "Welcome", from: { name: "Ada" }, isRead: false, isStarred: true, sentAt: "2024-06-01" },
61
- { id: 2, subject: "Invoice", from: { name: "Billing" }, isRead: true, hasAttachment: true, sentAt: "2024-06-02" },
62
- ]
63
-
64
- const fields: FieldConfig[] = [
65
- { path: "subject", type: "text" },
66
- { path: "from.name", label: "From", type: "text" },
67
- { path: "sentAt", type: "date-format", dateFormat: "YYYY-MM-DD" },
68
- ]
69
-
70
- const inboxConfig: InboxConfig = {
71
- titlePath: "subject",
72
- previewPath: "from.name",
73
- dateField: "sentAt",
74
- }
75
-
76
- function Mailbox() {
77
- const state = useDataViewsState({ data: messages, fields })
78
- const [selectedId, setSelectedId] = useState<number | null>(null)
79
- return (
80
- <InboxView
81
- data={state.flatItems}
82
- fields={state.resolvedFields}
83
- config={state.config}
84
- inboxConfig={inboxConfig}
85
- selectedItemId={selectedId}
86
- renderDetail={(item) =>
87
- item ? <MessageDetail message={item} /> : <Empty />
88
- }
89
- />
90
- )
91
- }
92
- ```
93
-
94
- ### Link rows to routes (framework-agnostic)
95
-
96
- `itemHref` turns each row into a link. By default the card renders a plain `<a>`
97
- (full-page navigation), so it works in any framework. For client-side routing,
98
- pass your router's link via `linkComponent` — this is what makes navigation
99
- behave consistently across environments. Without it you get a normal `<a>`.
100
-
101
- ```tsx
102
- // Next.js
103
- import Link from "next/link"
104
-
105
- <InboxView
106
- data={state.flatItems}
107
- fields={state.resolvedFields}
108
- config={state.config}
109
- itemHref={(item, id) => `/messages/${id}`}
110
- linkComponent={Link} // React Router users pass their <Link> the same way
111
- />
112
- ```
113
-
114
- > Via `DataViewsLayout` (tab mode) the same prop is named `inboxLinkComponent`.
115
-
116
- ## API Reference
117
-
118
- ### `InboxViewProps`
119
-
120
- | Prop | Type | Default | Description |
121
- |---|---|---|---|
122
- | `data` | `DynamicRecord[]` | — (required) | Records to render as list items. Pass `state.flatItems`. |
123
- | `fields` | `FieldConfig[]` | — (required) | Field map controlling list-item content. Pass `state.resolvedFields`. |
124
- | `config` | `ViewConfig` | — (required) | View config from `useDataViewsState`. |
125
- | `inboxConfig` | `InboxConfig` | auto-detected | Overrides for which record paths map to title/preview/avatar/date/read/starred/attachment/priority. |
126
- | `columns` | `DynamicColumnConfig[]` | `undefined` | Explicit column overrides. Usually derived from `fields`. |
127
- | `onDataUpdate` | `(data: DynamicRecord[]) => void` | `undefined` | Called when item data changes (e.g. toggling read/starred). |
128
- | `filters` | `DynamicFilterConfig[]` | `undefined` | Explicit filter definitions. Usually inferred from `filterable` fields. |
129
- | `filterState` | `FilterState` | uncontrolled | Controlled filter state. Pair with `onFilterChange`. |
130
- | `onFilterChange` | `(filters: FilterState) => void` | `undefined` | Fires when a filter changes. |
131
- | `showFilters` | `boolean` | `true` | Show the integrated filter panel. |
132
- | `itemHref` | `(item: DynamicRecord, id: any) => string` | `undefined` | When set, each row becomes a link to the returned href. |
133
- | `linkComponent` | `ElementType` | `"a"` | Component used to render each item's link when `itemHref` is set. Pass your router's link (Next.js `Link`, React Router `Link`) for client-side navigation. Defaults to a plain `<a>` (full-page nav). |
134
- | `selectedItemId` | `any` | `undefined` | Id of the currently selected row (drives the detail pane + highlight). |
135
- | `renderDetail` | `(item: DynamicRecord \| null) => ReactNode` | `undefined` | Renders the right-hand detail pane for the selected item. |
136
-
137
- ### `InboxConfig`
138
-
139
- ```ts
140
- type InboxConfig = {
141
- starredField?: string
142
- readField?: string
143
- attachmentField?: string
144
- priorityField?: string
145
- titlePath?: string
146
- previewPath?: string
147
- avatarPath?: string
148
- dateField?: string
149
- }
150
- ```
151
-
152
- See [`DataViewsLayout`](./data-views-layout.md#fieldconfig) for `FieldConfig`,
153
- `FilterState`, and related shapes.
154
-
155
- ## Accessibility
156
-
157
- - The all/starred/priority switcher uses [`TabFormItem`](./tab-form-item.md) (full keyboard support).
158
- - Star/archive/delete actions are real `<button>`s with accessible labels.
159
- - Avatars fall back to initials via [`Avatar`](./avatar.md).
160
-
161
- ## Theming
162
-
163
- Uses only `*-presentation-*` design tokens. Control the scheme via the parent
164
- `DataViewsLayout`'s `theme`.
165
-
166
- ## Related
167
-
168
- - [`DataViewsLayout`](./data-views-layout.md) — the tabbed container that renders this for you
169
- - [`TableView`](./table-view.md) · [`KanbanView`](./kanban-view.md) · [`TreeView`](./tree-view.md) — sibling views
170
- - [How-to: Render a backend response with DataViews](../how-to/data-views-from-backend-response.md)