torch-glare 2.1.3 → 2.1.5

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.
@@ -0,0 +1,136 @@
1
+ ---
2
+ title: TreeView
3
+ description: Standalone hierarchical tree view for DataViews — a sidebar tree of nodes with a right pane (table or card) for the selected node. Use inside DataViewsLayout (tab mode) or directly in Composable Mode.
4
+ group: Data Display
5
+ keywords: [data-views, tree-view, tree, hierarchy, nested, sidebar, parent-child, children, composable, dynamic-data]
6
+ ---
7
+
8
+ # TreeView
9
+
10
+ > The tree renderer behind `DataViewsLayout`'s "Tree" tab. It builds a hierarchy from your records (via a `children[]` array or a `parentId` reference) and shows a sidebar tree with a right pane for the selected node. In tab mode the layout renders it for you — and auto-hides the Tree tab when no hierarchy is detected. Render it directly only in **Composable Mode**.
11
+
12
+ ## Installation
13
+
14
+ Part of `torch-glare`. Ships with the `DataViews` folder when you run `npx torch-glare add DataViews` — no separate install. It reuses the sibling `TableView`, the `Card` component, a colocated tree sidebar/drawer, and `lucide-react`.
15
+
16
+ ## Import
17
+
18
+ ```tsx
19
+ import { TreeView, useDataViewsState } from "torch-glare"
20
+ import type { TreeViewProps, TreeConfig, FieldConfig } from "torch-glare"
21
+ ```
22
+
23
+ ## When to use it directly
24
+
25
+ | Situation | Use |
26
+ |---|---|
27
+ | You want the standard tabbed multi-view UI | `DataViewsLayout` — the Tree tab appears automatically when hierarchy is detected. |
28
+ | You want a custom layout with an always-on tree | Render `TreeView` directly with state from `useDataViewsState`. |
29
+ | You want a file/folder tree without the data-grid pane | Use [`TreeFolder`](./tree-drop-down.md) or [`TreeSubLayout`](./tree-sub-layout.md) instead. |
30
+
31
+ ## Hierarchy detection
32
+
33
+ `TreeView` auto-detects shape from your data. Override with `treeConfig`:
34
+
35
+ - **Nested** — each record carries a `children: []` array.
36
+ - **Flat / adjacency list** — each record carries a `parentId` (or similar) pointing at its parent's id.
37
+
38
+ ```tsx
39
+ // nested
40
+ const departments = [
41
+ { id: 1, name: "Engineering", children: [
42
+ { id: 2, name: "Platform" },
43
+ { id: 3, name: "Product" },
44
+ ]},
45
+ ]
46
+
47
+ // flat
48
+ const rows = [
49
+ { id: 1, name: "Engineering", parentId: null },
50
+ { id: 2, name: "Platform", parentId: 1 },
51
+ ]
52
+ ```
53
+
54
+ ## Composable Mode example
55
+
56
+ ```tsx
57
+ import { TreeView, useDataViewsState } from "torch-glare"
58
+ import type { FieldConfig, TreeConfig } from "torch-glare"
59
+
60
+ const fields: FieldConfig[] = [
61
+ { path: "name", type: "text" },
62
+ { path: "headcount", type: "number" },
63
+ ]
64
+
65
+ const treeConfig: TreeConfig = {
66
+ childrenField: "children",
67
+ nodeLabel: "name",
68
+ defaultExpanded: "roots", // "all" | "roots" | "none"
69
+ defaultRightPane: "table", // "table" | "card"
70
+ }
71
+
72
+ function OrgTree() {
73
+ const state = useDataViewsState({ data: departments, fields, treeConfig })
74
+ return (
75
+ <TreeView
76
+ data={state.items}
77
+ fields={state.resolvedFields}
78
+ config={state.config}
79
+ treeConfig={treeConfig}
80
+ />
81
+ )
82
+ }
83
+ ```
84
+
85
+ ## API Reference
86
+
87
+ ### `TreeViewProps`
88
+
89
+ | Prop | Type | Default | Description |
90
+ |---|---|---|---|
91
+ | `data` | `DynamicRecord[]` | — (required) | Records to build the hierarchy from. Pass `state.items` (nested) in composable mode. |
92
+ | `fields` | `FieldConfig[]` | — (required) | Field map for the right-pane table/card. Pass `state.resolvedFields`. |
93
+ | `config` | `ViewConfig` | — (required) | View config from `useDataViewsState`. |
94
+ | `treeConfig` | `TreeConfig` | auto-detected | Hierarchy + expansion + right-pane config (see below). |
95
+ | `columns` | `DynamicColumnConfig[]` | `undefined` | Explicit column overrides for the right-pane table. |
96
+ | `onDataUpdate` | `(data: DynamicRecord[]) => void` | `undefined` | Called when nodes move (drag-and-drop reparent), if `dndEnabled`. |
97
+ | `filters` | `DynamicFilterConfig[]` | `undefined` | Explicit filter definitions. Usually inferred from `filterable` fields. |
98
+ | `filterState` | `FilterState` | uncontrolled | Controlled filter state. Pair with `onFilterChange`. |
99
+ | `onFilterChange` | `(filters: FilterState) => void` | `undefined` | Fires when a filter changes. |
100
+ | `showFilters` | `boolean` | `true` | Show the integrated filter panel. |
101
+
102
+ ### `TreeConfig`
103
+
104
+ ```ts
105
+ type TreeConfig = {
106
+ childrenField?: string // nested mode: array property holding children
107
+ parentField?: string // flat mode: property pointing at the parent id
108
+ idField?: string // id property (default "id")
109
+ orderField?: string // optional ordering within siblings
110
+ nodeLabel?: string // which field labels each tree node
111
+ defaultExpanded?: "all" | "roots" | "none"
112
+ defaultRightPane?: "table" | "card" // "details" accepted as a deprecated alias of "card"
113
+ dndEnabled?: boolean // enable drag-and-drop reparenting
114
+ }
115
+ ```
116
+
117
+ See [`DataViewsLayout`](./data-views-layout.md#fieldconfig) for `FieldConfig`,
118
+ `FilterState`, and related shapes.
119
+
120
+ ## Accessibility
121
+
122
+ - Tree rows expose `role="treeitem"` with `aria-expanded` and `aria-selected`.
123
+ - On mobile the sidebar collapses into a drawer with a labelled trigger.
124
+ - The right-pane table inherits [`TableView`](./table-view.md)'s accessibility.
125
+
126
+ ## Theming
127
+
128
+ Uses only `*-presentation-*` design tokens. Control the scheme via the parent
129
+ `DataViewsLayout`'s `theme`.
130
+
131
+ ## Related
132
+
133
+ - [`DataViewsLayout`](./data-views-layout.md) — the tabbed container that renders this for you
134
+ - [`TableView`](./table-view.md) · [`KanbanView`](./kanban-view.md) · [`InboxView`](./inbox-view.md) — sibling views
135
+ - [`TreeFolder`](./tree-drop-down.md) / [`TreeSubLayout`](./tree-sub-layout.md) — non-grid tree navigation
136
+ - [How-to: Render a backend response with DataViews](../how-to/data-views-from-backend-response.md)
@@ -0,0 +1,191 @@
1
+ ---
2
+ title: Render a backend response with DataViews
3
+ description: Recipes for turning common backend JSON shapes into a DataViewsLayout — flat lists, nested hierarchies, inbox/message shapes, and server-driven filtering.
4
+ group: how-to
5
+ keywords: [data-views, recipes, backend, json, api, flat, nested, hierarchy, inbox, server-side, filtering, pagination, how-to]
6
+ ---
7
+
8
+ # Render a backend response with DataViews
9
+
10
+ The goal of [`DataViewsLayout`](../components/data-views-layout.md) is "one
11
+ backend response → many UI shapes." This guide maps the JSON shapes you get
12
+ back from an API to the props that turn them into a working view.
13
+
14
+ ## TL;DR
15
+
16
+ ```tsx
17
+ import { DataViewsLayout } from "torch-glare"
18
+
19
+ // the simplest possible case — just pass the array
20
+ <DataViewsLayout title="Records" data={await api.get("/records")} />
21
+ ```
22
+
23
+ Everything below is about refining that default for specific shapes.
24
+
25
+ ## Recipe 1 — Flat list of objects
26
+
27
+ The most common API response. Pass it straight in; every primitive field
28
+ becomes a column and the Table/Kanban/Inbox tabs all work. The Tree tab
29
+ auto-hides because there's no hierarchy.
30
+
31
+ ```tsx
32
+ // GET /employees → [{ id, name, role, salary, joinDate }, ...]
33
+ <DataViewsLayout title="Employees" data={employees} />
34
+ ```
35
+
36
+ Add a declarative `fields` map when you want typed rendering (currencies,
37
+ badges, dates) and filters:
38
+
39
+ ```tsx
40
+ import type { FieldConfig } from "torch-glare"
41
+
42
+ const fields: FieldConfig[] = [
43
+ { path: "name", label: "Name", type: "text" },
44
+ { path: "role", type: "text", filterable: true },
45
+ { path: "salary", type: "currency", currency: "USD", filterable: true },
46
+ { path: "joinDate", type: "date-format", dateFormat: "YYYY-MM-DD" },
47
+ ]
48
+
49
+ <DataViewsLayout title="Employees" data={employees} fields={fields} />
50
+ ```
51
+
52
+ ## Recipe 2 — Status field → Kanban board
53
+
54
+ When a record has a status-like field, group it into a board. Use
55
+ `enum-badge` + `kanbanVariants` to color each column.
56
+
57
+ ```tsx
58
+ // GET /tasks → [{ id, title, status: "Todo" | "In Progress" | "Done" }, ...]
59
+ const fields: FieldConfig[] = [
60
+ { path: "title", type: "text" },
61
+ {
62
+ path: "status",
63
+ type: "enum-badge",
64
+ kanbanVariants: {
65
+ Todo: { label: "To Do", color: "gray" },
66
+ "In Progress": { label: "In Progress", color: "blue" },
67
+ Done: { label: "Done", color: "green" },
68
+ },
69
+ },
70
+ ]
71
+
72
+ <DataViewsLayout
73
+ title="Tasks"
74
+ data={tasks}
75
+ fields={fields}
76
+ views={{ table: true, kanban: true }}
77
+ kanbanGroupBy="status"
78
+ />
79
+ ```
80
+
81
+ ## Recipe 3 — Nested objects (dot-paths)
82
+
83
+ APIs often nest related data. Reference it with dot-paths — no flattening
84
+ needed.
85
+
86
+ ```tsx
87
+ // GET /orders → [{ id, total, customer: { name, email } }, ...]
88
+ const fields: FieldConfig[] = [
89
+ { path: "id", label: "Order #", type: "number" },
90
+ { path: "customer.name", label: "Customer", type: "text" },
91
+ { path: "customer.email", label: "Email", type: "link", linkType: "mailto" },
92
+ { path: "total", type: "currency", currency: "USD" },
93
+ ]
94
+
95
+ <DataViewsLayout title="Orders" data={orders} fields={fields} />
96
+ ```
97
+
98
+ ## Recipe 4 — Hierarchy (nested `children[]` or flat `parentId`)
99
+
100
+ When records form a tree, the Tree tab appears automatically. Both shapes work:
101
+
102
+ ```tsx
103
+ // Nested: GET /departments → [{ id, name, children: [...] }]
104
+ <DataViewsLayout
105
+ data={departments}
106
+ treeConfig={{ childrenField: "children", nodeLabel: "name", defaultExpanded: "roots" }}
107
+ />
108
+
109
+ // Flat / adjacency list: GET /nodes → [{ id, name, parentId }]
110
+ <DataViewsLayout
111
+ data={nodes}
112
+ treeConfig={{ parentField: "parentId", idField: "id", nodeLabel: "name" }}
113
+ />
114
+ ```
115
+
116
+ ## Recipe 5 — Message/inbox shape
117
+
118
+ For mailbox-like data, the Inbox view auto-detects `isRead`, `isStarred`,
119
+ `hasAttachment`, and `priority`. Map title/preview/date with `inboxConfig`.
120
+
121
+ ```tsx
122
+ // GET /messages → [{ id, subject, from: { name }, isRead, isStarred, sentAt }]
123
+ <DataViewsLayout
124
+ data={messages}
125
+ views={{ inbox: true }}
126
+ inboxConfig={{ titlePath: "subject", previewPath: "from.name", dateField: "sentAt" }}
127
+ />
128
+ ```
129
+
130
+ ## Recipe 6 — Server-driven filtering & pagination
131
+
132
+ Make the layout controlled: hold `filterState` yourself and refetch when it
133
+ changes. This keeps the URL/server as the source of truth.
134
+
135
+ ```tsx
136
+ import { useState, useEffect } from "react"
137
+ import type { FilterState } from "torch-glare"
138
+
139
+ function ServerDriven() {
140
+ const [rows, setRows] = useState([])
141
+ const [filterState, setFilterState] = useState<FilterState>({})
142
+
143
+ useEffect(() => {
144
+ api.get("/records", { params: { filters: filterState } }).then(setRows)
145
+ }, [filterState])
146
+
147
+ return (
148
+ <DataViewsLayout
149
+ data={rows}
150
+ fields={fields}
151
+ filterState={filterState}
152
+ onFilterChange={setFilterState}
153
+ />
154
+ )
155
+ }
156
+ ```
157
+
158
+ ## Recipe 7 — Custom layout (composable mode)
159
+
160
+ When tabs aren't what you want — e.g. a table beside a kanban — bypass
161
+ `DataViewsLayout` and compose the views with `useDataViewsState`.
162
+
163
+ ```tsx
164
+ import { TableView, KanbanView, useDataViewsState } from "torch-glare"
165
+
166
+ function SplitScreen({ data, fields }) {
167
+ const state = useDataViewsState({ data, fields })
168
+ return (
169
+ <div className="grid grid-cols-2 gap-4 h-screen">
170
+ <TableView data={state.flatItems} fields={state.resolvedFields} config={state.config} showFilters={false} />
171
+ <KanbanView data={state.flatItems} fields={state.resolvedFields} config={state.config} groupByField="status" />
172
+ </div>
173
+ )
174
+ }
175
+ ```
176
+
177
+ See each view's reference: [TableView](../components/table-view.md) ·
178
+ [KanbanView](../components/kanban-view.md) ·
179
+ [InboxView](../components/inbox-view.md) ·
180
+ [TreeView](../components/tree-view.md).
181
+
182
+ ## Gotchas
183
+
184
+ - **Empty `data`** → all views render their empty state; pass `isLoading` upstream if you fetch async.
185
+ - **Tree tab missing?** No hierarchy was detected. Supply `treeConfig` explicitly or check your `childrenField` / `parentField`.
186
+ - **Saved Views don't persist** in tab mode — that's a known limitation documented in [`DataViewsConfigPanel`](../components/data-views-config-panel.md). Use composable mode for real persistence.
187
+
188
+ ## Related
189
+
190
+ - [`DataViewsLayout`](../components/data-views-layout.md) — full prop reference
191
+ - [`DataViewsConfigPanel`](../components/data-views-config-panel.md) — settings/filters panel
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "torch-glare",
3
- "version": "2.1.3",
3
+ "version": "2.1.5",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "files": [
7
7
  "dist",
8
8
  "apps/lib",
9
9
  "docs",
10
- "!**/*-dev.*"
10
+ "!**/*-dev.*",
11
+ "!apps/lib/components/**/*.md"
11
12
  ],
12
13
  "bin": {
13
14
  "torch-glare": "dist/bin/index.js"