@object-ui/core 17.5.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 +7404 -0
- package/README.md +127 -26
- package/dist/actions/ActionEngine.d.ts +5 -3
- package/dist/actions/ActionEngine.js +14 -10
- package/dist/actions/ActionRunner.d.ts +363 -30
- package/dist/actions/ActionRunner.js +544 -58
- package/dist/actions/TransactionManager.d.ts +7 -2
- package/dist/actions/actionKeys.d.ts +75 -13
- package/dist/actions/actionKeys.js +131 -16
- package/dist/actions/actionResponse.js +3 -3
- package/dist/actions/index.d.ts +2 -0
- package/dist/actions/index.js +2 -0
- package/dist/actions/recordDelete.d.ts +118 -0
- package/dist/actions/recordDelete.js +112 -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/adapters/ValueDataSource.d.ts +6 -1
- package/dist/adapters/ValueDataSource.js +1090 -98
- package/dist/adapters/batchTransaction.d.ts +1 -1
- package/dist/adapters/batchTransaction.js +1 -1
- package/dist/builder/schema-builder.d.ts +38 -81
- package/dist/builder/schema-builder.js +33 -152
- package/dist/data-scope/ViewDataProvider.d.ts +1 -1
- package/dist/data-scope/ViewDataProvider.js +21 -1
- package/dist/data-scope/element-data-source.d.ts +261 -7
- package/dist/data-scope/element-data-source.js +342 -52
- package/dist/data-scope/index.d.ts +9 -4
- package/dist/data-scope/index.js +9 -4
- package/dist/evaluator/ExpressionEvaluator.d.ts +128 -1
- package/dist/evaluator/ExpressionEvaluator.js +226 -13
- package/dist/evaluator/FormulaFunctions.d.ts +0 -10
- package/dist/evaluator/FormulaFunctions.js +166 -27
- package/dist/evaluator/SafeExpressionParser.js +0 -4
- package/dist/evaluator/declaredPredicate.d.ts +93 -9
- package/dist/evaluator/declaredPredicate.js +118 -17
- package/dist/evaluator/fieldRules.d.ts +107 -4
- package/dist/evaluator/fieldRules.js +231 -35
- package/dist/evaluator/index.d.ts +2 -0
- package/dist/evaluator/index.js +2 -0
- package/dist/evaluator/listConditional.d.ts +125 -9
- package/dist/evaluator/listConditional.js +167 -26
- package/dist/evaluator/optionRules.d.ts +126 -0
- package/dist/evaluator/optionRules.js +141 -4
- package/dist/evaluator/predicateInput.d.ts +15 -4
- package/dist/evaluator/predicateInput.js +35 -2
- package/dist/evaluator/rowPredicateCanon.d.ts +56 -0
- package/dist/evaluator/rowPredicateCanon.js +158 -0
- package/dist/evaluator/subjectPermissions.d.ts +76 -0
- package/dist/evaluator/subjectPermissions.js +44 -0
- package/dist/evaluator/unevaluablePredicate.d.ts +57 -0
- package/dist/evaluator/unevaluablePredicate.js +70 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +97 -2
- package/dist/protocols/index.d.ts +0 -1
- package/dist/protocols/index.js +9 -1
- package/dist/registry/Registry.d.ts +397 -48
- package/dist/registry/Registry.js +263 -11
- package/dist/registry/WidgetRegistry.js +7 -3
- package/dist/registry/html-tier-intrinsics.d.ts +90 -0
- package/dist/registry/html-tier-intrinsics.js +110 -0
- package/dist/registry/public-blocks.d.ts +8 -0
- package/dist/registry/public-blocks.js +57 -0
- package/dist/theme/ThemeEngine.js +1 -1
- package/dist/theme/index.d.ts +1 -1
- package/dist/theme/index.js +1 -1
- package/dist/types/index.d.ts +92 -13
- package/dist/utils/chart-category-key.d.ts +82 -0
- package/dist/utils/chart-category-key.js +84 -0
- package/dist/utils/chart-measure-key.d.ts +64 -0
- package/dist/utils/chart-measure-key.js +65 -0
- package/dist/utils/chart-presentation.d.ts +188 -0
- package/dist/utils/chart-presentation.js +345 -0
- package/dist/utils/chart-series.d.ts +271 -31
- package/dist/utils/chart-series.js +478 -55
- package/dist/utils/column-identity.d.ts +43 -0
- package/dist/utils/column-identity.js +47 -1
- package/dist/utils/column-sortability.d.ts +157 -0
- package/dist/utils/column-sortability.js +217 -0
- package/dist/utils/dashboard-filters.d.ts +38 -6
- package/dist/utils/dashboard-filters.js +107 -61
- package/dist/utils/dataset-format.d.ts +211 -30
- package/dist/utils/dataset-format.js +407 -52
- package/dist/utils/date-display.d.ts +354 -0
- package/dist/utils/date-display.js +463 -0
- package/dist/utils/date-macros.js +7 -0
- package/dist/utils/debug.js +7 -0
- package/dist/utils/drill-down.d.ts +78 -0
- package/dist/utils/drill-down.js +93 -0
- package/dist/utils/emptiness.d.ts +96 -0
- package/dist/utils/emptiness.js +101 -0
- package/dist/utils/expand-fields.d.ts +171 -11
- package/dist/utils/expand-fields.js +171 -11
- package/dist/utils/extract-records.d.ts +65 -5
- package/dist/utils/extract-records.js +65 -8
- package/dist/utils/filter-converter.d.ts +152 -4
- package/dist/utils/filter-converter.js +1092 -22
- package/dist/utils/filter-tokens.d.ts +49 -5
- package/dist/utils/filter-tokens.js +105 -34
- package/dist/utils/freeze-schema.d.ts +38 -1
- package/dist/utils/freeze-schema.js +10 -0
- package/dist/utils/grouping-fields.d.ts +61 -0
- package/dist/utils/grouping-fields.js +84 -0
- package/dist/utils/humanize-label.d.ts +60 -0
- package/dist/utils/humanize-label.js +62 -0
- package/dist/utils/managedBy.d.ts +30 -5
- package/dist/utils/managedBy.js +5 -5
- package/dist/utils/native-date-value.d.ts +48 -0
- package/dist/utils/native-date-value.js +167 -0
- package/dist/utils/non-grid-row-ceiling.d.ts +143 -0
- package/dist/utils/non-grid-row-ceiling.js +141 -0
- package/dist/utils/normalize-list-view.d.ts +131 -1
- package/dist/utils/normalize-list-view.js +248 -6
- package/dist/utils/number-display.d.ts +70 -17
- package/dist/utils/number-display.js +34 -12
- package/dist/utils/object-aggregate-query.d.ts +113 -0
- package/dist/utils/object-aggregate-query.js +50 -0
- package/dist/utils/parent-scope.d.ts +123 -0
- package/dist/utils/parent-scope.js +140 -0
- package/dist/utils/predicate-fields.d.ts +62 -4
- package/dist/utils/predicate-fields.js +169 -4
- package/dist/utils/record-color.d.ts +75 -0
- package/dist/utils/record-color.js +65 -0
- package/dist/utils/record-source.d.ts +298 -0
- package/dist/utils/record-source.js +331 -0
- package/dist/utils/record-title.d.ts +148 -8
- package/dist/utils/record-title.js +220 -26
- package/dist/utils/reference-keys.d.ts +21 -22
- package/dist/utils/reference-keys.js +532 -29
- 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 +128 -26
- package/dist/utils/sort-query.js +240 -13
- package/dist/utils/text-comparand.d.ts +92 -0
- package/dist/utils/text-comparand.js +99 -0
- package/dist/utils/unmaterialized-fields.d.ts +52 -0
- package/dist/utils/unmaterialized-fields.js +57 -0
- package/dist/utils/without-denied-fields.d.ts +49 -0
- package/dist/utils/without-denied-fields.js +52 -0
- package/dist/validation/index.d.ts +9 -4
- package/dist/validation/index.js +9 -4
- package/dist/validation/schema-validator.d.ts +1 -1
- package/dist/validation/schema-validator.js +305 -82
- 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.d.ts +1 -1
- package/dist/validation/validators/object-validation-engine.js +14 -5
- package/package.json +8 -9
- package/dist/__benchmarks__/core.bench.d.ts +0 -8
- package/dist/__benchmarks__/core.bench.js +0 -53
- package/dist/data-scope/DataScopeManager.d.ts +0 -127
- package/dist/data-scope/DataScopeManager.js +0 -229
- package/dist/protocols/ResponsiveProtocol.d.ts +0 -73
- package/dist/protocols/ResponsiveProtocol.js +0 -158
- package/dist/validation/validation-engine.d.ts +0 -88
- package/dist/validation/validation-engine.js +0 -434
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
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
|
+
import type { ChartSeriesBinding } from './chart-series.js';
|
|
9
|
+
/** Authored spec `ChartSeries` presentation, in the renderer's internal spelling. */
|
|
10
|
+
export interface AuthoredSeriesPresentation {
|
|
11
|
+
label?: string;
|
|
12
|
+
/** Spec `ChartSeries.type`, narrowed — see {@link seriesPresentation}. */
|
|
13
|
+
chartType?: 'bar' | 'line' | 'area';
|
|
14
|
+
yAxis?: 'left' | 'right';
|
|
15
|
+
color?: string;
|
|
16
|
+
stack?: string;
|
|
17
|
+
variant?: 'primary' | 'comparison';
|
|
18
|
+
dashArray?: string;
|
|
19
|
+
opacity?: number;
|
|
20
|
+
}
|
|
21
|
+
/** A derived series binding with the author's presentation merged onto it. */
|
|
22
|
+
export type MergedChartSeries = ChartSeriesBinding & AuthoredSeriesPresentation;
|
|
23
|
+
/**
|
|
24
|
+
* One authored `ChartSeries`, minus its `name` — i.e. everything about it that
|
|
25
|
+
* is presentation rather than membership.
|
|
26
|
+
*
|
|
27
|
+
* `type` is narrowed to the three families that COMPOSE on one cartesian plot.
|
|
28
|
+
* Without the narrowing a `type: 'pie'` would not merely be inert — it would
|
|
29
|
+
* count as a family disagreement in `effectiveChartFamily`, flip the whole
|
|
30
|
+
* chart into a combo, and then draw that series as a bar anyway.
|
|
31
|
+
*
|
|
32
|
+
* `ChartRenderer` now narrows the same way for every series it receives,
|
|
33
|
+
* `dataKey`-shaped or not (`01c27c431` fixed the fast path that used to
|
|
34
|
+
* forward a `dataKey`-shaped array straight through, so `normalizeChartSchema`'s
|
|
35
|
+
* own identical narrowing never saw it) — so this module is no longer that
|
|
36
|
+
* narrowing's only line of defense. It stays anyway:
|
|
37
|
+
* `AuthoredSeriesPresentation.chartType` is a typed, narrower field
|
|
38
|
+
* (`'bar' | 'line' | 'area'`) this module's own callers read directly, and the
|
|
39
|
+
* double narrowing downstream is idempotent.
|
|
40
|
+
*/
|
|
41
|
+
export declare function seriesPresentation(raw: Record<string, unknown>): AuthoredSeriesPresentation;
|
|
42
|
+
/**
|
|
43
|
+
* Merge an authored `series[]` array's PRESENTATION onto the bindings the
|
|
44
|
+
* dataset selection derived — the series half of #4229's split, and the only
|
|
45
|
+
* half this module lowers (see the file header).
|
|
46
|
+
*
|
|
47
|
+
* The match rule is **by name/key**: an authored `series[].name` is paired with
|
|
48
|
+
* the derived binding whose `dataKey` it equals, and the pairing decides
|
|
49
|
+
* nothing but presentation:
|
|
50
|
+
*
|
|
51
|
+
* - an authored entry naming a measure that is NOT in the dataset selection is
|
|
52
|
+
* **ignored** — membership belongs to the dataset, so an author cannot add,
|
|
53
|
+
* remove or re-point a series from the chart config;
|
|
54
|
+
* - a derived series with no authored entry keeps the family default, so every
|
|
55
|
+
* surface that never wrote `series` renders byte-for-byte as before;
|
|
56
|
+
* - where both exist the **explicit binding wins** (#2880 S2), which is the
|
|
57
|
+
* whole point: `type: 'line'` + `yAxis: 'right'` is how the spec says "this
|
|
58
|
+
* measure is a line on the secondary axis".
|
|
59
|
+
*
|
|
60
|
+
* Matching on `name` only is deliberate: `name` is the spec's authorable key
|
|
61
|
+
* for a series (`dataKey` is a declared ALIAS of it, resolved where the
|
|
62
|
+
* metadata is parsed), so reading a second spelling here would fossilize a
|
|
63
|
+
* dialect this renderer has no business accepting (AGENTS.md #0.1).
|
|
64
|
+
*
|
|
65
|
+
* The FIRST entry naming a measure wins; a later duplicate cannot silently
|
|
66
|
+
* reconfigure a series the author already described.
|
|
67
|
+
*
|
|
68
|
+
* @param derived the bindings {@link buildChartSeries} produced from the selection
|
|
69
|
+
* @param authoredSeries the authored `series` array (anything, incl. absent)
|
|
70
|
+
*/
|
|
71
|
+
export declare function mergeAuthoredSeries(derived: ChartSeriesBinding[], authoredSeries: unknown): MergedChartSeries[];
|
|
72
|
+
/**
|
|
73
|
+
* Lower an authored chart config's CHROME (spec
|
|
74
|
+
* `DashboardWidgetChartConfigSchema` / `ReportChartSchema`, both extensions of
|
|
75
|
+
* `ChartConfigSchema` that keep its chrome keys unchanged) onto the chart
|
|
76
|
+
* schema a dataset-bound surface hands the renderer.
|
|
77
|
+
*
|
|
78
|
+
* ## Why this is a whitelist and not a spread
|
|
79
|
+
*
|
|
80
|
+
* Until #3135 NONE of a dashboard widget's `chartConfig` reached the renderer:
|
|
81
|
+
* the widget read `options` and nothing else, so an author who wrote
|
|
82
|
+
* `showLegend: false` still got a legend and one who wrote `true` only got one
|
|
83
|
+
* because "on" is the renderer's default. #3135 lowered that single flag and
|
|
84
|
+
* left the rest declared and inert. objectstack#7016 lowered the rest of the
|
|
85
|
+
* keys that are actually DELIVERED, admitting a key only when both of these
|
|
86
|
+
* hold:
|
|
87
|
+
*
|
|
88
|
+
* 1. **The chart block draws it end to end.** `{ type: 'chart' }` resolves to
|
|
89
|
+
* `ChartRenderer` → `AdvancedChartImpl`, which draws `title`/`subtitle` in
|
|
90
|
+
* its ChartFrame, turns `description` into the chart container's
|
|
91
|
+
* `role="img"` + `aria-label`, applies `height` as that container's inline
|
|
92
|
+
* height, reads `colors` as the positional palette, prints
|
|
93
|
+
* `showDataLabels` as a Recharts `LabelList`, draws `annotations` as
|
|
94
|
+
* ReferenceLine/ReferenceArea and honours `interaction` as the tooltip
|
|
95
|
+
* toggle plus `Brush`. Forwarding a key the renderer ignores would only
|
|
96
|
+
* move declared-but-not-delivered one layer down, which is the failure this
|
|
97
|
+
* exists to remove.
|
|
98
|
+
* 2. **It does not fight the dataset derivation.** `type` stays out: the
|
|
99
|
+
* calling surface's own type already picks the chart family, which is the
|
|
100
|
+
* dataset path's chart-family channel. The spec agrees on the dashboard
|
|
101
|
+
* carrier: since 17.5.0 `DashboardWidgetChartConfigSchema` refuses
|
|
102
|
+
* `chartConfig.type` at parse, with `xAxis` / `yAxis` / `series`, none of
|
|
103
|
+
* which this whitelist ever lowered (objectui#11315).
|
|
104
|
+
*
|
|
105
|
+
* `aria` is not lowered either. No renderer reads it: `AdvancedChartImpl` has
|
|
106
|
+
* no `aria` prop, and `SchemaRenderer`'s ARIA injection reads the FLAT
|
|
107
|
+
* `ariaLabel`/`ariaDescribedBy`/`role`, never a nested `aria` object (criterion
|
|
108
|
+
* 1). And it is no longer declared: spec 17.5.0 retired `ChartConfig.aria` as
|
|
109
|
+
* a `retiredKey` tombstone (objectstack#17751, objectui#4044), so an authored
|
|
110
|
+
* one is refused at parse. The accessible name a chart applies is
|
|
111
|
+
* `description`, above.
|
|
112
|
+
*
|
|
113
|
+
* `title` IS emitted here. A caller that paints the title itself (the report
|
|
114
|
+
* renderer's own `h3` above the chart) must drop it from the result, or the
|
|
115
|
+
* chart draws a second one.
|
|
116
|
+
*
|
|
117
|
+
* ## `title` / `subtitle` / `description` are `I18nLabel`, and travel as such
|
|
118
|
+
*
|
|
119
|
+
* `ChartConfigSchema` types all three as the spec's `I18nLabel` union — a plain
|
|
120
|
+
* string OR an inline locale map — so all three are lowered by
|
|
121
|
+
* {@link forwardedI18nLabel}, which hands the authored value on untouched for
|
|
122
|
+
* the renderer to resolve against the viewer's language. Read that helper for
|
|
123
|
+
* why resolving cannot happen in this package.
|
|
124
|
+
*
|
|
125
|
+
* ⚠️ LEDGERED, by name, as still unresolved after objectui#9038 — not in
|
|
126
|
+
* that card's scope and not reached by the change above:
|
|
127
|
+
*
|
|
128
|
+
* - **A series `label`** still goes through {@link labelText}'s
|
|
129
|
+
* first-string-wins pick, the design objectui#4020 ledgered and a caller
|
|
130
|
+
* can override. Not reopened here.
|
|
131
|
+
*
|
|
132
|
+
* ✓ CLEARED, objectui#9150 — **the report renderer's own heading.**
|
|
133
|
+
* `DatasetReportChart` (`@object-ui/plugin-report`) still drops `title` from
|
|
134
|
+
* this result and still paints its own `h3`, but that read site no longer
|
|
135
|
+
* narrows `chart.title` to a plain string: it resolves the union through
|
|
136
|
+
* `pickLocalized` against the viewer's language, like every other `I18nLabel`
|
|
137
|
+
* on that surface. Kept here rather than deleted because the ledger entry is
|
|
138
|
+
* what the fixing card was dispatched from, and because the FIRST half of it —
|
|
139
|
+
* that a caller painting the title itself must drop `title` from this result —
|
|
140
|
+
* is a live constraint on this whitelist, stated above.
|
|
141
|
+
*
|
|
142
|
+
* @param raw the authored chart config (anything, incl. absent)
|
|
143
|
+
* @param fieldCategoryColors per-category colours resolved from the category
|
|
144
|
+
* dimension's own select/lookup option colours, merged UNDER an explicit
|
|
145
|
+
* author map (see the `colors` note below)
|
|
146
|
+
* @returns only the keys that resolved, so the caller can spread it over the
|
|
147
|
+
* derived chart schema and every undeclared key keeps the renderer's default
|
|
148
|
+
*/
|
|
149
|
+
export declare function chartConfigPresentation(raw: unknown, fieldCategoryColors?: Record<string, string> | null): Record<string, unknown>;
|
|
150
|
+
/**
|
|
151
|
+
* Whether a chart of this family IGNORES `compareTo` — the one declaration of
|
|
152
|
+
* that rule (objectui#7495).
|
|
153
|
+
*
|
|
154
|
+
* Every surface that turns a `compareTo` into a comparison reads this; none
|
|
155
|
+
* keeps a list of its own. The inline chart path (`@object-ui/plugin-charts`'
|
|
156
|
+
* ObjectChart) skips the comparison fetch and synthesises no overlay series for
|
|
157
|
+
* these families, and the dataset widget path (`@object-ui/plugin-dashboard`'s
|
|
158
|
+
* DatasetWidget) forwards no `compareTo` to the executor for a chart of one of
|
|
159
|
+
* them, so the comparison pass never runs and no overlay series is appended.
|
|
160
|
+
*
|
|
161
|
+
* - `pie` / `donut` / `funnel` — single-distribution charts: a comparison
|
|
162
|
+
* overlay has no meaning on them, and the renderer's pie and funnel arms
|
|
163
|
+
* draw only `series[0]`.
|
|
164
|
+
* - `scatter` (objectui#7402) — a scatter binds ONE measure: the renderer reads
|
|
165
|
+
* y through the single `YAxis dataKey={series[0].dataKey}`, so an overlay was
|
|
166
|
+
* painted on the PRIMARY's y and "previous period" landed exactly on top of
|
|
167
|
+
* "current". Drawing it honestly needs the multi-measure projection declined
|
|
168
|
+
* as option A of objectui#7194; `compareTo` on a scatter returns WITH that
|
|
169
|
+
* projection.
|
|
170
|
+
*
|
|
171
|
+
* It reads the RENDERER's chart family (`bar`, `pie`, `scatter`, …), not a
|
|
172
|
+
* dashboard widget type: a caller whose widget types alias a family (the
|
|
173
|
+
* dashboard maps `bubble` to `scatter` and `pyramid` to `funnel`) maps first and
|
|
174
|
+
* asks about the family. An absent or unrecognised family does NOT ignore
|
|
175
|
+
* `compareTo` — the comparison runs.
|
|
176
|
+
*
|
|
177
|
+
* Why it lives here: the dashboard reaches `@object-ui/plugin-charts` only as a
|
|
178
|
+
* devDependency, so this package — a runtime dependency of both — is the one
|
|
179
|
+
* place both paths can read. Two copies were how the lists drifted apart: the
|
|
180
|
+
* dashboard's said "scatter only" while the charts package said all four, so a
|
|
181
|
+
* compare-to pie widget ran a comparison query whose overlay the renderer then
|
|
182
|
+
* dropped (objectui#7495; objectui#4389 is the precedent that a second copy of
|
|
183
|
+
* chart-presentation logic is a defect).
|
|
184
|
+
*
|
|
185
|
+
* @param chartType the chart family, or `undefined` when none is declared
|
|
186
|
+
* @returns `true` for `pie`, `donut`, `funnel` and `scatter`; `false` otherwise
|
|
187
|
+
*/
|
|
188
|
+
export declare function chartTypeIgnoresCompareTo(chartType: string | undefined): boolean;
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
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
|
+
const isRecord = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
|
|
9
|
+
/**
|
|
10
|
+
* An i18n label is a plain string or a `{ en, zh-CN, … }` record; charts render
|
|
11
|
+
* a string.
|
|
12
|
+
*
|
|
13
|
+
* ⚠️ Its one caller is {@link seriesPresentation}'s `label`. An axis `title`
|
|
14
|
+
* read this too until `061f5e829` moved it off the pick, and the axis lowering
|
|
15
|
+
* itself is gone since objectui#11372 (file header).
|
|
16
|
+
*
|
|
17
|
+
* **First-string-wins, deliberately, and deliberately NOT locale-aware** — this
|
|
18
|
+
* package is React-free and holds no i18n provider, so it cannot know which
|
|
19
|
+
* limb a console wants. A caller that CAN resolve the language (the report
|
|
20
|
+
* renderer, via `pickLocalized`) resolves the label itself and overrides the
|
|
21
|
+
* merged one; see objectui#4020, whose whole point is that picking the first
|
|
22
|
+
* limb paints English on a zh console.
|
|
23
|
+
*/
|
|
24
|
+
function labelText(v) {
|
|
25
|
+
if (typeof v === 'string' && v)
|
|
26
|
+
return v;
|
|
27
|
+
if (isRecord(v)) {
|
|
28
|
+
const first = Object.values(v).find((x) => typeof x === 'string' && x);
|
|
29
|
+
return first;
|
|
30
|
+
}
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* An authored `I18nLabel` that travels to the renderer **unresolved** — the
|
|
35
|
+
* chart's own `title` / `subtitle` / `description` (objectui#9038).
|
|
36
|
+
*
|
|
37
|
+
* ## Why this does not resolve, and why that is not {@link labelText}'s answer
|
|
38
|
+
*
|
|
39
|
+
* These three keys are lowered onto a chart SCHEMA slot that already declares
|
|
40
|
+
* the union: `normalizeChartSchema` (`@object-ui/plugin-charts`) resolves
|
|
41
|
+
* `title`/`subtitle`/`description` through `pickLocalized` with the viewer's
|
|
42
|
+
* language, which `ChartRenderer` reads from `useObjectTranslation` and hands
|
|
43
|
+
* down (objectui#8943), and `ObjectChart` reads `schema.title` through the same
|
|
44
|
+
* resolver for its drill heading. So the value's one resolution point is
|
|
45
|
+
* already downstream of here, holding the one thing this package does not have
|
|
46
|
+
* — the viewer. Forwarding is what lets that resolver see the value at all.
|
|
47
|
+
*
|
|
48
|
+
* Resolving here is not merely unnecessary, it is unavailable: `pickLocalized`
|
|
49
|
+
* lives in `@object-ui/i18n`, which DEPENDS on this package. Declaring the
|
|
50
|
+
* reverse edge makes the build graph cyclic (measured: `turbo run build
|
|
51
|
+
* --filter=@object-ui/core` refuses with `Cyclic dependency detected`), and
|
|
52
|
+
* that package's entry point is a React provider plus hooks — the layering
|
|
53
|
+
* this file's header states, arrived at from the other side.
|
|
54
|
+
*
|
|
55
|
+
* ⛔ Do NOT "fix" this by reusing {@link labelText}. The two answer different
|
|
56
|
+
* questions and only one of them is a choice:
|
|
57
|
+
*
|
|
58
|
+
* - `labelText` is a locale-unaware PICK for a slot whose consumer takes a
|
|
59
|
+
* plain string. It is wrong for a zh console and is ledgered as such
|
|
60
|
+
* (objectui#4020) because a caller that CAN resolve the language overrides
|
|
61
|
+
* the merged label — the mitigation depends on the value still existing.
|
|
62
|
+
* - The guard this replaced (`typeof v === 'string' && v ? v : undefined`)
|
|
63
|
+
* offered no such route: it ERASED the map arm, the `if (title)` guard then
|
|
64
|
+
* skipped the assignment, and the chart drew **no heading at all**, in every
|
|
65
|
+
* language, with no diagnostic. Nothing downstream could override a key that
|
|
66
|
+
* never arrived.
|
|
67
|
+
*
|
|
68
|
+
* ## The admission test, and what it deliberately does not decide
|
|
69
|
+
*
|
|
70
|
+
* A plain string, or a record carrying at least one usable string entry. That
|
|
71
|
+
* is the same "is there anything here" question the `if (title)` truthiness
|
|
72
|
+
* guard always asked, extended to the map arm — it does NOT decide which limb
|
|
73
|
+
* wins, which stays `pickLocalized`'s alone. Everything else (a number, a
|
|
74
|
+
* boolean, an array, `''`, `{}`, a record with no string value) is refused
|
|
75
|
+
* exactly as before, so no key reaches the caller that cannot render, and the
|
|
76
|
+
* `@returns` contract below — only keys that resolved — still holds.
|
|
77
|
+
*
|
|
78
|
+
* The map is forwarded VERBATIM: `InlineLocaleMapSchema` is
|
|
79
|
+
* `z.record(<tag>, z.string())` and enforcing that belongs at the parse, not in
|
|
80
|
+
* a renderer-side coercion (AGENTS.md #0.1). A non-string entry falls through
|
|
81
|
+
* `pickLocalized`'s limbs exactly as an absent one does, which is why admitting
|
|
82
|
+
* the record on the strength of one usable entry cannot paint `[object
|
|
83
|
+
* Object]`.
|
|
84
|
+
*/
|
|
85
|
+
function forwardedI18nLabel(v) {
|
|
86
|
+
if (typeof v === 'string')
|
|
87
|
+
return v || undefined;
|
|
88
|
+
if (isRecord(v) && Object.values(v).some((x) => typeof x === 'string' && x)) {
|
|
89
|
+
return v;
|
|
90
|
+
}
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* One authored `ChartSeries`, minus its `name` — i.e. everything about it that
|
|
95
|
+
* is presentation rather than membership.
|
|
96
|
+
*
|
|
97
|
+
* `type` is narrowed to the three families that COMPOSE on one cartesian plot.
|
|
98
|
+
* Without the narrowing a `type: 'pie'` would not merely be inert — it would
|
|
99
|
+
* count as a family disagreement in `effectiveChartFamily`, flip the whole
|
|
100
|
+
* chart into a combo, and then draw that series as a bar anyway.
|
|
101
|
+
*
|
|
102
|
+
* `ChartRenderer` now narrows the same way for every series it receives,
|
|
103
|
+
* `dataKey`-shaped or not (`01c27c431` fixed the fast path that used to
|
|
104
|
+
* forward a `dataKey`-shaped array straight through, so `normalizeChartSchema`'s
|
|
105
|
+
* own identical narrowing never saw it) — so this module is no longer that
|
|
106
|
+
* narrowing's only line of defense. It stays anyway:
|
|
107
|
+
* `AuthoredSeriesPresentation.chartType` is a typed, narrower field
|
|
108
|
+
* (`'bar' | 'line' | 'area'`) this module's own callers read directly, and the
|
|
109
|
+
* double narrowing downstream is idempotent.
|
|
110
|
+
*/
|
|
111
|
+
export function seriesPresentation(raw) {
|
|
112
|
+
const out = {};
|
|
113
|
+
const family = raw.type;
|
|
114
|
+
if (family === 'bar' || family === 'line' || family === 'area')
|
|
115
|
+
out.chartType = family;
|
|
116
|
+
if (raw.yAxis === 'left' || raw.yAxis === 'right')
|
|
117
|
+
out.yAxis = raw.yAxis;
|
|
118
|
+
const label = labelText(raw.label);
|
|
119
|
+
if (label)
|
|
120
|
+
out.label = label;
|
|
121
|
+
if (typeof raw.color === 'string' && raw.color)
|
|
122
|
+
out.color = raw.color;
|
|
123
|
+
if (typeof raw.stack === 'string' && raw.stack)
|
|
124
|
+
out.stack = raw.stack;
|
|
125
|
+
if (raw.variant === 'primary' || raw.variant === 'comparison')
|
|
126
|
+
out.variant = raw.variant;
|
|
127
|
+
if (typeof raw.dashArray === 'string' && raw.dashArray)
|
|
128
|
+
out.dashArray = raw.dashArray;
|
|
129
|
+
if (typeof raw.opacity === 'number' && Number.isFinite(raw.opacity))
|
|
130
|
+
out.opacity = raw.opacity;
|
|
131
|
+
return out;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Merge an authored `series[]` array's PRESENTATION onto the bindings the
|
|
135
|
+
* dataset selection derived — the series half of #4229's split, and the only
|
|
136
|
+
* half this module lowers (see the file header).
|
|
137
|
+
*
|
|
138
|
+
* The match rule is **by name/key**: an authored `series[].name` is paired with
|
|
139
|
+
* the derived binding whose `dataKey` it equals, and the pairing decides
|
|
140
|
+
* nothing but presentation:
|
|
141
|
+
*
|
|
142
|
+
* - an authored entry naming a measure that is NOT in the dataset selection is
|
|
143
|
+
* **ignored** — membership belongs to the dataset, so an author cannot add,
|
|
144
|
+
* remove or re-point a series from the chart config;
|
|
145
|
+
* - a derived series with no authored entry keeps the family default, so every
|
|
146
|
+
* surface that never wrote `series` renders byte-for-byte as before;
|
|
147
|
+
* - where both exist the **explicit binding wins** (#2880 S2), which is the
|
|
148
|
+
* whole point: `type: 'line'` + `yAxis: 'right'` is how the spec says "this
|
|
149
|
+
* measure is a line on the secondary axis".
|
|
150
|
+
*
|
|
151
|
+
* Matching on `name` only is deliberate: `name` is the spec's authorable key
|
|
152
|
+
* for a series (`dataKey` is a declared ALIAS of it, resolved where the
|
|
153
|
+
* metadata is parsed), so reading a second spelling here would fossilize a
|
|
154
|
+
* dialect this renderer has no business accepting (AGENTS.md #0.1).
|
|
155
|
+
*
|
|
156
|
+
* The FIRST entry naming a measure wins; a later duplicate cannot silently
|
|
157
|
+
* reconfigure a series the author already described.
|
|
158
|
+
*
|
|
159
|
+
* @param derived the bindings {@link buildChartSeries} produced from the selection
|
|
160
|
+
* @param authoredSeries the authored `series` array (anything, incl. absent)
|
|
161
|
+
*/
|
|
162
|
+
export function mergeAuthoredSeries(derived, authoredSeries) {
|
|
163
|
+
const authored = new Map();
|
|
164
|
+
for (const entry of Array.isArray(authoredSeries) ? authoredSeries : []) {
|
|
165
|
+
if (!isRecord(entry))
|
|
166
|
+
continue;
|
|
167
|
+
const name = typeof entry.name === 'string' ? entry.name : undefined;
|
|
168
|
+
if (name && !authored.has(name))
|
|
169
|
+
authored.set(name, entry);
|
|
170
|
+
}
|
|
171
|
+
return derived.map((s) => {
|
|
172
|
+
const entry = authored.get(s.dataKey);
|
|
173
|
+
return entry ? { ...s, ...seriesPresentation(entry) } : s;
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Lower an authored chart config's CHROME (spec
|
|
178
|
+
* `DashboardWidgetChartConfigSchema` / `ReportChartSchema`, both extensions of
|
|
179
|
+
* `ChartConfigSchema` that keep its chrome keys unchanged) onto the chart
|
|
180
|
+
* schema a dataset-bound surface hands the renderer.
|
|
181
|
+
*
|
|
182
|
+
* ## Why this is a whitelist and not a spread
|
|
183
|
+
*
|
|
184
|
+
* Until #3135 NONE of a dashboard widget's `chartConfig` reached the renderer:
|
|
185
|
+
* the widget read `options` and nothing else, so an author who wrote
|
|
186
|
+
* `showLegend: false` still got a legend and one who wrote `true` only got one
|
|
187
|
+
* because "on" is the renderer's default. #3135 lowered that single flag and
|
|
188
|
+
* left the rest declared and inert. objectstack#7016 lowered the rest of the
|
|
189
|
+
* keys that are actually DELIVERED, admitting a key only when both of these
|
|
190
|
+
* hold:
|
|
191
|
+
*
|
|
192
|
+
* 1. **The chart block draws it end to end.** `{ type: 'chart' }` resolves to
|
|
193
|
+
* `ChartRenderer` → `AdvancedChartImpl`, which draws `title`/`subtitle` in
|
|
194
|
+
* its ChartFrame, turns `description` into the chart container's
|
|
195
|
+
* `role="img"` + `aria-label`, applies `height` as that container's inline
|
|
196
|
+
* height, reads `colors` as the positional palette, prints
|
|
197
|
+
* `showDataLabels` as a Recharts `LabelList`, draws `annotations` as
|
|
198
|
+
* ReferenceLine/ReferenceArea and honours `interaction` as the tooltip
|
|
199
|
+
* toggle plus `Brush`. Forwarding a key the renderer ignores would only
|
|
200
|
+
* move declared-but-not-delivered one layer down, which is the failure this
|
|
201
|
+
* exists to remove.
|
|
202
|
+
* 2. **It does not fight the dataset derivation.** `type` stays out: the
|
|
203
|
+
* calling surface's own type already picks the chart family, which is the
|
|
204
|
+
* dataset path's chart-family channel. The spec agrees on the dashboard
|
|
205
|
+
* carrier: since 17.5.0 `DashboardWidgetChartConfigSchema` refuses
|
|
206
|
+
* `chartConfig.type` at parse, with `xAxis` / `yAxis` / `series`, none of
|
|
207
|
+
* which this whitelist ever lowered (objectui#11315).
|
|
208
|
+
*
|
|
209
|
+
* `aria` is not lowered either. No renderer reads it: `AdvancedChartImpl` has
|
|
210
|
+
* no `aria` prop, and `SchemaRenderer`'s ARIA injection reads the FLAT
|
|
211
|
+
* `ariaLabel`/`ariaDescribedBy`/`role`, never a nested `aria` object (criterion
|
|
212
|
+
* 1). And it is no longer declared: spec 17.5.0 retired `ChartConfig.aria` as
|
|
213
|
+
* a `retiredKey` tombstone (objectstack#17751, objectui#4044), so an authored
|
|
214
|
+
* one is refused at parse. The accessible name a chart applies is
|
|
215
|
+
* `description`, above.
|
|
216
|
+
*
|
|
217
|
+
* `title` IS emitted here. A caller that paints the title itself (the report
|
|
218
|
+
* renderer's own `h3` above the chart) must drop it from the result, or the
|
|
219
|
+
* chart draws a second one.
|
|
220
|
+
*
|
|
221
|
+
* ## `title` / `subtitle` / `description` are `I18nLabel`, and travel as such
|
|
222
|
+
*
|
|
223
|
+
* `ChartConfigSchema` types all three as the spec's `I18nLabel` union — a plain
|
|
224
|
+
* string OR an inline locale map — so all three are lowered by
|
|
225
|
+
* {@link forwardedI18nLabel}, which hands the authored value on untouched for
|
|
226
|
+
* the renderer to resolve against the viewer's language. Read that helper for
|
|
227
|
+
* why resolving cannot happen in this package.
|
|
228
|
+
*
|
|
229
|
+
* ⚠️ LEDGERED, by name, as still unresolved after objectui#9038 — not in
|
|
230
|
+
* that card's scope and not reached by the change above:
|
|
231
|
+
*
|
|
232
|
+
* - **A series `label`** still goes through {@link labelText}'s
|
|
233
|
+
* first-string-wins pick, the design objectui#4020 ledgered and a caller
|
|
234
|
+
* can override. Not reopened here.
|
|
235
|
+
*
|
|
236
|
+
* ✓ CLEARED, objectui#9150 — **the report renderer's own heading.**
|
|
237
|
+
* `DatasetReportChart` (`@object-ui/plugin-report`) still drops `title` from
|
|
238
|
+
* this result and still paints its own `h3`, but that read site no longer
|
|
239
|
+
* narrows `chart.title` to a plain string: it resolves the union through
|
|
240
|
+
* `pickLocalized` against the viewer's language, like every other `I18nLabel`
|
|
241
|
+
* on that surface. Kept here rather than deleted because the ledger entry is
|
|
242
|
+
* what the fixing card was dispatched from, and because the FIRST half of it —
|
|
243
|
+
* that a caller painting the title itself must drop `title` from this result —
|
|
244
|
+
* is a live constraint on this whitelist, stated above.
|
|
245
|
+
*
|
|
246
|
+
* @param raw the authored chart config (anything, incl. absent)
|
|
247
|
+
* @param fieldCategoryColors per-category colours resolved from the category
|
|
248
|
+
* dimension's own select/lookup option colours, merged UNDER an explicit
|
|
249
|
+
* author map (see the `colors` note below)
|
|
250
|
+
* @returns only the keys that resolved, so the caller can spread it over the
|
|
251
|
+
* derived chart schema and every undeclared key keeps the renderer's default
|
|
252
|
+
*/
|
|
253
|
+
export function chartConfigPresentation(raw, fieldCategoryColors) {
|
|
254
|
+
const config = isRecord(raw) ? raw : {};
|
|
255
|
+
const out = {};
|
|
256
|
+
if (typeof config.showLegend === 'boolean')
|
|
257
|
+
out.showLegend = config.showLegend;
|
|
258
|
+
if (typeof config.showDataLabels === 'boolean')
|
|
259
|
+
out.showDataLabels = config.showDataLabels;
|
|
260
|
+
// The three `I18nLabel` slots, carried through unresolved — see
|
|
261
|
+
// {@link forwardedI18nLabel} for why this package forwards rather than picks,
|
|
262
|
+
// and for what is still ledgered as unresolved on purpose.
|
|
263
|
+
const title = forwardedI18nLabel(config.title);
|
|
264
|
+
if (title)
|
|
265
|
+
out.title = title;
|
|
266
|
+
const subtitle = forwardedI18nLabel(config.subtitle);
|
|
267
|
+
if (subtitle)
|
|
268
|
+
out.subtitle = subtitle;
|
|
269
|
+
const description = forwardedI18nLabel(config.description);
|
|
270
|
+
if (description)
|
|
271
|
+
out.description = description;
|
|
272
|
+
// A non-positive height would collapse the plot; the container default is the
|
|
273
|
+
// more honest answer than an invisible chart.
|
|
274
|
+
if (typeof config.height === 'number' && Number.isFinite(config.height) && config.height > 0) {
|
|
275
|
+
out.height = config.height;
|
|
276
|
+
}
|
|
277
|
+
if (Array.isArray(config.annotations) && config.annotations.length > 0)
|
|
278
|
+
out.annotations = config.annotations;
|
|
279
|
+
if (config.interaction && typeof config.interaction === 'object' && !Array.isArray(config.interaction)) {
|
|
280
|
+
out.interaction = config.interaction;
|
|
281
|
+
}
|
|
282
|
+
// `colors` is overloaded kanban-style — and the two arms reach the renderer
|
|
283
|
+
// through two DIFFERENT props, so the split has to happen here (the react
|
|
284
|
+
// tier's ObjectChart splits it the same way): a `string[]` is the positional
|
|
285
|
+
// palette (`colors`), a `{ value: color }` record is an explicit per-category
|
|
286
|
+
// map (`categoryColors`). The author's map is merged OVER the dimension
|
|
287
|
+
// field's own option colours, which is the precedence the spec field comment
|
|
288
|
+
// states ("a value→color map — and a select/lookup dimension's option colors
|
|
289
|
+
// — take precedence over the positional palette per category").
|
|
290
|
+
const palette = Array.isArray(config.colors)
|
|
291
|
+
? config.colors.filter((c) => typeof c === 'string' && !!c)
|
|
292
|
+
: undefined;
|
|
293
|
+
if (palette?.length)
|
|
294
|
+
out.colors = palette;
|
|
295
|
+
const authorCategoryColors = config.colors && typeof config.colors === 'object' && !Array.isArray(config.colors)
|
|
296
|
+
? config.colors
|
|
297
|
+
: undefined;
|
|
298
|
+
if (fieldCategoryColors || authorCategoryColors) {
|
|
299
|
+
out.categoryColors = { ...(fieldCategoryColors ?? {}), ...(authorCategoryColors ?? {}) };
|
|
300
|
+
}
|
|
301
|
+
return out;
|
|
302
|
+
}
|
|
303
|
+
/** The families {@link chartTypeIgnoresCompareTo} answers `true` for. */
|
|
304
|
+
const CHART_TYPES_IGNORING_COMPARE_TO = new Set(['pie', 'donut', 'funnel', 'scatter']);
|
|
305
|
+
/**
|
|
306
|
+
* Whether a chart of this family IGNORES `compareTo` — the one declaration of
|
|
307
|
+
* that rule (objectui#7495).
|
|
308
|
+
*
|
|
309
|
+
* Every surface that turns a `compareTo` into a comparison reads this; none
|
|
310
|
+
* keeps a list of its own. The inline chart path (`@object-ui/plugin-charts`'
|
|
311
|
+
* ObjectChart) skips the comparison fetch and synthesises no overlay series for
|
|
312
|
+
* these families, and the dataset widget path (`@object-ui/plugin-dashboard`'s
|
|
313
|
+
* DatasetWidget) forwards no `compareTo` to the executor for a chart of one of
|
|
314
|
+
* them, so the comparison pass never runs and no overlay series is appended.
|
|
315
|
+
*
|
|
316
|
+
* - `pie` / `donut` / `funnel` — single-distribution charts: a comparison
|
|
317
|
+
* overlay has no meaning on them, and the renderer's pie and funnel arms
|
|
318
|
+
* draw only `series[0]`.
|
|
319
|
+
* - `scatter` (objectui#7402) — a scatter binds ONE measure: the renderer reads
|
|
320
|
+
* y through the single `YAxis dataKey={series[0].dataKey}`, so an overlay was
|
|
321
|
+
* painted on the PRIMARY's y and "previous period" landed exactly on top of
|
|
322
|
+
* "current". Drawing it honestly needs the multi-measure projection declined
|
|
323
|
+
* as option A of objectui#7194; `compareTo` on a scatter returns WITH that
|
|
324
|
+
* projection.
|
|
325
|
+
*
|
|
326
|
+
* It reads the RENDERER's chart family (`bar`, `pie`, `scatter`, …), not a
|
|
327
|
+
* dashboard widget type: a caller whose widget types alias a family (the
|
|
328
|
+
* dashboard maps `bubble` to `scatter` and `pyramid` to `funnel`) maps first and
|
|
329
|
+
* asks about the family. An absent or unrecognised family does NOT ignore
|
|
330
|
+
* `compareTo` — the comparison runs.
|
|
331
|
+
*
|
|
332
|
+
* Why it lives here: the dashboard reaches `@object-ui/plugin-charts` only as a
|
|
333
|
+
* devDependency, so this package — a runtime dependency of both — is the one
|
|
334
|
+
* place both paths can read. Two copies were how the lists drifted apart: the
|
|
335
|
+
* dashboard's said "scatter only" while the charts package said all four, so a
|
|
336
|
+
* compare-to pie widget ran a comparison query whose overlay the renderer then
|
|
337
|
+
* dropped (objectui#7495; objectui#4389 is the precedent that a second copy of
|
|
338
|
+
* chart-presentation logic is a defect).
|
|
339
|
+
*
|
|
340
|
+
* @param chartType the chart family, or `undefined` when none is declared
|
|
341
|
+
* @returns `true` for `pie`, `donut`, `funnel` and `scatter`; `false` otherwise
|
|
342
|
+
*/
|
|
343
|
+
export function chartTypeIgnoresCompareTo(chartType) {
|
|
344
|
+
return typeof chartType === 'string' && CHART_TYPES_IGNORING_COMPARE_TO.has(chartType);
|
|
345
|
+
}
|