@object-ui/plugin-tree 17.6.0 → 17.7.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/CHANGELOG.md +2204 -0
- package/README.md +153 -20
- package/dist/ObjectTree.d.ts +43 -3
- package/dist/ObjectTree.d.ts.map +1 -1
- package/dist/index.d.ts +27 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +263 -177
- package/dist/index.umd.cjs +1 -1
- package/package.json +14 -10
package/README.md
CHANGED
|
@@ -7,44 +7,177 @@ the right view for hierarchies of unbounded depth such as **business unit /
|
|
|
7
7
|
org chart**, category trees, menu trees, BOMs, or nested comments. (Grouping
|
|
8
8
|
handles *fixed-depth* hierarchies; a tree handles arbitrary depth.)
|
|
9
9
|
|
|
10
|
-
It registers
|
|
10
|
+
It registers one component type via the `ComponentRegistry`:
|
|
11
11
|
|
|
12
|
-
- `object-tree` — the object-bound renderer
|
|
13
|
-
|
|
12
|
+
- `object-tree` — the object-bound renderer, and the one an author selects
|
|
13
|
+
|
|
14
|
+
The bare `tree` registry key (`view:tree`), a second key on the same renderer,
|
|
15
|
+
is RETIRED (objectui#10859 batch 8): `objectui validate` refused it at `type`,
|
|
16
|
+
and nothing authored it. `tree` survives as a host-composition VIEW type (see
|
|
17
|
+
below), which `ObjectView` and `ListView` compose into an `object-tree` node.
|
|
14
18
|
|
|
15
19
|
## Usage
|
|
16
20
|
|
|
21
|
+
Author an `object-tree` node. `ObjectQLComponentSchema` narrows on `type`, so
|
|
22
|
+
each key is checked against `ObjectTreeSchema`, which mirrors the `object-tree`
|
|
23
|
+
row of `@objectstack/spec` (17.5.0 and later):
|
|
24
|
+
|
|
17
25
|
```ts
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
26
|
+
import type { ObjectQLComponentSchema } from '@object-ui/types';
|
|
27
|
+
|
|
28
|
+
const schema: ObjectQLComponentSchema = {
|
|
29
|
+
type: 'object-tree',
|
|
21
30
|
objectName: 'business_unit',
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
},
|
|
32
|
-
],
|
|
33
|
-
}
|
|
31
|
+
tree: {
|
|
32
|
+
parentField: 'parent', // single-parent pointer (auto-detected if omitted)
|
|
33
|
+
labelField: 'name', // indented first column
|
|
34
|
+
fields: ['manager'], // additional flat columns
|
|
35
|
+
defaultExpandedDepth: 1, // 0 = roots only; omit = expand all
|
|
36
|
+
},
|
|
37
|
+
filter: [{ field: 'status', operator: 'equals', value: 'active' }],
|
|
38
|
+
navigation: { mode: 'drawer' },
|
|
39
|
+
};
|
|
34
40
|
```
|
|
35
41
|
|
|
36
|
-
###
|
|
42
|
+
### Keys
|
|
43
|
+
|
|
44
|
+
| Key | Description |
|
|
45
|
+
| --- | --- |
|
|
46
|
+
| `objectName` | The object to query. One of `data`, `staticData` and `objectName` is the record source, read in that order; `objectui validate` refuses a node with none of the three. |
|
|
47
|
+
| `data` | A `{ provider, … }` configuration, read first. `{ provider: 'value', items }` draws those records and `{ provider: 'object', object }` queries that object. The `api` and `schema` providers draw no rows on the tree. A bare array is not a record source (objectui#8348): inline rows go under `staticData`. |
|
|
48
|
+
| `staticData` | Inline records, read second: a `data` configuration wins, and a tree carrying both `staticData` and `objectName` draws these rows and never queries the object. |
|
|
49
|
+
| `tree` | The field configuration — the spec's `TreeConfig` (table below). |
|
|
50
|
+
| `filter` | Rule array `[{ field, operator, value }]`, lowered to `$filter` and applied to inline rows as well. Context tokens such as `{current_user_id}` are resolved first. A record whose parent the filter removed is drawn as a root. |
|
|
51
|
+
| `navigation` | What a row click opens (below). |
|
|
52
|
+
|
|
53
|
+
The node's `dataSource` binding is **not** a record source on this block: the
|
|
54
|
+
registration is not wrapped in `ElementDataSourceGate`, so nothing lands a
|
|
55
|
+
binding's object on `objectName`, and `objectui validate` does not count it.
|
|
56
|
+
|
|
57
|
+
### The `tree` block
|
|
37
58
|
|
|
38
59
|
| Key | Default | Description |
|
|
39
60
|
| --- | --- | --- |
|
|
40
61
|
| `parentField` | auto-detected | Field holding the parent reference. When omitted, the renderer picks the object's `tree` field (or a lookup/master_detail that references the same object). |
|
|
41
62
|
| `labelField` | `name` | Field rendered indented in the first column. |
|
|
42
|
-
| `fields` | `[]` | Additional fields rendered as flat columns. |
|
|
63
|
+
| `fields` | `[]` | Additional fields rendered as flat columns after the label. |
|
|
43
64
|
| `defaultExpandedDepth` | _unset_ | Initial expansion depth. `0` = roots only; unset = expand everything. |
|
|
44
65
|
|
|
45
66
|
Records whose parent is missing (or points outside the result set) are kept as
|
|
46
67
|
roots, so nothing is silently dropped.
|
|
47
68
|
|
|
69
|
+
⚠️ The same four keys written **flat** on the node are still read, and a flat
|
|
70
|
+
key outranks the block's member of the same name. They are the form a host
|
|
71
|
+
composes; the spec row declares only the `tree` block, so author that.
|
|
72
|
+
|
|
73
|
+
### `navigation`
|
|
74
|
+
|
|
75
|
+
`navigation` takes the spec's `NavigationConfig` (`{ mode, size, width,
|
|
76
|
+
openNewTab, preventNavigation }`), the block a list view declares. On a tree no
|
|
77
|
+
parent view navigates for:
|
|
78
|
+
|
|
79
|
+
- **Absent**: a click opens nothing. This renderer supplies no drawer default.
|
|
80
|
+
- `drawer`, `modal` and `popover` open the row's record in that overlay; `split`
|
|
81
|
+
opens it beside the tree, which stays drawn.
|
|
82
|
+
- `new_window` opens `/{objectName}/record/{id}` in a new tab; `none` opens
|
|
83
|
+
nothing.
|
|
84
|
+
- `page`, and a block without `mode` (it takes the spec's `page` default), open
|
|
85
|
+
a record page through the record navigator the host publishes (the console
|
|
86
|
+
publishes one on its custom pages, record pages and list views). The object
|
|
87
|
+
is `data.object` when `data` is the object provider, so it wins when both
|
|
88
|
+
are written, and the tree's `objectName` otherwise. Under a host that
|
|
89
|
+
publishes none, such as an embedded renderer, or on a tree that names neither
|
|
90
|
+
(inline rows with no `objectName`), there is no record page to open and the
|
|
91
|
+
click opens nothing.
|
|
92
|
+
- `preventNavigation: true` opens nothing whatever the mode. `openNewTab: true`
|
|
93
|
+
opens the record page in a new tab and outranks every mode except `none`.
|
|
94
|
+
- `size` picks the overlay width bucket; the deprecated `width` wins over it.
|
|
95
|
+
|
|
96
|
+
A click handler from a parent view outranks the whole key.
|
|
97
|
+
|
|
98
|
+
### Expansion: the seed and the user's answer
|
|
99
|
+
|
|
100
|
+
`defaultExpandedDepth` **seeds** expansion; it does not own it. The seeded set is
|
|
101
|
+
derived from the forest during render rather than mirrored into component state,
|
|
102
|
+
so a tree that expands by default is painted expanded in the first commit that
|
|
103
|
+
has rows — there is no frame in which the forest is drawn collapsed
|
|
104
|
+
(objectui#8666).
|
|
105
|
+
|
|
106
|
+
When the record set changes — a refetch, a filter, a host that reallocates the
|
|
107
|
+
rows — the seed is recomputed for the new forest. A node the user opened or
|
|
108
|
+
closed by clicking its chevron, **and which is still in the forest**, keeps the
|
|
109
|
+
user's answer; every other node, a genuinely new one included, takes the seed.
|
|
110
|
+
Expansion is per-mount session state: it is not addressable and is not persisted.
|
|
111
|
+
|
|
112
|
+
### The `tree` view type is host composition, not authoring
|
|
113
|
+
|
|
114
|
+
`tree` is **not** an authorable view type. Neither `ObjectViewSchema.defaultViewType`
|
|
115
|
+
nor `NamedListView.type` admits it — both are the same seven-value union that stops
|
|
116
|
+
at `map` — so no authored document selects a tree view, and `ObjectViewSchema`
|
|
117
|
+
declares no `views` member at all. The `tree` branch runs only when a **host**
|
|
118
|
+
composes `ObjectView` with a `views` prop, whose entries carry `id` and `label`
|
|
119
|
+
and are typed `ViewType`. That was ruled deliberate on objectui#5321
|
|
120
|
+
(2026-08-20): `tree` and `chart` are recorded as host-composition-only surfaces
|
|
121
|
+
rather than added to the authored unions.
|
|
122
|
+
|
|
123
|
+
The live consumer is the console: it passes stored view records to `ObjectView`
|
|
124
|
+
as `views`, and its create-view dialog offers `tree` among the types a console
|
|
125
|
+
user can create. To render a tree from authored metadata, write the
|
|
126
|
+
`object-tree` node above.
|
|
127
|
+
|
|
128
|
+
#### The host config has a type — `TreeViewConfig`
|
|
129
|
+
|
|
130
|
+
Host config is **not** untyped config. A block a host stores and re-writes is a
|
|
131
|
+
contract, so the per-view `tree` block is exported from `@object-ui/types` and
|
|
132
|
+
the renderer imports it rather than keeping a private copy (objectui#8253,
|
|
133
|
+
ruled 2026-09-07).
|
|
134
|
+
|
|
135
|
+
⚠️ It is **not the single declaration of that shape**, and saying so was itself
|
|
136
|
+
the defect objectui#8841 fixed. `@objectstack/spec` owns this block — it
|
|
137
|
+
declares it as `TreeConfig` and hangs it on `ListView.tree` — and
|
|
138
|
+
`@object-ui/types` already publishes it a second way, derived, as
|
|
139
|
+
`ListViewSchema['tree']`. `TreeViewConfig` is now a **derivation of the
|
|
140
|
+
protocol's block** rather than a copy of it — in `packages/types/src/views.ts`
|
|
141
|
+
it is a one-line alias of `NonNullable<ListView['tree']>`, taken from
|
|
142
|
+
`@objectstack/spec/ui`. So the accurate claim is the narrower one: this is the
|
|
143
|
+
name a host writes against, and it tracks the protocol by construction rather
|
|
144
|
+
than by anyone remembering to update it.
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
import type { TreeViewConfig } from '@object-ui/types';
|
|
148
|
+
|
|
149
|
+
// The block a host writes as `tree` on a `views` entry, or as `options.tree`
|
|
150
|
+
// on a stored view record. Every key is optional; a host writes the subset it
|
|
151
|
+
// means.
|
|
152
|
+
const treeConfig: TreeViewConfig = {
|
|
153
|
+
parentField: 'parent', // single-parent pointer (auto-detected if omitted)
|
|
154
|
+
labelField: 'name', // indented first column
|
|
155
|
+
fields: ['name', 'manager'], // additional flat columns
|
|
156
|
+
defaultExpandedDepth: 1, // 0 = roots only; omit = expand all
|
|
157
|
+
};
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Annotating the block is what turns a typo into a diagnostic: `parentFeild` used
|
|
161
|
+
to be stored, read by nobody and reported by nothing, because the `views` entry
|
|
162
|
+
admits any key. Against this type it is a compile error.
|
|
163
|
+
|
|
164
|
+
⛔ `titleField` is **not** part of this block. objectui#8253 declared it as a
|
|
165
|
+
legacy second rung for `labelField`; `@objectstack/spec@17.4.0` refuses
|
|
166
|
+
`tree.titleField` by name (`TreeConfigSchema` is strict since spec #15469), so
|
|
167
|
+
declaring it published a key the protocol rejects — an author who followed this
|
|
168
|
+
type was refused at publish. objectui#8841 removed it.
|
|
169
|
+
|
|
170
|
+
The renderers still *tolerate* a `titleField` already stored on a view record:
|
|
171
|
+
`plugin-view`, `plugin-list` and the console's own composition each fall back to
|
|
172
|
+
it when `labelField` is absent, so nothing that renders today stops rendering.
|
|
173
|
+
Those reads are untyped tolerance awaiting a follow-up, ⛔ not a declaration —
|
|
174
|
+
write `labelField`, which is the protocol's spelling and wins wherever both are
|
|
175
|
+
present.
|
|
176
|
+
|
|
177
|
+
⛔ This does not make `tree` an authorable view type. objectui#5321 is
|
|
178
|
+
unchanged: the block is written by a **host**, never by a document author, and
|
|
179
|
+
the authored node is the `object-tree` schema at the top of this file.
|
|
180
|
+
|
|
48
181
|
## License
|
|
49
182
|
|
|
50
183
|
MIT — see [LICENSE](./LICENSE).
|
package/dist/ObjectTree.d.ts
CHANGED
|
@@ -1,10 +1,50 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
import { DataSource } from '@object-ui/types';
|
|
2
|
+
import { DataSource, ObjectTreeSchema } from '@object-ui/types';
|
|
3
3
|
export interface ObjectTreeProps {
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* The `object-tree` node this renderer draws.
|
|
6
|
+
*
|
|
7
|
+
* Both of this package's registrations (`object-tree` and `tree`, in this
|
|
8
|
+
* package's `index.tsx`) publish one `treeInputs` list and one renderer, so one
|
|
9
|
+
* props type covers both tags — the shape `plugin-map` already has under two
|
|
10
|
+
* tags, and the shape objectui#8651 gave `ObjectCalendar`.
|
|
11
|
+
*
|
|
12
|
+
* ## What typing this BUYS, stated rather than assumed (objectui#8655)
|
|
13
|
+
*
|
|
14
|
+
* It was `any`, and an `any` is not a weaker answer than a type — it is NO
|
|
15
|
+
* answer. `checker.getPropertyOfType` through `any` returns `undefined` for
|
|
16
|
+
* `objectName`, which this schema certainly declares, exactly as it does for a
|
|
17
|
+
* nonsense token; so every "is this key declared?" asked of this prop came back
|
|
18
|
+
* "unknown" while LOOKING like "undeclared". That ordering is the card's — type
|
|
19
|
+
* FIRST, classify second — and it is objectui#8410's standing ruling that an
|
|
20
|
+
* absence returned by an instrument structurally unable to see a declaration is
|
|
21
|
+
* not a reading at all.
|
|
22
|
+
*
|
|
23
|
+
* ## ⚠️ The CEILING, so nobody reads this as more than it is
|
|
24
|
+
*
|
|
25
|
+
* `BaseSchema` ends in `[key: string]: any`, so an UNDECLARED key read off this
|
|
26
|
+
* type still compiles and still types `any`. What the annotation buys is that the
|
|
27
|
+
* question becomes ANSWERABLE by the checker, ⛔ not that an undeclared read is
|
|
28
|
+
* refused — the same ceiling objectui#5155 / objectui#7927 record for the mirror.
|
|
29
|
+
*/
|
|
30
|
+
schema: ObjectTreeSchema;
|
|
5
31
|
dataSource?: DataSource;
|
|
6
32
|
className?: string;
|
|
7
|
-
|
|
33
|
+
/**
|
|
34
|
+
* TWO parameters since objectui#9357, and the second is not decoration: this
|
|
35
|
+
* prop reaches `useNavigationOverlay` as its `onRowClick`, and `handleClick`
|
|
36
|
+
* invokes it as `onRowClick(record, event)` — the modifier payload a host
|
|
37
|
+
* needs to implement Cmd/Ctrl/middle-click for itself. Declaring one
|
|
38
|
+
* parameter hid the second on the ONE line a host reads. Spelled `any` and
|
|
39
|
+
* not `HandleClickModifiers` for the reason objectui#9341 measured on
|
|
40
|
+
* `ObjectKanbanSchema.onCardClick`: that interface lives in
|
|
41
|
+
* `@object-ui/react`, the published twins in `@object-ui/types` may not name
|
|
42
|
+
* it, and a host that discovered the payload from the implementation
|
|
43
|
+
* annotated it `React.MouseEvent` — which a narrower declaration refuses
|
|
44
|
+
* contravariantly. `BaseSchema`'s own `onClick` / `onChange` / `onSubmit`
|
|
45
|
+
* already use this spelling for exactly this situation.
|
|
46
|
+
*/
|
|
47
|
+
onRowClick?: (record: any, event?: any) => void;
|
|
8
48
|
/** Inline data (passed by ListView/ObjectView for non-grid views). */
|
|
9
49
|
data?: any[];
|
|
10
50
|
loading?: boolean;
|
package/dist/ObjectTree.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ObjectTree.d.ts","sourceRoot":"","sources":["../src/ObjectTree.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;GAWG;AAEH,OAAO,KAAuC,MAAM,OAAO,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"ObjectTree.d.ts","sourceRoot":"","sources":["../src/ObjectTree.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;GAWG;AAEH,OAAO,KAAuC,MAAM,OAAO,CAAC;AAC5D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAkB,MAAM,kBAAkB,CAAC;AAqErF,MAAM,WAAW,eAAe;IAC9B;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;;;;;;;OAaG;IACH,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IAChD,sEAAsE;IACtE,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAqbD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAotBhD,CAAC;AAEF,eAAe,UAAU,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
import { ObjectTree, ObjectTreeProps } from './ObjectTree';
|
|
2
|
+
import { ObjectTree, ObjectTreeProps } from './ObjectTree.js';
|
|
3
3
|
export { ObjectTree };
|
|
4
4
|
export type { ObjectTreeProps };
|
|
5
5
|
export declare const ObjectTreeRenderer: React.FC<any>;
|
|
6
|
+
/**
|
|
7
|
+
* ⛔ The bare `tree` node type key is RETIRED (objectui#10859 batch 8, the
|
|
8
|
+
* seat's ruling on that card, by the objectui#10393 / objectui#8760 route).
|
|
9
|
+
* `object-tree` is the one spelling this plugin serves.
|
|
10
|
+
*
|
|
11
|
+
* ## What was here, and why it went
|
|
12
|
+
*
|
|
13
|
+
* `ComponentRegistry.register('tree', ObjectTreeRenderer, { namespace: 'view',
|
|
14
|
+
* ... })` — a second key on the SAME renderer with the SAME `inputs`, which
|
|
15
|
+
* stored both `view:tree` and the bare `tree` fallback. Only `object-tree` has
|
|
16
|
+
* a declaration: `@objectstack/spec`'s `ComponentPropsMap` row, which
|
|
17
|
+
* `AnyComponentSchema` arms, while no arm claims `tree`. So `objectui validate`
|
|
18
|
+
* refused a node authored `type: 'tree'` at `type` while the registry mounted
|
|
19
|
+
* it. `PUBLIC_BLOCKS` already declined to curate it as "a second spelling of
|
|
20
|
+
* this one block".
|
|
21
|
+
*
|
|
22
|
+
* ## Why unregistering is the whole retirement
|
|
23
|
+
*
|
|
24
|
+
* Nothing wrote the node: 0 producers in source, docs, examples or the
|
|
25
|
+
* catalog, measured for the card. The other `tree` spellings in the stack are
|
|
26
|
+
* different layers and stay untouched — the FIELD type `tree` (a self-reference
|
|
27
|
+
* that `@object-ui/fields` renders as `field:lookup`) and a STORED list-view
|
|
28
|
+
* type, which `@object-ui/plugin-list`'s `ListView` already composes into an
|
|
29
|
+
* `object-tree` node. The console's lazy `tree` stub (`apps/console/src/register-plugins.ts`)
|
|
30
|
+
* went in the same change, so no host keeps the key alive.
|
|
31
|
+
*/
|
|
6
32
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,OAAO,EAAE,UAAU,EAAE,CAAC;AACtB,YAAY,EAAE,eAAe,EAAE,CAAC;AAIhC,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,GAAG,CAG5C,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,OAAO,EAAE,UAAU,EAAE,CAAC;AACtB,YAAY,EAAE,eAAe,EAAE,CAAC;AAIhC,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,GAAG,CAG5C,CAAC;AA+DF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG"}
|