@object-ui/core 17.3.0 → 17.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/CHANGELOG.md +2018 -0
- package/README.md +38 -34
- package/dist/actions/ActionEngine.d.ts +43 -26
- package/dist/actions/ActionEngine.js +67 -54
- package/dist/actions/ActionRunner.d.ts +281 -31
- package/dist/actions/ActionRunner.js +458 -36
- package/dist/actions/actionKeys.d.ts +163 -27
- package/dist/actions/actionKeys.js +238 -27
- package/dist/actions/actionResponse.js +1 -1
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/index.js +1 -0
- package/dist/actions/recordIdParam.d.ts +77 -0
- package/dist/actions/recordIdParam.js +37 -0
- package/dist/actions/serverActionHandler.d.ts +1 -1
- package/dist/actions/serverActionHandler.js +1 -1
- package/dist/data-scope/ViewDataProvider.d.ts +37 -12
- package/dist/data-scope/ViewDataProvider.js +83 -5
- package/dist/data-scope/element-data-source.d.ts +113 -0
- package/dist/data-scope/element-data-source.js +216 -0
- package/dist/data-scope/index.d.ts +2 -1
- package/dist/data-scope/index.js +1 -0
- package/dist/evaluator/ExpressionEvaluator.d.ts +42 -1
- package/dist/evaluator/ExpressionEvaluator.js +42 -1
- package/dist/evaluator/SafeExpressionParser.js +0 -4
- package/dist/evaluator/declaredPredicate.d.ts +119 -0
- package/dist/evaluator/declaredPredicate.js +152 -0
- package/dist/evaluator/fieldRules.d.ts +35 -0
- package/dist/evaluator/fieldRules.js +79 -20
- package/dist/evaluator/index.d.ts +2 -0
- package/dist/evaluator/index.js +2 -0
- package/dist/evaluator/listConditional.d.ts +54 -4
- package/dist/evaluator/listConditional.js +125 -19
- package/dist/evaluator/optionRules.d.ts +55 -0
- package/dist/evaluator/optionRules.js +60 -0
- package/dist/evaluator/predicateInput.d.ts +51 -3
- package/dist/evaluator/predicateInput.js +62 -5
- package/dist/evaluator/rowPredicateCanon.d.ts +67 -0
- package/dist/evaluator/rowPredicateCanon.js +208 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.js +31 -1
- package/dist/protocols/DndProtocol.d.ts +42 -1
- package/dist/protocols/KeyboardProtocol.d.ts +31 -1
- package/dist/protocols/SharingProtocol.d.ts +11 -1
- package/dist/registry/Registry.d.ts +62 -16
- package/dist/theme/ThemeEngine.d.ts +0 -8
- package/dist/theme/ThemeEngine.js +22 -103
- package/dist/theme/index.d.ts +1 -1
- package/dist/theme/index.js +7 -1
- package/dist/types/index.d.ts +125 -12
- package/dist/utils/chart-presentation.d.ts +205 -0
- package/dist/utils/chart-presentation.js +280 -0
- package/dist/utils/chart-series.d.ts +390 -23
- package/dist/utils/chart-series.js +776 -32
- package/dist/utils/column-identity.d.ts +43 -0
- package/dist/utils/column-identity.js +47 -1
- package/dist/utils/compare-to.d.ts +40 -13
- package/dist/utils/compare-to.js +4 -32
- package/dist/utils/dashboard-filters.d.ts +54 -8
- package/dist/utils/dashboard-filters.js +261 -12
- package/dist/utils/dataset-format.d.ts +73 -14
- package/dist/utils/dataset-format.js +142 -9
- package/dist/utils/dataset-pivot.d.ts +28 -7
- package/dist/utils/dataset-pivot.js +27 -6
- package/dist/utils/debug.js +7 -0
- package/dist/utils/dom-props.d.ts +137 -0
- package/dist/utils/dom-props.js +153 -0
- package/dist/utils/expand-fields.d.ts +97 -7
- package/dist/utils/expand-fields.js +97 -7
- package/dist/utils/filter-tokens.js +0 -1
- package/dist/utils/freeze-schema.d.ts +38 -1
- package/dist/utils/freeze-schema.js +10 -0
- package/dist/utils/humanize-label.d.ts +60 -0
- package/dist/utils/humanize-label.js +62 -0
- package/dist/utils/managedBy.d.ts +27 -2
- package/dist/utils/normalize-list-view.d.ts +36 -6
- package/dist/utils/normalize-list-view.js +93 -9
- package/dist/utils/number-display.d.ts +155 -0
- package/dist/utils/number-display.js +71 -0
- package/dist/utils/predicate-fields.d.ts +29 -4
- package/dist/utils/predicate-fields.js +44 -4
- package/dist/utils/resolve-view-id.d.ts +31 -0
- package/dist/utils/resolve-view-id.js +45 -0
- package/dist/utils/retired-field-types.d.ts +90 -0
- package/dist/utils/retired-field-types.js +124 -0
- package/dist/utils/sort-query.d.ts +57 -0
- package/dist/utils/sort-query.js +35 -0
- package/dist/utils/unmaterialized-fields.d.ts +52 -0
- package/dist/utils/unmaterialized-fields.js +57 -0
- package/dist/validation/index.d.ts +1 -0
- package/dist/validation/index.js +1 -0
- package/dist/validation/schema-validator.js +105 -0
- package/dist/validation/server-owned-value.d.ts +62 -0
- package/dist/validation/server-owned-value.js +99 -0
- package/dist/validation/validators/object-validation-engine.js +0 -2
- package/package.json +5 -5
- package/dist/__benchmarks__/core.bench.d.ts +0 -8
- package/dist/__benchmarks__/core.bench.js +0 -53
- package/dist/utils/merge-views-into-objects.d.ts +0 -19
- package/dist/utils/merge-views-into-objects.js +0 -39
|
@@ -11,17 +11,107 @@
|
|
|
11
11
|
* list / grid / detail cell can render the related record's display name
|
|
12
12
|
* instead of a bare id placeholder ("—").
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* lookup
|
|
17
|
-
*
|
|
18
|
-
* `reference` + id storage), so a `user` column that is NOT requested for
|
|
19
|
-
* expansion comes back as a raw user id and renders as "—". Keeping the set
|
|
20
|
-
* here as the single source of truth is what closes that gap.
|
|
14
|
+
* `user` is a lookup specialised to `sys_user`: it carries the same `reference`
|
|
15
|
+
* + id storage and the server resolves it through the same expand path as
|
|
16
|
+
* `lookup` / `master_detail`, so a `user` column that is NOT requested for
|
|
17
|
+
* expansion comes back as a raw user id and renders as "—" (objectui#2032).
|
|
21
18
|
*
|
|
22
19
|
* Note on `tree`: a self-referencing hierarchy field is a reference too, so it
|
|
23
20
|
* belongs in this set; whether the backend materialises the expanded object
|
|
24
21
|
* for it is a server concern — requesting it is harmless and forward-compatible.
|
|
22
|
+
*
|
|
23
|
+
* ## One family, many consumers — and NO reliable count of them
|
|
24
|
+
*
|
|
25
|
+
* This section used to open "One family, four consumers" and enumerate four.
|
|
26
|
+
* That count was hand-kept, and hand-kept counts of this table are exactly what
|
|
27
|
+
* keeps going wrong: it was already stale by two when objectui#5692 measured it
|
|
28
|
+
* (`paramToField` in `app-shell` had joined with objectui#5312, and
|
|
29
|
+
* `ListView`'s relational-sort rule reads the set directly), and objectui#5312's
|
|
30
|
+
* claim to have converted "the LAST private copy" was false by two MORE — see
|
|
31
|
+
* the falsification note at the end. Read the list below as the lineage of the
|
|
32
|
+
* conversions, not as a census; the mechanical fact is the identity pins.
|
|
33
|
+
*
|
|
34
|
+
* This is the reference-bearing FAMILY, not the `$expand` builder's private
|
|
35
|
+
* list, and these concerns read it under several different words:
|
|
36
|
+
*
|
|
37
|
+
* - `$expand` construction — `buildExpandFields` below ("expandable");
|
|
38
|
+
* - predicate-record projection — `predicate-record.ts` ("relational");
|
|
39
|
+
* - the object form's data-source wiring — `needsDataSourceWiring` in
|
|
40
|
+
* `packages/components/src/renderers/form/form.tsx`, which decides which
|
|
41
|
+
* registered widget gets `dataSource` / `dependentValues` /
|
|
42
|
+
* `dependsOnLabels` threaded to it;
|
|
43
|
+
* - the grid's bulk-action dialog — `widgetNeedsDataSource` in
|
|
44
|
+
* `packages/plugin-grid/src/components/bulkParamToField.ts`, which decides
|
|
45
|
+
* which param widget is handed the grid's `DataSource` and which param field
|
|
46
|
+
* shape carries `reference_to` / `display_field`;
|
|
47
|
+
* - the action-param dialog — `paramToField` in
|
|
48
|
+
* `packages/app-shell/src/utils/paramToField.ts`, which decides which param
|
|
49
|
+
* carries a reference target (objectui#5312);
|
|
50
|
+
* - the list view's relational-sort rule — `ListView.tsx`, which will not offer
|
|
51
|
+
* a server-side sort on a field whose stored value is a foreign key;
|
|
52
|
+
* - the dashboard table's `$expand` whitelist — `computeLookupExpand` in
|
|
53
|
+
* `packages/plugin-dashboard/src/ObjectDataTable.tsx` (objectui#5692);
|
|
54
|
+
* - the dashboard's relation/link test — `isLookupType` in
|
|
55
|
+
* `packages/plugin-dashboard/src/recordFields.tsx` (objectui#5692).
|
|
56
|
+
*
|
|
57
|
+
* The third one used to be a second hand-maintained copy, and this comment used
|
|
58
|
+
* to claim the set "mirrors the form layer's `DATA_SOURCE_FIELD_TYPES`
|
|
59
|
+
* (`lookup` / `master_detail` / `tree`)". That was true for 15 days. The form's
|
|
60
|
+
* copy then gained `capability-multiselect` (objectui#2403) and, the same day,
|
|
61
|
+
* `object-ref` / `filter-condition` / `recipient-picker` (objectui#2421), after
|
|
62
|
+
* which the two sets were not in a subset relation in EITHER direction — `user`
|
|
63
|
+
* only here, three picker names only there — and no gate could say so
|
|
64
|
+
* (objectui#4790). The form now DERIVES from this set instead of restating it:
|
|
65
|
+
*
|
|
66
|
+
* form's data-source rule == EXPANDABLE_FIELD_TYPES
|
|
67
|
+
* + three widget-hint-only picker names
|
|
68
|
+
*
|
|
69
|
+
* The extension is one-directional and cannot fold back into this set:
|
|
70
|
+
* `object-ref` / `filter-condition` / `recipient-picker` are widget HINTS, never
|
|
71
|
+
* declarable field types (`widgetHintOnly: true` in
|
|
72
|
+
* `app-shell/src/utils/paramValueShape.ts`), so no object schema can produce a
|
|
73
|
+
* field whose `type` is one of them.
|
|
74
|
+
*
|
|
75
|
+
* The fourth was a hand-maintained copy too, one member set removed from BOTH
|
|
76
|
+
* of the above (`lookup` / `master_detail` / `user` — no `tree`, no pickers, and
|
|
77
|
+
* for a while a fifth spelling, `owner`, that objectui#4814 retired). It now
|
|
78
|
+
* derives from this set with NO extension, because the bulk dialog's params
|
|
79
|
+
* cannot produce the three widget-hint names (objectui#4815):
|
|
80
|
+
*
|
|
81
|
+
* bulk dialog's data-source rule == EXPANDABLE_FIELD_TYPES
|
|
82
|
+
*
|
|
83
|
+
* Neither consumer copies the set — both call `.has()` on THIS object, and both
|
|
84
|
+
* carry an identity pin (a spy on this `has`) so a member-identical private copy
|
|
85
|
+
* fails rather than quietly re-forking the table.
|
|
86
|
+
*
|
|
87
|
+
* ## The "LAST private copy" claim was false — objectui#5692
|
|
88
|
+
*
|
|
89
|
+
* objectui#5312 converted `paramToField` (`app-shell`) and recorded it as the
|
|
90
|
+
* fourth and LAST private copy of this rule. `packages/plugin-dashboard` held
|
|
91
|
+
* two more the whole time — `LOOKUP_TYPES` in `recordFields.tsx` and an inline
|
|
92
|
+
* disjunction inside `computeLookupExpand` in `ObjectDataTable.tsx` — which
|
|
93
|
+
* predate that sweep and were outside its file surface, so nothing contradicted
|
|
94
|
+
* the claim. Both now derive from this set with NO extension and carry the same
|
|
95
|
+
* identity pin.
|
|
96
|
+
*
|
|
97
|
+
* Those two were NOT member-identical to this set, in either direction: they
|
|
98
|
+
* lacked `tree` and carried a fifth spelling, `reference`. Converging them was
|
|
99
|
+
* therefore a behaviour change in two directions, and the direction that could
|
|
100
|
+
* have widened THIS set was settled by measurement rather than by preference:
|
|
101
|
+
* `reference` is absent from `@objectstack/spec`'s closed `FieldType`
|
|
102
|
+
* vocabulary and is refused by `FieldSchema.safeParse` — measured with `lookup`
|
|
103
|
+
* / `master_detail` / `user` / `tree` as live controls and the retired `owner`
|
|
104
|
+
* plus a nonsense spelling as dead ones — so no spec-compliant object schema can
|
|
105
|
+
* declare a field whose stored type is `reference`, and the dashboard's copies
|
|
106
|
+
* were carrying a dead spelling, not a member this set was missing. This set is
|
|
107
|
+
* unchanged by objectui#5692; the two dashboard faces simply stopped answering
|
|
108
|
+
* for a type no producer can emit.
|
|
109
|
+
*
|
|
110
|
+
* Stated so it reads as a decision rather than a surprise: **adding a member
|
|
111
|
+
* here also grants that type the form's data-source wiring.** That is the
|
|
112
|
+
* intended coupling — a type whose stored value is a foreign key needs a
|
|
113
|
+
* `DataSource` to resolve that key on the read path (expand) and on the edit
|
|
114
|
+
* path (the picker) alike.
|
|
25
115
|
*/
|
|
26
116
|
export declare const EXPANDABLE_FIELD_TYPES: ReadonlySet<string>;
|
|
27
117
|
/**
|
|
@@ -12,17 +12,107 @@ import { columnIdentity } from './column-identity.js';
|
|
|
12
12
|
* list / grid / detail cell can render the related record's display name
|
|
13
13
|
* instead of a bare id placeholder ("—").
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* lookup
|
|
18
|
-
*
|
|
19
|
-
* `reference` + id storage), so a `user` column that is NOT requested for
|
|
20
|
-
* expansion comes back as a raw user id and renders as "—". Keeping the set
|
|
21
|
-
* here as the single source of truth is what closes that gap.
|
|
15
|
+
* `user` is a lookup specialised to `sys_user`: it carries the same `reference`
|
|
16
|
+
* + id storage and the server resolves it through the same expand path as
|
|
17
|
+
* `lookup` / `master_detail`, so a `user` column that is NOT requested for
|
|
18
|
+
* expansion comes back as a raw user id and renders as "—" (objectui#2032).
|
|
22
19
|
*
|
|
23
20
|
* Note on `tree`: a self-referencing hierarchy field is a reference too, so it
|
|
24
21
|
* belongs in this set; whether the backend materialises the expanded object
|
|
25
22
|
* for it is a server concern — requesting it is harmless and forward-compatible.
|
|
23
|
+
*
|
|
24
|
+
* ## One family, many consumers — and NO reliable count of them
|
|
25
|
+
*
|
|
26
|
+
* This section used to open "One family, four consumers" and enumerate four.
|
|
27
|
+
* That count was hand-kept, and hand-kept counts of this table are exactly what
|
|
28
|
+
* keeps going wrong: it was already stale by two when objectui#5692 measured it
|
|
29
|
+
* (`paramToField` in `app-shell` had joined with objectui#5312, and
|
|
30
|
+
* `ListView`'s relational-sort rule reads the set directly), and objectui#5312's
|
|
31
|
+
* claim to have converted "the LAST private copy" was false by two MORE — see
|
|
32
|
+
* the falsification note at the end. Read the list below as the lineage of the
|
|
33
|
+
* conversions, not as a census; the mechanical fact is the identity pins.
|
|
34
|
+
*
|
|
35
|
+
* This is the reference-bearing FAMILY, not the `$expand` builder's private
|
|
36
|
+
* list, and these concerns read it under several different words:
|
|
37
|
+
*
|
|
38
|
+
* - `$expand` construction — `buildExpandFields` below ("expandable");
|
|
39
|
+
* - predicate-record projection — `predicate-record.ts` ("relational");
|
|
40
|
+
* - the object form's data-source wiring — `needsDataSourceWiring` in
|
|
41
|
+
* `packages/components/src/renderers/form/form.tsx`, which decides which
|
|
42
|
+
* registered widget gets `dataSource` / `dependentValues` /
|
|
43
|
+
* `dependsOnLabels` threaded to it;
|
|
44
|
+
* - the grid's bulk-action dialog — `widgetNeedsDataSource` in
|
|
45
|
+
* `packages/plugin-grid/src/components/bulkParamToField.ts`, which decides
|
|
46
|
+
* which param widget is handed the grid's `DataSource` and which param field
|
|
47
|
+
* shape carries `reference_to` / `display_field`;
|
|
48
|
+
* - the action-param dialog — `paramToField` in
|
|
49
|
+
* `packages/app-shell/src/utils/paramToField.ts`, which decides which param
|
|
50
|
+
* carries a reference target (objectui#5312);
|
|
51
|
+
* - the list view's relational-sort rule — `ListView.tsx`, which will not offer
|
|
52
|
+
* a server-side sort on a field whose stored value is a foreign key;
|
|
53
|
+
* - the dashboard table's `$expand` whitelist — `computeLookupExpand` in
|
|
54
|
+
* `packages/plugin-dashboard/src/ObjectDataTable.tsx` (objectui#5692);
|
|
55
|
+
* - the dashboard's relation/link test — `isLookupType` in
|
|
56
|
+
* `packages/plugin-dashboard/src/recordFields.tsx` (objectui#5692).
|
|
57
|
+
*
|
|
58
|
+
* The third one used to be a second hand-maintained copy, and this comment used
|
|
59
|
+
* to claim the set "mirrors the form layer's `DATA_SOURCE_FIELD_TYPES`
|
|
60
|
+
* (`lookup` / `master_detail` / `tree`)". That was true for 15 days. The form's
|
|
61
|
+
* copy then gained `capability-multiselect` (objectui#2403) and, the same day,
|
|
62
|
+
* `object-ref` / `filter-condition` / `recipient-picker` (objectui#2421), after
|
|
63
|
+
* which the two sets were not in a subset relation in EITHER direction — `user`
|
|
64
|
+
* only here, three picker names only there — and no gate could say so
|
|
65
|
+
* (objectui#4790). The form now DERIVES from this set instead of restating it:
|
|
66
|
+
*
|
|
67
|
+
* form's data-source rule == EXPANDABLE_FIELD_TYPES
|
|
68
|
+
* + three widget-hint-only picker names
|
|
69
|
+
*
|
|
70
|
+
* The extension is one-directional and cannot fold back into this set:
|
|
71
|
+
* `object-ref` / `filter-condition` / `recipient-picker` are widget HINTS, never
|
|
72
|
+
* declarable field types (`widgetHintOnly: true` in
|
|
73
|
+
* `app-shell/src/utils/paramValueShape.ts`), so no object schema can produce a
|
|
74
|
+
* field whose `type` is one of them.
|
|
75
|
+
*
|
|
76
|
+
* The fourth was a hand-maintained copy too, one member set removed from BOTH
|
|
77
|
+
* of the above (`lookup` / `master_detail` / `user` — no `tree`, no pickers, and
|
|
78
|
+
* for a while a fifth spelling, `owner`, that objectui#4814 retired). It now
|
|
79
|
+
* derives from this set with NO extension, because the bulk dialog's params
|
|
80
|
+
* cannot produce the three widget-hint names (objectui#4815):
|
|
81
|
+
*
|
|
82
|
+
* bulk dialog's data-source rule == EXPANDABLE_FIELD_TYPES
|
|
83
|
+
*
|
|
84
|
+
* Neither consumer copies the set — both call `.has()` on THIS object, and both
|
|
85
|
+
* carry an identity pin (a spy on this `has`) so a member-identical private copy
|
|
86
|
+
* fails rather than quietly re-forking the table.
|
|
87
|
+
*
|
|
88
|
+
* ## The "LAST private copy" claim was false — objectui#5692
|
|
89
|
+
*
|
|
90
|
+
* objectui#5312 converted `paramToField` (`app-shell`) and recorded it as the
|
|
91
|
+
* fourth and LAST private copy of this rule. `packages/plugin-dashboard` held
|
|
92
|
+
* two more the whole time — `LOOKUP_TYPES` in `recordFields.tsx` and an inline
|
|
93
|
+
* disjunction inside `computeLookupExpand` in `ObjectDataTable.tsx` — which
|
|
94
|
+
* predate that sweep and were outside its file surface, so nothing contradicted
|
|
95
|
+
* the claim. Both now derive from this set with NO extension and carry the same
|
|
96
|
+
* identity pin.
|
|
97
|
+
*
|
|
98
|
+
* Those two were NOT member-identical to this set, in either direction: they
|
|
99
|
+
* lacked `tree` and carried a fifth spelling, `reference`. Converging them was
|
|
100
|
+
* therefore a behaviour change in two directions, and the direction that could
|
|
101
|
+
* have widened THIS set was settled by measurement rather than by preference:
|
|
102
|
+
* `reference` is absent from `@objectstack/spec`'s closed `FieldType`
|
|
103
|
+
* vocabulary and is refused by `FieldSchema.safeParse` — measured with `lookup`
|
|
104
|
+
* / `master_detail` / `user` / `tree` as live controls and the retired `owner`
|
|
105
|
+
* plus a nonsense spelling as dead ones — so no spec-compliant object schema can
|
|
106
|
+
* declare a field whose stored type is `reference`, and the dashboard's copies
|
|
107
|
+
* were carrying a dead spelling, not a member this set was missing. This set is
|
|
108
|
+
* unchanged by objectui#5692; the two dashboard faces simply stopped answering
|
|
109
|
+
* for a type no producer can emit.
|
|
110
|
+
*
|
|
111
|
+
* Stated so it reads as a decision rather than a surprise: **adding a member
|
|
112
|
+
* here also grants that type the form's data-source wiring.** That is the
|
|
113
|
+
* intended coupling — a type whose stored value is a foreign key needs a
|
|
114
|
+
* `DataSource` to resolve that key on the read path (expand) and on the edit
|
|
115
|
+
* path (the picker) alike.
|
|
26
116
|
*/
|
|
27
117
|
export const EXPANDABLE_FIELD_TYPES = new Set([
|
|
28
118
|
'lookup',
|
|
@@ -36,6 +36,33 @@ export declare const SYSTEM_VIEW_MARKER: unique symbol;
|
|
|
36
36
|
export type DeepReadonly<T> = T extends (...args: any[]) => any ? T : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepReadonly<U>> : T extends object ? {
|
|
37
37
|
readonly [K in keyof T]: DeepReadonly<T[K]>;
|
|
38
38
|
} : T;
|
|
39
|
+
/**
|
|
40
|
+
* The inverse of `DeepReadonly<T>`: recursively strip `readonly` at every depth.
|
|
41
|
+
*
|
|
42
|
+
* Named `DeepMutable` rather than `Mutable` for the same reason the type above
|
|
43
|
+
* is not called `Readonly` — TS' built-in `Readonly<T>` is SHALLOW, so a bare
|
|
44
|
+
* `Mutable` would understate what this does by exactly the depth gap that made
|
|
45
|
+
* `cloneAsOverride` wrong in the first place. A type whose name misreports its
|
|
46
|
+
* own reach is the defect this pair exists to close, not a naming preference.
|
|
47
|
+
*
|
|
48
|
+
* Symmetric with `DeepReadonly` arm for arm, deliberately including its two
|
|
49
|
+
* limits: a tuple widens to an array (`DeepReadonly` widens it the same way, so
|
|
50
|
+
* an inverse that preserved tuples would not be an inverse), and `Date`,
|
|
51
|
+
* `RegExp`, `Map` and `Set` are mapped as plain objects even though
|
|
52
|
+
* `isFreezableObject` skips them at runtime.
|
|
53
|
+
*
|
|
54
|
+
* One thing it deliberately does NOT do: drop the `SYSTEM_VIEW_MARKER` key.
|
|
55
|
+
* `cloneAsOverride` never copies the symbol, so a clone's marker is always
|
|
56
|
+
* absent — but the key is declared optional (`?: true`), and carrying it
|
|
57
|
+
* therefore states "may be absent", which is true. Excluding it needs
|
|
58
|
+
* `Exclude<keyof T, typeof SYSTEM_VIEW_MARKER>`, a NON-homomorphic mapped type
|
|
59
|
+
* that drops the `?` modifier from every other property and turns optional keys
|
|
60
|
+
* required — a strictly worse type, and a real break for callers, traded for
|
|
61
|
+
* removing a key that already reads as optional.
|
|
62
|
+
*/
|
|
63
|
+
export type DeepMutable<T> = T extends (...args: any[]) => any ? T : T extends ReadonlyArray<infer U> ? DeepMutable<U>[] : T extends object ? {
|
|
64
|
+
-readonly [K in keyof T]: DeepMutable<T[K]>;
|
|
65
|
+
} : T;
|
|
39
66
|
/**
|
|
40
67
|
* A schema that has been frozen by `defineSystemView()`. The marker symbol is
|
|
41
68
|
* non-enumerable and therefore invisible to consumers, but its presence
|
|
@@ -87,8 +114,18 @@ export declare function isSystemView(value: unknown): boolean;
|
|
|
87
114
|
* Produce a deep, mutable clone of a System View so callers can apply
|
|
88
115
|
* Tenant/User overrides without touching the source schema.
|
|
89
116
|
*
|
|
117
|
+
* Returns `DeepMutable<T>`, not `T`. The declaration used to hand back the
|
|
118
|
+
* input type unchanged, which meant cloning a `SystemView<S>` returned
|
|
119
|
+
* something still typed deep-readonly even though the implementation had always
|
|
120
|
+
* produced a plain mutable object — so the override flow this function exists
|
|
121
|
+
* for, `draft.columns.push(...)`, did not type-check (TS2339). The return type
|
|
122
|
+
* now relaxes TOWARD what the runtime already does, never away from it, which
|
|
123
|
+
* is why the change adds capability to a caller's type rather than removing it.
|
|
124
|
+
*
|
|
90
125
|
* Implementation note: uses `structuredClone` when available (Node 17+, all
|
|
91
126
|
* evergreen browsers) and falls back to a JSON round-trip. The marker
|
|
92
127
|
* symbol is intentionally NOT copied — the clone is no longer a System View.
|
|
128
|
+
* Neither path can copy it by accident: `structuredClone` ignores symbol keys,
|
|
129
|
+
* and `JSON.stringify` never sees them.
|
|
93
130
|
*/
|
|
94
|
-
export declare function cloneAsOverride<T>(view: T): T
|
|
131
|
+
export declare function cloneAsOverride<T>(view: T): DeepMutable<T>;
|
|
@@ -140,9 +140,19 @@ export function isSystemView(value) {
|
|
|
140
140
|
* Produce a deep, mutable clone of a System View so callers can apply
|
|
141
141
|
* Tenant/User overrides without touching the source schema.
|
|
142
142
|
*
|
|
143
|
+
* Returns `DeepMutable<T>`, not `T`. The declaration used to hand back the
|
|
144
|
+
* input type unchanged, which meant cloning a `SystemView<S>` returned
|
|
145
|
+
* something still typed deep-readonly even though the implementation had always
|
|
146
|
+
* produced a plain mutable object — so the override flow this function exists
|
|
147
|
+
* for, `draft.columns.push(...)`, did not type-check (TS2339). The return type
|
|
148
|
+
* now relaxes TOWARD what the runtime already does, never away from it, which
|
|
149
|
+
* is why the change adds capability to a caller's type rather than removing it.
|
|
150
|
+
*
|
|
143
151
|
* Implementation note: uses `structuredClone` when available (Node 17+, all
|
|
144
152
|
* evergreen browsers) and falls back to a JSON round-trip. The marker
|
|
145
153
|
* symbol is intentionally NOT copied — the clone is no longer a System View.
|
|
154
|
+
* Neither path can copy it by accident: `structuredClone` ignores symbol keys,
|
|
155
|
+
* and `JSON.stringify` never sees them.
|
|
146
156
|
*/
|
|
147
157
|
export function cloneAsOverride(view) {
|
|
148
158
|
if (view == null || typeof view !== 'object')
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI — value-fallback label prettifier
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* humanize-label — the ONE home for the VALUE fallback: a stored value becomes
|
|
10
|
+
* a display string when nothing else resolves it (objectui#5444).
|
|
11
|
+
*
|
|
12
|
+
* Two byte-identical copies preceded this module: `@object-ui/fields` (read by
|
|
13
|
+
* `plugin-grid`, `plugin-gantt`, `plugin-detail`, and by the field renderers in
|
|
14
|
+
* that package itself) and a deliberate local copy in `plugin-charts`'
|
|
15
|
+
* `ObjectChart.tsx`, whose comment said it existed "to avoid a dependency on
|
|
16
|
+
* `@object-ui/fields`". `@object-ui/core` is the shared ancestor both packages
|
|
17
|
+
* already depend on, so the convention gets a single home without a new
|
|
18
|
+
* dependency edge and without React reaching core — objectui#4389's ruling for
|
|
19
|
+
* this family: core-canonical logic, plugins consume; never core importing
|
|
20
|
+
* react. Both former sites now re-export THIS function, so there is one
|
|
21
|
+
* implementation and two doorways.
|
|
22
|
+
*
|
|
23
|
+
* ## Why this is NOT `humanizeFieldKey`, and why the two are kept apart
|
|
24
|
+
*
|
|
25
|
+
* The renderer carries a second prettifier — `humanizeFieldKey` in
|
|
26
|
+
* `@object-ui/plugin-dashboard`, the single home for the KEY fallback (a field
|
|
27
|
+
* key becomes a column header). It is a DIFFERENT convention, not a stale copy
|
|
28
|
+
* of this one: it additionally splits camelCase.
|
|
29
|
+
*
|
|
30
|
+
* input humanizeFieldKey humanizeLabel (here)
|
|
31
|
+
* ────────────────── ────────────────── ──────────────────
|
|
32
|
+
* needs_analysis Needs Analysis Needs Analysis
|
|
33
|
+
* NeedsAnalysis Needs Analysis NeedsAnalysis <- differ
|
|
34
|
+
* unitPrice Unit Price UnitPrice <- differ
|
|
35
|
+
* BestCase Best Case BestCase <- differ
|
|
36
|
+
* lost-to-competitor Lost-To-Competitor Lost To Competitor <- differ
|
|
37
|
+
*
|
|
38
|
+
* They are fed different inputs, which is why their outputs may differ. A field
|
|
39
|
+
* KEY is authored in the codebase and is expected to carry a machine spelling,
|
|
40
|
+
* so splitting camelCase recovers words its author meant. A stored VALUE is
|
|
41
|
+
* arbitrary tenant data — a picklist member, an object name, a code from
|
|
42
|
+
* another system — where a mid-token capital is not reliably a word boundary,
|
|
43
|
+
* and splitting it rewrites what the tenant wrote (`McDonald` -> `Mc Donald`).
|
|
44
|
+
* Note also that the two conventions do not nest: on the last row above each
|
|
45
|
+
* leaves alone the separator the other rewrites, so "converge" would have to
|
|
46
|
+
* pick a winner per input class rather than adopt one side wholesale.
|
|
47
|
+
*
|
|
48
|
+
* Kept distinct DELIBERATELY, and this paragraph is the write-down whose
|
|
49
|
+
* absence made the split read as drift. Converging them is a decision, not a
|
|
50
|
+
* refactor: this function is read by grid, gantt, detail and charts, so any
|
|
51
|
+
* change to what it RETURNS moves rendered output in four packages at once.
|
|
52
|
+
* That change is explicitly out of scope for the convergence that created this
|
|
53
|
+
* module; it needs its own card.
|
|
54
|
+
*
|
|
55
|
+
* Examples:
|
|
56
|
+
* "in_progress" -> "In Progress"
|
|
57
|
+
* "high-priority" -> "High Priority"
|
|
58
|
+
* "active" -> "Active"
|
|
59
|
+
*/
|
|
60
|
+
export declare function humanizeLabel(value: string): string;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI — value-fallback label prettifier
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* humanize-label — the ONE home for the VALUE fallback: a stored value becomes
|
|
10
|
+
* a display string when nothing else resolves it (objectui#5444).
|
|
11
|
+
*
|
|
12
|
+
* Two byte-identical copies preceded this module: `@object-ui/fields` (read by
|
|
13
|
+
* `plugin-grid`, `plugin-gantt`, `plugin-detail`, and by the field renderers in
|
|
14
|
+
* that package itself) and a deliberate local copy in `plugin-charts`'
|
|
15
|
+
* `ObjectChart.tsx`, whose comment said it existed "to avoid a dependency on
|
|
16
|
+
* `@object-ui/fields`". `@object-ui/core` is the shared ancestor both packages
|
|
17
|
+
* already depend on, so the convention gets a single home without a new
|
|
18
|
+
* dependency edge and without React reaching core — objectui#4389's ruling for
|
|
19
|
+
* this family: core-canonical logic, plugins consume; never core importing
|
|
20
|
+
* react. Both former sites now re-export THIS function, so there is one
|
|
21
|
+
* implementation and two doorways.
|
|
22
|
+
*
|
|
23
|
+
* ## Why this is NOT `humanizeFieldKey`, and why the two are kept apart
|
|
24
|
+
*
|
|
25
|
+
* The renderer carries a second prettifier — `humanizeFieldKey` in
|
|
26
|
+
* `@object-ui/plugin-dashboard`, the single home for the KEY fallback (a field
|
|
27
|
+
* key becomes a column header). It is a DIFFERENT convention, not a stale copy
|
|
28
|
+
* of this one: it additionally splits camelCase.
|
|
29
|
+
*
|
|
30
|
+
* input humanizeFieldKey humanizeLabel (here)
|
|
31
|
+
* ────────────────── ────────────────── ──────────────────
|
|
32
|
+
* needs_analysis Needs Analysis Needs Analysis
|
|
33
|
+
* NeedsAnalysis Needs Analysis NeedsAnalysis <- differ
|
|
34
|
+
* unitPrice Unit Price UnitPrice <- differ
|
|
35
|
+
* BestCase Best Case BestCase <- differ
|
|
36
|
+
* lost-to-competitor Lost-To-Competitor Lost To Competitor <- differ
|
|
37
|
+
*
|
|
38
|
+
* They are fed different inputs, which is why their outputs may differ. A field
|
|
39
|
+
* KEY is authored in the codebase and is expected to carry a machine spelling,
|
|
40
|
+
* so splitting camelCase recovers words its author meant. A stored VALUE is
|
|
41
|
+
* arbitrary tenant data — a picklist member, an object name, a code from
|
|
42
|
+
* another system — where a mid-token capital is not reliably a word boundary,
|
|
43
|
+
* and splitting it rewrites what the tenant wrote (`McDonald` -> `Mc Donald`).
|
|
44
|
+
* Note also that the two conventions do not nest: on the last row above each
|
|
45
|
+
* leaves alone the separator the other rewrites, so "converge" would have to
|
|
46
|
+
* pick a winner per input class rather than adopt one side wholesale.
|
|
47
|
+
*
|
|
48
|
+
* Kept distinct DELIBERATELY, and this paragraph is the write-down whose
|
|
49
|
+
* absence made the split read as drift. Converging them is a decision, not a
|
|
50
|
+
* refactor: this function is read by grid, gantt, detail and charts, so any
|
|
51
|
+
* change to what it RETURNS moves rendered output in four packages at once.
|
|
52
|
+
* That change is explicitly out of scope for the convergence that created this
|
|
53
|
+
* module; it needs its own card.
|
|
54
|
+
*
|
|
55
|
+
* Examples:
|
|
56
|
+
* "in_progress" -> "In Progress"
|
|
57
|
+
* "high-priority" -> "High Priority"
|
|
58
|
+
* "active" -> "Active"
|
|
59
|
+
*/
|
|
60
|
+
export function humanizeLabel(value) {
|
|
61
|
+
return value.replace(/[_-]/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
|
|
62
|
+
}
|
|
@@ -51,8 +51,33 @@ export type UserActionOverride = boolean | {
|
|
|
51
51
|
disabledWhen?: RowCrudPredicates['disabledWhen'];
|
|
52
52
|
};
|
|
53
53
|
export interface UserActionsOverride {
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Bare boolean or the object form. Widened from `boolean` in objectui#4646:
|
|
56
|
+
* `@objectstack/spec@17.0.0` types this key
|
|
57
|
+
* `z.union([z.boolean(), RowCrudActionOverrideSchema])`, and the spec's
|
|
58
|
+
* resolver emits the parsed predicates as `CrudAffordances.createPredicates`.
|
|
59
|
+
* The local type said `boolean` — so an author writing the shape the spec
|
|
60
|
+
* accepts, and that the related-list toolbar now honours
|
|
61
|
+
* (`RelatedRecordActionsBridge`, objectui#4646), was rejected by objectui's
|
|
62
|
+
* own type while the runtime handled it.
|
|
63
|
+
*/
|
|
64
|
+
create?: UserActionOverride;
|
|
65
|
+
/**
|
|
66
|
+
* Bare boolean or the object form — the SAME union as `create` above, which
|
|
67
|
+
* is how `@objectstack/spec@17.0.0` types the two toolbar keys
|
|
68
|
+
* (`z.union([z.boolean(), RowCrudActionOverrideSchema])` for each), and its
|
|
69
|
+
* resolver emits `CrudAffordances.importPredicates` beside
|
|
70
|
+
* `createPredicates`.
|
|
71
|
+
*
|
|
72
|
+
* Widened in objectui#5142, deliberately IN THE SAME change that gave the
|
|
73
|
+
* object-list toolbar's Import button a consumer for those predicates
|
|
74
|
+
* (`ObjectView`). objectui#4646 left this key narrow on purpose and said why:
|
|
75
|
+
* widening a type ahead of its consumer re-declares the inert-metadata defect
|
|
76
|
+
* — objectui would have *claimed* to accept the object form while still
|
|
77
|
+
* dropping the predicate. Type and consumer travel together, so the
|
|
78
|
+
* declaration stays an honest statement of what this renderer honours.
|
|
79
|
+
*/
|
|
80
|
+
import?: UserActionOverride;
|
|
56
81
|
edit?: UserActionOverride;
|
|
57
82
|
delete?: UserActionOverride;
|
|
58
83
|
exportCsv?: boolean;
|
|
@@ -35,13 +35,33 @@ export declare const DENSITY_MODE_TO_ROW_HEIGHT: Record<DensityMode, RowHeight>;
|
|
|
35
35
|
*/
|
|
36
36
|
export declare const ROW_HEIGHT_TO_DENSITY_MODE: Record<RowHeight, DensityMode>;
|
|
37
37
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
38
|
+
* Runtime reader for {@link ROW_HEIGHT_TO_DENSITY_MODE}: the spec's five row
|
|
39
|
+
* heights narrowed onto the renderer's three densities, and **nothing else**.
|
|
40
|
+
*
|
|
41
|
+
* The parameter stays `unknown` because this is the boundary user-authored view
|
|
42
|
+
* metadata actually crosses — `ListViewSchema.rowHeight` is statically a
|
|
43
|
+
* `RowHeight`, but the value arrives from stored view definitions TypeScript
|
|
44
|
+
* never saw (`ObjectView`: `viewDef.rowHeight ?? listSchema.rowHeight`). The
|
|
45
|
+
* type-level half of the guarantee is the `Record<RowHeight, …>` on the table
|
|
46
|
+
* above, which fails the build when the spec grows a row height.
|
|
47
|
+
*
|
|
48
|
+
* An off-spec value gets NO density (objectui#4440). It used to be coerced to
|
|
49
|
+
* `comfortable`, which is the opposite of what `@object-ui/react`'s spec bridge
|
|
50
|
+
* answers for the same string after objectui#4352 — one metadata-driven system
|
|
51
|
+
* holding two answers for one input. AGENTS.md #0.1 decides which one survives:
|
|
52
|
+
* a renderer-side rehabilitation of off-spec metadata is a second de-facto
|
|
53
|
+
* contract, and one strict contract beats N. The producer is where a bad
|
|
54
|
+
* `rowHeight` gets fixed.
|
|
55
|
+
*
|
|
56
|
+
* Callers apply their own "nothing was said" default to `undefined`, so an
|
|
57
|
+
* off-spec row height now renders exactly like an absent one — `'compact'` in
|
|
58
|
+
* both `ListView` and `ObjectGrid`.
|
|
59
|
+
*
|
|
60
|
+
* `hasOwnProperty`, not `in`: `in` walks the prototype chain, so `'toString'`
|
|
61
|
+
* used to come back as `Object.prototype.toString` — a FUNCTION returned from
|
|
62
|
+
* something typed `DensityMode`.
|
|
43
63
|
*/
|
|
44
|
-
export declare function rowHeightToDensityMode(rowHeight: unknown): DensityMode;
|
|
64
|
+
export declare function rowHeightToDensityMode(rowHeight: unknown): DensityMode | undefined;
|
|
45
65
|
/**
|
|
46
66
|
* ObjectUI's `list-view` node historically used a different vocabulary from
|
|
47
67
|
* `@objectstack/spec` for the same concepts (`fields` where the spec says
|
|
@@ -105,5 +125,15 @@ export declare function rowHeightToDensityMode(rowHeight: unknown): DensityMode;
|
|
|
105
125
|
* breaking change with no inventory. Runs AFTER the `fields` → `columns` fold
|
|
106
126
|
* above, so a view that spells its column list the legacy way still gets its
|
|
107
127
|
* entries canonicalized.
|
|
128
|
+
* - the four PER-VIEW-TYPE config aliases phase 3 carried over
|
|
129
|
+
* ({@link PER_VIEW_CONFIG_ALIASES}): `kanban.groupField` → `groupByField`,
|
|
130
|
+
* `kanban.cardFields` → `columns`, `gallery.imageField` → `coverField`,
|
|
131
|
+
* `timeline.dateField` → `startDateField`. One read-site changes behaviour
|
|
132
|
+
* as a result, and it is a CORRECTION of the same inverted precedence A2
|
|
133
|
+
* fixed for `densityMode`: `ListView`'s kanban adapter resolves
|
|
134
|
+
* `cardFields || columns`, i.e. legacy over canonical, so a config carrying
|
|
135
|
+
* BOTH used to render the legacy value. After the fold the canonical
|
|
136
|
+
* `columns` reaches it. Every other reader of these four was already
|
|
137
|
+
* canonical-first and is unaffected.
|
|
108
138
|
*/
|
|
109
139
|
export declare function normalizeListViewSchema<T>(schema: T): T;
|