@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
|
@@ -5,22 +5,6 @@
|
|
|
5
5
|
* This source code is licensed under the MIT license found in the
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
|
-
/**
|
|
9
|
-
* chart-series — shape a semantic-layer `queryDataset` result into the
|
|
10
|
-
* `{ data, xAxisKey, series }` triple a chart renderer consumes (ADR-0021, #1759).
|
|
11
|
-
*
|
|
12
|
-
* Shared by `ObjectChart` (plugin-charts) and `DatasetWidget` (plugin-dashboard)
|
|
13
|
-
* so multi-dimension charts visualise identically across surfaces.
|
|
14
|
-
*
|
|
15
|
-
* Rules:
|
|
16
|
-
* - **2+ dimensions, single measure** → pivot the SECOND dimension into one
|
|
17
|
-
* series per distinct value (grouped/coloured bars). `xAxisKey` = first
|
|
18
|
-
* dimension; each output row is one first-dimension bucket with a column per
|
|
19
|
-
* second-dimension value holding the measure. This makes the second dimension
|
|
20
|
-
* visible instead of just repeating the x-axis label.
|
|
21
|
-
* - **otherwise** (single dimension, or multiple measures) → first dimension is
|
|
22
|
-
* the x-axis and each measure is its own series (long format passes through).
|
|
23
|
-
*/
|
|
24
8
|
export interface ChartResultField {
|
|
25
9
|
name: string;
|
|
26
10
|
label?: string;
|
|
@@ -47,7 +31,150 @@ export interface ChartSeriesResult {
|
|
|
47
31
|
xAxisKey: string | undefined;
|
|
48
32
|
series: ChartSeriesBinding[];
|
|
49
33
|
}
|
|
50
|
-
|
|
34
|
+
/**
|
|
35
|
+
* The bucket a NULL/undefined category VALUE renders under, when the caller
|
|
36
|
+
* supplies no localized label of its own (objectui#4466).
|
|
37
|
+
*
|
|
38
|
+
* English by construction and deliberately so: this package is React-free and
|
|
39
|
+
* i18n-free (see {@link OptionLabelTranslator} for the same reason stated one
|
|
40
|
+
* layer down), so the locale bundle cannot be read here. Every renderer that
|
|
41
|
+
* has an i18n provider passes its own resolved label through
|
|
42
|
+
* {@link ChartSeriesOptions.nullCategoryLabel} — this constant is the floor
|
|
43
|
+
* that keeps a provider-less host (or a caller that has not been wired yet)
|
|
44
|
+
* drawing the group rather than dropping it.
|
|
45
|
+
*
|
|
46
|
+
* Exported so a consumer can recognise the bucket it will be handed —
|
|
47
|
+
* {@link findChartSeriesRow} uses the same default, which is what makes a
|
|
48
|
+
* clicked bucket bar resolve back to its (raw null) dataset row.
|
|
49
|
+
*/
|
|
50
|
+
export declare const NULL_CATEGORY_LABEL = "(None)";
|
|
51
|
+
/**
|
|
52
|
+
* The IDENTITY of the chart bucket a first-dimension raw value falls in —
|
|
53
|
+
* distinct from the string that bucket DISPLAYS (objectui#4508).
|
|
54
|
+
*
|
|
55
|
+
* Deliberately the SAME encoder the pivot TABLE already keys its buckets with
|
|
56
|
+
* (`pivotBucketId` over a `pivotDimensionValue`-normalized value), because the
|
|
57
|
+
* two surfaces answer the same question about the same dataset rows and were
|
|
58
|
+
* answering it differently: `buildPivot` gives a null dimension value a bucket
|
|
59
|
+
* id no string can spell, while the chart used the display label as its own
|
|
60
|
+
* key. That gap is the whole of objectui#4508 — see `dataset-pivot.ts` for why
|
|
61
|
+
* the encoding is JSON rather than a delimiter or a placeholder character.
|
|
62
|
+
*
|
|
63
|
+
* Two collisions follow from a label-as-key, and one encoder closes both:
|
|
64
|
+
* - `null` and `''` are different groups. `String(x ?? '')` made them one
|
|
65
|
+
* bucket, so two groups drew ONE bar and one of them lost its drill.
|
|
66
|
+
* `[null]` and `[""]` are different ids.
|
|
67
|
+
* - a stored value that literally spells the bucket label (`'(None)'`, or any
|
|
68
|
+
* localized `nullCategoryLabel` — `'(未指定)'` and the other nine packs) is
|
|
69
|
+
* not the null group. `["(None)"]` and `[null]` are different ids, so the
|
|
70
|
+
* click resolves to the rows its own bar was drawn from.
|
|
71
|
+
*/
|
|
72
|
+
export declare function chartBucketId(rawValue: unknown): string;
|
|
73
|
+
/**
|
|
74
|
+
* The key an emitted chart row carries its {@link chartBucketId} under, when it
|
|
75
|
+
* carries one — the "alongside the display label" half of objectui#4508.
|
|
76
|
+
*
|
|
77
|
+
* **Written exactly when the display string does not name one bucket**, i.e.
|
|
78
|
+
* when two or more DISTINCT buckets render the same axis text. That is not a
|
|
79
|
+
* cost-saving heuristic, it is the complete condition: with the reader's
|
|
80
|
+
* matching made exact (see {@link findChartSeriesRow}), a display string that
|
|
81
|
+
* one bucket alone produces already identifies it, and the rows a chart is
|
|
82
|
+
* drawn from are also an authoring-visible surface (`data` on a `chart`
|
|
83
|
+
* schema) that renderer-internal metadata should not appear in for every
|
|
84
|
+
* ordinary chart. Rows that do not carry the category key at all never get one
|
|
85
|
+
* — they have no bucket, and that shape belongs to `hasNoCategoryKey`
|
|
86
|
+
* (framework#4033), not here.
|
|
87
|
+
*
|
|
88
|
+
* Read it with {@link chartRowBucketId} rather than the literal key.
|
|
89
|
+
*/
|
|
90
|
+
export declare const CHART_BUCKET_ID_KEY = "__bucketId";
|
|
91
|
+
/**
|
|
92
|
+
* Read the bucket identity off a row a chart was drawn from, or `undefined`
|
|
93
|
+
* when the row carries none (an unambiguous bucket, or rows a caller built
|
|
94
|
+
* itself rather than through {@link buildChartSeries}).
|
|
95
|
+
*
|
|
96
|
+
* Takes `unknown` because its callers hold a click payload, not a typed row:
|
|
97
|
+
* recharts hands a pie/funnel click a SPREAD COPY of the data item, which is
|
|
98
|
+
* why the identity is an ordinary enumerable property and not a symbol or a
|
|
99
|
+
* non-enumerable one — either of those would silently not survive the copy.
|
|
100
|
+
*/
|
|
101
|
+
export declare function chartRowBucketId(row: unknown): string | undefined;
|
|
102
|
+
/** Per-call knobs shared by {@link buildChartSeries} / {@link findChartSeriesRow}. */
|
|
103
|
+
export interface ChartSeriesOptions {
|
|
104
|
+
/**
|
|
105
|
+
* Display label for a category whose value is `null`/`undefined`. Defaults to
|
|
106
|
+
* {@link NULL_CATEGORY_LABEL}.
|
|
107
|
+
*
|
|
108
|
+
* Pass the SAME value to both helpers: `buildChartSeries` writes it into the
|
|
109
|
+
* rendered rows and `findChartSeriesRow` matches a clicked category against
|
|
110
|
+
* it, so a mismatch costs the null bucket its drill-through.
|
|
111
|
+
*/
|
|
112
|
+
nullCategoryLabel?: string;
|
|
113
|
+
}
|
|
114
|
+
/** Per-CLICK knobs for {@link findChartSeriesRow}, on top of the shared ones. */
|
|
115
|
+
export interface ChartDrillLookupOptions extends ChartSeriesOptions {
|
|
116
|
+
/**
|
|
117
|
+
* The clicked bucket's {@link chartBucketId}, when the renderer could read one
|
|
118
|
+
* off the row the segment was drawn from ({@link chartRowBucketId} over
|
|
119
|
+
* {@link CHART_BUCKET_ID_KEY}) — objectui#4508.
|
|
120
|
+
*
|
|
121
|
+
* Supplied ⇒ AUTHORITATIVE: the lookup matches on identity and ignores
|
|
122
|
+
* `category` entirely, which is what lets two bars painting the same axis
|
|
123
|
+
* text drill to their own rows instead of both resolving to the first.
|
|
124
|
+
* Absent ⇒ the bucket's display string identified it on its own (that is
|
|
125
|
+
* exactly when `buildChartSeries` writes no id), or the rows were not built
|
|
126
|
+
* by `buildChartSeries` at all, and the display match answers.
|
|
127
|
+
*/
|
|
128
|
+
bucketId?: string;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* What a chart renderer hands a drill handler when a segment is clicked.
|
|
132
|
+
*
|
|
133
|
+
* Declared HERE, beside the two helpers that write and read a bucket, rather
|
|
134
|
+
* than re-spelled inline in each of the three packages that pass it along
|
|
135
|
+
* (`AdvancedChartImpl` builds it, `ChartRenderer` and `ObjectChart` forward it,
|
|
136
|
+
* `DatasetWidget` consumes it). It was three inline literals when
|
|
137
|
+
* {@link ChartDrillLookupOptions.bucketId} needed a carrier, and a field added
|
|
138
|
+
* to one of three copies reaches the consumer as `undefined` with nothing red.
|
|
139
|
+
*/
|
|
140
|
+
export interface ChartSegmentClickEvent {
|
|
141
|
+
/** The clicked bucket's axis TEXT — what the user sees on the tick. */
|
|
142
|
+
category?: string;
|
|
143
|
+
/**
|
|
144
|
+
* The clicked bucket's IDENTITY, when its row carried one (objectui#4508).
|
|
145
|
+
* Feed it straight to {@link ChartDrillLookupOptions.bucketId}; it is the
|
|
146
|
+
* only field that survives two buckets painting the same `category`.
|
|
147
|
+
*/
|
|
148
|
+
categoryId?: string;
|
|
149
|
+
/** The clicked series' key — a measure, or a pivoted second-dimension value. */
|
|
150
|
+
series?: string;
|
|
151
|
+
/**
|
|
152
|
+
* The clicked series' DISPLAY LABEL, when the renderer knew one
|
|
153
|
+
* (objectui#4682) — `series[].label` as {@link buildChartSeries} assigned it.
|
|
154
|
+
*
|
|
155
|
+
* {@link ChartSegmentClickEvent.series} stays the LOOKUP key and this never
|
|
156
|
+
* substitutes for it: {@link findChartSeriesRow} resolves a key through the
|
|
157
|
+
* same assignment `buildChartSeries` made, and a label cannot be resolved
|
|
158
|
+
* that way — it is not unique (that is precisely why the colliding groups key
|
|
159
|
+
* by identity instead). This field exists for what a key cannot do: be SHOWN.
|
|
160
|
+
*
|
|
161
|
+
* The two strings are equal for every ordinary group, which is why reading
|
|
162
|
+
* the key as a title went unnoticed. They part company when a group's label
|
|
163
|
+
* cannot name it — objectui#4508's collision on the series axis — and the key
|
|
164
|
+
* becomes an opaque `chartBucketId`. A drawer titled `Backlog / [null]` over
|
|
165
|
+
* a segment the user saw labelled `(None)` reads as broken DATA rather than
|
|
166
|
+
* as a broken title, which is why the label travels with the click rather
|
|
167
|
+
* than being re-derived by the consumer.
|
|
168
|
+
*
|
|
169
|
+
* Absent when the renderer resolved no series, or when the series carries no
|
|
170
|
+
* label distinct from its key; a consumer titles itself from
|
|
171
|
+
* `seriesLabel ?? series` and so is unchanged wherever it is absent.
|
|
172
|
+
*/
|
|
173
|
+
seriesLabel?: string;
|
|
174
|
+
/** The measure value at the click point. */
|
|
175
|
+
value?: number;
|
|
176
|
+
}
|
|
177
|
+
export declare function buildChartSeries(rows: Array<Record<string, unknown>> | null | undefined, dimensions: string[] | null | undefined, values: string[] | null | undefined, fields?: ChartResultField[] | null, options?: ChartSeriesOptions): ChartSeriesResult;
|
|
51
178
|
/**
|
|
52
179
|
* Resolve select/enum dimension VALUES to display LABELS in chart rows.
|
|
53
180
|
*
|
|
@@ -83,10 +210,51 @@ export declare function relabelDimensions(rows: Array<Record<string, unknown>> |
|
|
|
83
210
|
* BOTH the x-axis dimension (`category`) and the series dimension (`seriesKey`).
|
|
84
211
|
* - **otherwise** → match the x-axis (first) dimension only.
|
|
85
212
|
*
|
|
86
|
-
*
|
|
87
|
-
* surfaces as `category`
|
|
213
|
+
* The x-axis comparison is string-wise on the rows' display values (which is
|
|
214
|
+
* what the chart surfaces as `category`), unless the click carried a bucket
|
|
215
|
+
* IDENTITY — {@link ChartDrillLookupOptions.bucketId} — in which case that is
|
|
216
|
+
* authoritative and the display string is not consulted at all. Returns `-1`
|
|
217
|
+
* when nothing matches.
|
|
218
|
+
*
|
|
219
|
+
* The SERIES comparison is not string-wise at all as of objectui#4673: a series
|
|
220
|
+
* key is a `dataKey` {@link buildChartSeries} assigned, so it is resolved back
|
|
221
|
+
* through the same {@link pivotSeriesBuckets} assignment (same rows, same
|
|
222
|
+
* `nullCategoryLabel`) to the group IDENTITY it names, and rows are matched on
|
|
223
|
+
* that. A null-valued second dimension had no series at all before, so its
|
|
224
|
+
* segment could not be clicked; now it can, and `String(r[gDim] ?? '')` would
|
|
225
|
+
* have answered that click with an empty-string group's rows — the same
|
|
226
|
+
* conflation objectui#4508 removed on the x-axis.
|
|
227
|
+
*
|
|
228
|
+
* The NULL-category bucket {@link buildChartSeries} renders (objectui#4466) is
|
|
229
|
+
* matched back to its row here, and it has to be: the caller passes the rows it
|
|
230
|
+
* charted FROM (still carrying the raw null) while the click event carries the
|
|
231
|
+
* bucket LABEL, so without this the one bar the fix made visible would resolve
|
|
232
|
+
* to `-1` and its drill-through would silently no-op. Pass the same
|
|
233
|
+
* `nullCategoryLabel` both helpers were given.
|
|
234
|
+
*
|
|
235
|
+
* **A null category reads as its bucket LABEL and nothing else** (objectui#4508).
|
|
236
|
+
* It used to read as the label OR the empty string, on the stated grounds that
|
|
237
|
+
* `''` is the drill layer's own spelling of "no group value" — but no producer
|
|
238
|
+
* of this function's `category` argument writes that spelling (the one
|
|
239
|
+
* production caller feeds it a chart click's category, and `computeDrillFilter`
|
|
240
|
+
* is downstream of the drill, not upstream of this lookup), while `''` IS what
|
|
241
|
+
* a genuine empty-string group paints. So the tolerance cost that group its own
|
|
242
|
+
* drill and gave it a null group's records instead — a WRONG click, not a dead
|
|
243
|
+
* one. `''` now means the empty-string group, which is what it says.
|
|
244
|
+
*
|
|
245
|
+
* **`xOf` covers BOTH arms, which is what let objectui#4497 bucket the pivot
|
|
246
|
+
* branch without touching this function.** Worth stating because the reverse
|
|
247
|
+
* would be silent: the pivot's emitted rows are AGGREGATED (`byX` collapses N
|
|
248
|
+
* raw rows into one bucket per first-dimension value), so they are NOT
|
|
249
|
+
* index-aligned with anything, and a caller cannot drill by the emitted row's
|
|
250
|
+
* position. It drills by SEARCHING the raw rows here and indexing
|
|
251
|
+
* `drillRawRows` with what this returns — see `DatasetWidget.handleChartDrill`,
|
|
252
|
+
* the one production caller. So the pivot's map key and its emitted display
|
|
253
|
+
* value being two different things never reached the drill: the raw rows this
|
|
254
|
+
* searches still carry their null either way, and `xOf` reads the label back to
|
|
255
|
+
* them in the multi-dimension arm exactly as in the single-dimension one.
|
|
88
256
|
*/
|
|
89
|
-
export declare function findChartSeriesRow(rows: Array<Record<string, unknown>> | null | undefined, dimensions: string[] | null | undefined, values: string[] | null | undefined, category: string | undefined, seriesKey?: string): number;
|
|
257
|
+
export declare function findChartSeriesRow(rows: Array<Record<string, unknown>> | null | undefined, dimensions: string[] | null | undefined, values: string[] | null | undefined, category: string | undefined, seriesKey?: string, options?: ChartDrillLookupOptions): number;
|
|
90
258
|
/**
|
|
91
259
|
* Build a per-category colour map from a select/lookup field's `options`.
|
|
92
260
|
*
|
|
@@ -129,14 +297,213 @@ export declare function buildCategoryOrder(options: unknown): string[] | null;
|
|
|
129
297
|
* after the declared ones rather than dropping them).
|
|
130
298
|
*/
|
|
131
299
|
export declare function buildCategoryRank(order: string[] | null | undefined): Map<string, number> | null;
|
|
300
|
+
/**
|
|
301
|
+
* The i18n seam of the analytics label net (objectui#4030).
|
|
302
|
+
*
|
|
303
|
+
* `(storedValue, authoredLabel) => displayLabel` — the SAME signature
|
|
304
|
+
* `resolveGroupByLabels` (plugin-charts) already takes for the legacy aggregate
|
|
305
|
+
* path, which callers bind to `useSafeFieldLabel().fieldOptionLabel(object,
|
|
306
|
+
* field, …)`. That resolver reads `{ns}.fieldOptions.<object>.<field>.<value>`,
|
|
307
|
+
* the one convention list and form surfaces already translate select options
|
|
308
|
+
* through (`useObjectLabel.translateOptions`, and `@objectstack/spec` names
|
|
309
|
+
* objectui as its reader). Analytics reuses that channel rather than growing a
|
|
310
|
+
* chart-side per-locale dialect.
|
|
311
|
+
*
|
|
312
|
+
* Omitted everywhere it isn't available (no i18n provider, an unresolvable
|
|
313
|
+
* owning object): every helper below then behaves exactly as it did before this
|
|
314
|
+
* seam existed.
|
|
315
|
+
*/
|
|
316
|
+
export type OptionLabelTranslator = (value: string, authoredLabel: string) => string;
|
|
317
|
+
/**
|
|
318
|
+
* Run a select field's `options` through the locale bundle, returning options
|
|
319
|
+
* whose `label` is the translated one (objectui#4030).
|
|
320
|
+
*
|
|
321
|
+
* The mirror of `useObjectLabel().translateOptions` — the channel list and form
|
|
322
|
+
* surfaces localize select options through — kept pure and shape-tolerant so
|
|
323
|
+
* the analytics net can apply it at the point the resolved options are read.
|
|
324
|
+
* Everything downstream ({@link buildOptionColorMap},
|
|
325
|
+
* {@link buildCategoryOrder}) keeps reading `option.label` and needs no
|
|
326
|
+
* knowledge of i18n, exactly like `SelectCellRenderer` on the list side.
|
|
327
|
+
*
|
|
328
|
+
* Colour, order and every other option key survive untouched — only `label`
|
|
329
|
+
* changes. Returns the input ARRAY ITSELF when there is no translator or
|
|
330
|
+
* nothing translated, so an untranslated app keeps the identities (and the
|
|
331
|
+
* memo/render behaviour) it had before.
|
|
332
|
+
*/
|
|
333
|
+
export declare function localizeFieldOptions(options: unknown, translate?: OptionLabelTranslator): unknown;
|
|
132
334
|
/**
|
|
133
335
|
* Build a `{ value → label }` map from a select/enum field's `options`, for
|
|
134
336
|
* resolving a grouped dimension's stored value to its display label (fed to
|
|
135
337
|
* {@link relabelDimensions}). Mirrors {@link buildOptionColorMap}.
|
|
136
338
|
*
|
|
137
339
|
* Options may be `{ value, label }` objects or bare strings (value == label —
|
|
138
|
-
* nothing to relabel). Only entries whose
|
|
139
|
-
*
|
|
340
|
+
* nothing to relabel). Only entries whose display label actually differs from
|
|
341
|
+
* the key are kept, so the map is empty (→ `null`) when relabeling would be a
|
|
140
342
|
* no-op and the caller can skip it entirely.
|
|
343
|
+
*
|
|
344
|
+
* **With a `translate` seam (objectui#4030) the map gains a SECOND key per
|
|
345
|
+
* option: the AUTHORED label.** A dimension's rows reach this net keyed either
|
|
346
|
+
* way — value-keyed when the server did not resolve the dimension (the whole
|
|
347
|
+
* reason this net exists), already label-keyed when it did (ADR-0021) — and
|
|
348
|
+
* the reported symptom is the second case: a chart legend showing the object's
|
|
349
|
+
* English `label` verbatim beside a related list showing the translation. One
|
|
350
|
+
* key resolves `orion`, the other re-translates `Orion Engineered Carbons`;
|
|
351
|
+
* `relabelDimensions` is value-wise and idempotent, so whichever the row
|
|
352
|
+
* carries lands on the same translated display.
|
|
353
|
+
*
|
|
354
|
+
* Value keys win over authored-label keys: a stored value that happens to
|
|
355
|
+
* equal some other option's label is still that option's value.
|
|
356
|
+
*
|
|
357
|
+
* Without a translator this is byte-for-byte the pre-#4030 map — the authored
|
|
358
|
+
* label then IS the display, so no second key is ever emitted.
|
|
359
|
+
*/
|
|
360
|
+
export declare function buildDimensionLabelMap(options: unknown, translate?: OptionLabelTranslator): Record<string, string> | null;
|
|
361
|
+
/**
|
|
362
|
+
* The object a relationship field points at, or `undefined` when the field is
|
|
363
|
+
* not a relationship (or names no target).
|
|
364
|
+
*
|
|
365
|
+
* The target lives under `reference` on framework-served field defs; older /
|
|
366
|
+
* spec shapes spell it `reference_to` / `referenceTo` / `reference_to_object`,
|
|
367
|
+
* and any of them may carry a bare name, a one-element array, or `{ object }`.
|
|
368
|
+
* Same canonicalization as the dataset designer's `resolveReferenceTo`.
|
|
369
|
+
*
|
|
370
|
+
* The **type gate is deliberate**: only a declared relationship is walked, so a
|
|
371
|
+
* path segment naming a plain field can never be turned into an object name and
|
|
372
|
+
* fetched speculatively.
|
|
373
|
+
*/
|
|
374
|
+
export declare function resolveRelationshipTarget(fieldDef: unknown): string | undefined;
|
|
375
|
+
/**
|
|
376
|
+
* Resolve each dataset dimension's underlying `field` to that field's select
|
|
377
|
+
* `options`, following DOTTED relationship paths to the object that actually
|
|
378
|
+
* owns the terminal field (objectui#4053).
|
|
379
|
+
*
|
|
380
|
+
* A dimension's `field` is either a local field name (`industry`) or a
|
|
381
|
+
* relationship path (`crm_account.industry`, and — per ADR-0071, which the
|
|
382
|
+
* dataset designer emits — `crm_account.owner.department`). Reading the options
|
|
383
|
+
* as `baseSchema.fields[path]` only ever works for the local spelling: for a
|
|
384
|
+
* dotted path the options live on the RELATED object, the lookup missed
|
|
385
|
+
* silently, and the chart fell through to the raw stored enum while the same
|
|
386
|
+
* field as a local dimension rendered its labels beside it.
|
|
387
|
+
*
|
|
388
|
+
* This is the object-resolution step of that ONE lookup, not a dotted-path
|
|
389
|
+
* variant beside it: a single-segment path never enters the walk and resolves
|
|
390
|
+
* exactly as before, so the local path cannot drift away from the joined one.
|
|
391
|
+
*
|
|
392
|
+
* `loadObjectSchema` supplies each hop's object metadata doc — the caller's
|
|
393
|
+
* existing channel (the same `GET /meta/object/:name` read that produced
|
|
394
|
+
* `baseSchema`), so no new fetch layer is introduced. It is memoized per call,
|
|
395
|
+
* because sibling dimensions routinely share a prefix (`crm_account.industry`
|
|
396
|
+
* alongside `crm_account.type` fetches `crm_account` once), and seeded with
|
|
397
|
+
* `baseSchema` under its own `name` so the base is never re-fetched.
|
|
398
|
+
*
|
|
399
|
+
* Best-effort by construction: a hop that is not a relationship, a target that
|
|
400
|
+
* cannot be loaded, or a terminal field that carries no `options` simply yields
|
|
401
|
+
* no entry, and the caller keeps the raw value. Returns `{ fieldPath → options }`
|
|
402
|
+
* for the paths that did resolve.
|
|
403
|
+
*
|
|
404
|
+
* Thin wrapper over {@link resolveDimensionFieldMeta}, which is the same ONE
|
|
405
|
+
* walk keeping the identity of what it found. Callers that translate option
|
|
406
|
+
* labels need that identity (the i18n key is
|
|
407
|
+
* `fieldOptions.<owningObject>.<terminalField>.<value>`, and for a dotted path
|
|
408
|
+
* the owner is the RELATIONSHIP TARGET, not the dataset's base object) — see
|
|
409
|
+
* objectui#4030.
|
|
410
|
+
*/
|
|
411
|
+
export declare function resolveDimensionFieldOptions(baseSchema: unknown, fieldPaths: Array<string | undefined | null>, loadObjectSchema: (objectName: string) => Promise<unknown>): Promise<Record<string, unknown>>;
|
|
412
|
+
/**
|
|
413
|
+
* What {@link resolveDimensionFieldOptions} found for ONE dimension field path
|
|
414
|
+
* — the options AND the identity of the field they belong to.
|
|
415
|
+
*/
|
|
416
|
+
export interface DimensionFieldMeta {
|
|
417
|
+
/**
|
|
418
|
+
* The object that OWNS the terminal field: the dataset's base object for a
|
|
419
|
+
* local path, the last relationship's TARGET for a dotted one. `undefined`
|
|
420
|
+
* only when the base schema carries no `name` and the path is local.
|
|
421
|
+
*/
|
|
422
|
+
object: string | undefined;
|
|
423
|
+
/** The terminal field's own name — the LAST path segment, never the path. */
|
|
424
|
+
field: string;
|
|
425
|
+
/** The terminal field's `options`, exactly as the metadata doc carries them. */
|
|
426
|
+
options: unknown;
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* {@link resolveDimensionFieldOptions} keeping what it walked THROUGH.
|
|
430
|
+
*
|
|
431
|
+
* Same single walk, same best-effort tolerance, same memoized loader — it just
|
|
432
|
+
* returns `{ object, field, options }` per resolved path instead of the options
|
|
433
|
+
* alone. Split out for objectui#4030: applying the locale bundle to a resolved
|
|
434
|
+
* option label needs the key the bundle is written under
|
|
435
|
+
* (`fieldOptions.<object>.<field>.<value>`), and for `crm_account.industry`
|
|
436
|
+
* that object is `crm_account` — the walk already knows it and used to drop it
|
|
437
|
+
* on the floor. Deriving it at the call site would mean re-walking the
|
|
438
|
+
* relationship chain a second time, i.e. two derivations of one fact.
|
|
439
|
+
*/
|
|
440
|
+
export declare function resolveDimensionFieldMeta(baseSchema: unknown, fieldPaths: Array<string | undefined | null>, loadObjectSchema: (objectName: string) => Promise<unknown>): Promise<Record<string, DimensionFieldMeta>>;
|
|
441
|
+
/**
|
|
442
|
+
* The locale-bundle resolver a caller supplies to translate one option label —
|
|
443
|
+
* `useSafeFieldLabel().fieldOptionLabel` in every current caller, i.e. the
|
|
444
|
+
* `{ns}.fieldOptions.<object>.<field>.<value>` convention list and form
|
|
445
|
+
* surfaces already translate select options through (objectui#4030).
|
|
446
|
+
*
|
|
447
|
+
* Taken as a plain function so this package needs no knowledge of i18n, React,
|
|
448
|
+
* or which provider is mounted — the same reason {@link OptionLabelTranslator}
|
|
449
|
+
* is a bare function type.
|
|
450
|
+
*/
|
|
451
|
+
export type FieldOptionLabelResolver = (object: string, field: string, value: string, authoredLabel: string) => string;
|
|
452
|
+
/**
|
|
453
|
+
* Bind a {@link FieldOptionLabelResolver} to ONE resolved dimension field,
|
|
454
|
+
* yielding the {@link OptionLabelTranslator} the option helpers take.
|
|
455
|
+
*
|
|
456
|
+
* The binding is the part that is easy to get wrong, which is why it is stated
|
|
457
|
+
* once here rather than at each call site: the translator must be bound to the
|
|
458
|
+
* object that OWNS the terminal field — `crm_account` for `crm_account.industry`,
|
|
459
|
+
* NOT the dataset's base object — because that owner is the key the locale
|
|
460
|
+
* bundle is written under. {@link resolveDimensionFieldMeta} already walked the
|
|
461
|
+
* relationship chain and kept that owner; this just reads it.
|
|
462
|
+
*
|
|
463
|
+
* Returns `undefined` — i.e. "no translation, keep the authored labels" — when
|
|
464
|
+
* the owner could not be resolved or no resolver was supplied. Every helper
|
|
465
|
+
* downstream then behaves exactly as it did before the #4030 seam existed.
|
|
466
|
+
*/
|
|
467
|
+
export declare function dimensionOptionTranslator(meta: DimensionFieldMeta | undefined, fieldOptionLabel?: FieldOptionLabelResolver): OptionLabelTranslator | undefined;
|
|
468
|
+
/** One dimension paired with the field path its values resolve through. */
|
|
469
|
+
export interface DimensionRelabelTarget {
|
|
470
|
+
/** The dimension name as the surface renders it (the row key). */
|
|
471
|
+
dim: string;
|
|
472
|
+
/** That dimension's underlying field path, as the dataset query reported it. */
|
|
473
|
+
path: string;
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Derive the `{ dimension → { value|authoredLabel → displayLabel } }` maps that
|
|
477
|
+
* {@link relabelDimensions} consumes, from metadata {@link loadDimensionFieldMeta}
|
|
478
|
+
* (or {@link resolveDimensionFieldMeta}) already resolved.
|
|
479
|
+
*
|
|
480
|
+
* This is the LOCALE-APPLYING step, and keeping it separate from the load is
|
|
481
|
+
* the whole point of the split (objectui#4030 / PR #4324): the fetched metadata
|
|
482
|
+
* stays locale-free, so switching language re-derives these maps in a render
|
|
483
|
+
* memo instead of re-issuing the metadata read. A caller that folds the two
|
|
484
|
+
* together reintroduces a refetch on every language switch — the defect #4324
|
|
485
|
+
* fixed, and the property objectui#4389 lifted here so it is stated once.
|
|
486
|
+
*
|
|
487
|
+
* Best-effort per dimension, exactly as the longhand copies were: a path that
|
|
488
|
+
* resolved no metadata, or whose field carries no `options`, contributes no
|
|
489
|
+
* entry, and `relabelDimensions` then returns the caller's rows by identity.
|
|
490
|
+
* Returns `null` — never an empty object — when nothing resolved, so callers
|
|
491
|
+
* can skip the relabel entirely.
|
|
492
|
+
*/
|
|
493
|
+
export declare function deriveDimensionLabelMaps(metaByPath: Record<string, DimensionFieldMeta> | null | undefined, relabel: readonly DimensionRelabelTarget[] | null | undefined, fieldOptionLabel?: FieldOptionLabelResolver): Record<string, Record<string, string>> | null;
|
|
494
|
+
/**
|
|
495
|
+
* Load one dataset query's dimension field metadata: fetch the base object's
|
|
496
|
+
* schema, then resolve every dimension's field path against it.
|
|
497
|
+
*
|
|
498
|
+
* The two-step composition every caller wrote out by hand. `loadObjectSchema`
|
|
499
|
+
* stays the CALLER's channel — this package issues no reads of its own and has
|
|
500
|
+
* no opinion about transport, which is what keeps it free of React, of
|
|
501
|
+
* `SchemaRendererContext`, and of the authenticated-`apiFetch` concern
|
|
502
|
+
* (objectui#4121) that belongs to the layer holding the host context.
|
|
503
|
+
*
|
|
504
|
+
* The same loader serves both steps by design: {@link resolveDimensionFieldMeta}
|
|
505
|
+
* memoizes it per call and seeds the cache with the base schema under its own
|
|
506
|
+
* `name`, so the base object is read ONCE even when several dotted dimensions
|
|
507
|
+
* walk back through it. That read count is pinned on both consuming surfaces.
|
|
141
508
|
*/
|
|
142
|
-
export declare function
|
|
509
|
+
export declare function loadDimensionFieldMeta(loadObjectSchema: (objectName: string) => Promise<unknown>, object: string, fieldPaths: Array<string | undefined | null>): Promise<Record<string, DimensionFieldMeta>>;
|