@object-ui/core 17.6.0 → 18.0.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.
Files changed (147) hide show
  1. package/CHANGELOG.md +7260 -0
  2. package/README.md +228 -14
  3. package/dist/actions/ActionEngine.d.ts +4 -2
  4. package/dist/actions/ActionEngine.js +13 -8
  5. package/dist/actions/ActionRunner.d.ts +473 -69
  6. package/dist/actions/ActionRunner.js +792 -125
  7. package/dist/actions/TransactionManager.d.ts +7 -2
  8. package/dist/actions/UndoManager.d.ts +8 -13
  9. package/dist/actions/actionKeys.d.ts +9 -7
  10. package/dist/actions/actionKeys.js +67 -14
  11. package/dist/actions/actionResponse.js +2 -2
  12. package/dist/actions/index.d.ts +2 -0
  13. package/dist/actions/index.js +2 -0
  14. package/dist/actions/navigationTarget.d.ts +41 -0
  15. package/dist/actions/navigationTarget.js +20 -0
  16. package/dist/actions/recordDelete.d.ts +154 -0
  17. package/dist/actions/recordDelete.js +110 -0
  18. package/dist/adapters/ApiDataSource.d.ts +8 -4
  19. package/dist/adapters/ApiDataSource.js +19 -7
  20. package/dist/adapters/ValueDataSource.d.ts +6 -1
  21. package/dist/adapters/ValueDataSource.js +1090 -98
  22. package/dist/adapters/batchTransaction.d.ts +1 -1
  23. package/dist/adapters/batchTransaction.js +1 -1
  24. package/dist/adapters/resolveDataSource.d.ts +9 -6
  25. package/dist/adapters/resolveDataSource.js +9 -12
  26. package/dist/builder/schema-builder.d.ts +38 -81
  27. package/dist/builder/schema-builder.js +33 -152
  28. package/dist/data-scope/ViewDataProvider.d.ts +8 -25
  29. package/dist/data-scope/ViewDataProvider.js +27 -54
  30. package/dist/data-scope/element-data-source.d.ts +261 -7
  31. package/dist/data-scope/element-data-source.js +342 -52
  32. package/dist/data-scope/index.d.ts +9 -4
  33. package/dist/data-scope/index.js +9 -4
  34. package/dist/evaluator/ExpressionEvaluator.d.ts +86 -0
  35. package/dist/evaluator/ExpressionEvaluator.js +184 -12
  36. package/dist/evaluator/FormulaFunctions.d.ts +0 -10
  37. package/dist/evaluator/FormulaFunctions.js +166 -27
  38. package/dist/evaluator/deadlineSettled.d.ts +40 -0
  39. package/dist/evaluator/deadlineSettled.js +45 -0
  40. package/dist/evaluator/declaredPredicate.d.ts +93 -9
  41. package/dist/evaluator/declaredPredicate.js +117 -16
  42. package/dist/evaluator/fieldRules.d.ts +88 -4
  43. package/dist/evaluator/fieldRules.js +208 -31
  44. package/dist/evaluator/index.d.ts +2 -0
  45. package/dist/evaluator/index.js +2 -0
  46. package/dist/evaluator/listConditional.d.ts +103 -34
  47. package/dist/evaluator/listConditional.js +127 -50
  48. package/dist/evaluator/optionRules.d.ts +77 -6
  49. package/dist/evaluator/optionRules.js +87 -10
  50. package/dist/evaluator/predicateInput.d.ts +15 -4
  51. package/dist/evaluator/predicateInput.js +35 -2
  52. package/dist/evaluator/rowPredicateCanon.d.ts +7 -18
  53. package/dist/evaluator/rowPredicateCanon.js +65 -115
  54. package/dist/evaluator/subjectPermissions.d.ts +105 -0
  55. package/dist/evaluator/subjectPermissions.js +86 -0
  56. package/dist/evaluator/unevaluablePredicate.d.ts +57 -0
  57. package/dist/evaluator/unevaluablePredicate.js +70 -0
  58. package/dist/index.d.ts +15 -0
  59. package/dist/index.js +83 -2
  60. package/dist/protocols/index.d.ts +0 -1
  61. package/dist/protocols/index.js +9 -1
  62. package/dist/registry/Registry.d.ts +382 -34
  63. package/dist/registry/Registry.js +247 -12
  64. package/dist/registry/WidgetRegistry.js +7 -3
  65. package/dist/registry/html-tier-intrinsics.d.ts +90 -0
  66. package/dist/registry/html-tier-intrinsics.js +110 -0
  67. package/dist/registry/public-blocks.d.ts +8 -0
  68. package/dist/registry/public-blocks.js +57 -0
  69. package/dist/theme/ThemeEngine.js +1 -1
  70. package/dist/utils/affordanceGrants.d.ts +371 -0
  71. package/dist/utils/affordanceGrants.js +252 -0
  72. package/dist/utils/chart-category-key.d.ts +82 -0
  73. package/dist/utils/chart-category-key.js +84 -0
  74. package/dist/utils/chart-measure-key.d.ts +64 -0
  75. package/dist/utils/chart-measure-key.js +65 -0
  76. package/dist/utils/chart-presentation.d.ts +95 -112
  77. package/dist/utils/chart-presentation.js +169 -104
  78. package/dist/utils/chart-series.d.ts +165 -5
  79. package/dist/utils/chart-series.js +123 -18
  80. package/dist/utils/column-sortability.d.ts +157 -0
  81. package/dist/utils/column-sortability.js +217 -0
  82. package/dist/utils/dashboard-filters.d.ts +38 -6
  83. package/dist/utils/dashboard-filters.js +107 -61
  84. package/dist/utils/dataset-format.d.ts +211 -30
  85. package/dist/utils/dataset-format.js +389 -46
  86. package/dist/utils/date-display.d.ts +346 -0
  87. package/dist/utils/date-display.js +644 -0
  88. package/dist/utils/date-macros.js +7 -0
  89. package/dist/utils/drill-down.d.ts +78 -0
  90. package/dist/utils/drill-down.js +93 -0
  91. package/dist/utils/emptiness.d.ts +96 -0
  92. package/dist/utils/emptiness.js +101 -0
  93. package/dist/utils/expand-fields.d.ts +81 -9
  94. package/dist/utils/expand-fields.js +81 -9
  95. package/dist/utils/extract-records.d.ts +65 -5
  96. package/dist/utils/extract-records.js +65 -8
  97. package/dist/utils/filter-converter.d.ts +158 -6
  98. package/dist/utils/filter-converter.js +1098 -24
  99. package/dist/utils/filter-tokens.d.ts +49 -5
  100. package/dist/utils/filter-tokens.js +105 -33
  101. package/dist/utils/grouping-fields.d.ts +61 -0
  102. package/dist/utils/grouping-fields.js +84 -0
  103. package/dist/utils/managedBy.d.ts +3 -3
  104. package/dist/utils/managedBy.js +5 -5
  105. package/dist/utils/native-date-value.d.ts +48 -0
  106. package/dist/utils/native-date-value.js +167 -0
  107. package/dist/utils/non-grid-row-ceiling.d.ts +143 -0
  108. package/dist/utils/non-grid-row-ceiling.js +141 -0
  109. package/dist/utils/normalize-list-view.d.ts +135 -11
  110. package/dist/utils/normalize-list-view.js +243 -64
  111. package/dist/utils/number-display.d.ts +63 -14
  112. package/dist/utils/number-display.js +34 -12
  113. package/dist/utils/object-aggregate-query.d.ts +113 -0
  114. package/dist/utils/object-aggregate-query.js +50 -0
  115. package/dist/utils/parent-scope.d.ts +123 -0
  116. package/dist/utils/parent-scope.js +140 -0
  117. package/dist/utils/predicate-fields.d.ts +45 -12
  118. package/dist/utils/predicate-fields.js +141 -12
  119. package/dist/utils/record-color.d.ts +75 -0
  120. package/dist/utils/record-color.js +65 -0
  121. package/dist/utils/record-source.d.ts +298 -0
  122. package/dist/utils/record-source.js +331 -0
  123. package/dist/utils/record-title.d.ts +148 -8
  124. package/dist/utils/record-title.js +220 -26
  125. package/dist/utils/reference-keys.d.ts +21 -22
  126. package/dist/utils/reference-keys.js +532 -29
  127. package/dist/utils/sort-query.d.ts +115 -15
  128. package/dist/utils/sort-query.js +240 -13
  129. package/dist/utils/text-comparand.d.ts +92 -0
  130. package/dist/utils/text-comparand.js +99 -0
  131. package/dist/utils/without-denied-fields.d.ts +49 -0
  132. package/dist/utils/without-denied-fields.js +52 -0
  133. package/dist/validation/index.d.ts +8 -4
  134. package/dist/validation/index.js +8 -4
  135. package/dist/validation/schema-validator.d.ts +1 -1
  136. package/dist/validation/schema-validator.js +225 -83
  137. package/dist/validation/server-owned-value.d.ts +5 -1
  138. package/dist/validation/server-owned-value.js +5 -1
  139. package/dist/validation/validators/object-validation-engine.d.ts +1 -1
  140. package/dist/validation/validators/object-validation-engine.js +14 -3
  141. package/package.json +8 -9
  142. package/dist/data-scope/DataScopeManager.d.ts +0 -127
  143. package/dist/data-scope/DataScopeManager.js +0 -229
  144. package/dist/protocols/ResponsiveProtocol.d.ts +0 -73
  145. package/dist/protocols/ResponsiveProtocol.js +0 -158
  146. package/dist/validation/validation-engine.d.ts +0 -88
  147. package/dist/validation/validation-engine.js +0 -434
@@ -5,58 +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-presentation — the AUTHORED half of a dataset-bound chart, lowered onto
10
- * the bindings {@link buildChartSeries} derived from the dataset selection.
11
- *
12
- * `chart-series.ts` beside this file owns the DATA half: which columns become
13
- * series, which rows, which buckets. This file owns everything the author gets
14
- * to say about how that data LOOKS — the ruled data/presentation split of
15
- * objectui#4229, stated once:
16
- *
17
- * - **Data (derived, never forwarded)** — series MEMBERSHIP and the column each
18
- * binding reads. Concretely `buildChartSeries`' `dataKey`s, `xAxisKey`, and
19
- * the spec's two binding keys `ChartSeries.name` and `ChartAxis.field`.
20
- * - **Presentation (authored, merged forward)** — everything else on those same
21
- * objects: `series[].type` (the per-series mark), `series[].yAxis` (which
22
- * axis it binds to), `label`/`color`/`stack`/`variant`/`dashArray`/`opacity`,
23
- * the axis definitions' `title`/`format`/`min`/`max`/`stepSize`/
24
- * `showGridLines`/`position`/`logarithmic`, and the chart chrome
25
- * (`showLegend`, `showDataLabels`, `colors`, `annotations`, …).
26
- *
27
- * ## Why it lives in `@object-ui/core` rather than in one plugin
28
- *
29
- * It was written for `plugin-dashboard`'s `DatasetWidget` (#3135 → objectstack#7016
30
- * → #4229) and lifted here by objectui#4877, when `plugin-report`'s embedded
31
- * report chart turned out to need the SAME merge over the same spec shapes: the
32
- * report was forwarding six keys and dropping every authored chrome key the
33
- * schema declares, and re-deriving this beside the dashboard's copy is exactly
34
- * the duplication objectui#4389 filed as a defect (two longhand copies of the
35
- * analytics label net, one per plugin, drifting apart).
36
- *
37
- * Everything here is a pure data transform over plain records, so it sits below
38
- * both plugins with no React and no i18n — the same layering `chart-series.ts`
39
- * already has (see {@link OptionLabelTranslator} there for why an i18n-resolved
40
- * string always arrives as an ARGUMENT rather than being read here).
41
- *
42
- * ## Two entry points, because the two surfaces declare axes differently
43
- *
44
- * A dashboard widget's `chartConfig` spells its axes as spec `ChartAxis`
45
- * OBJECTS. A report's `chart.xAxis` / `chart.yAxis` are bare dimension/measure
46
- * NAME strings — on that surface the axes are pure DATA (they ARE the selection)
47
- * and carry no presentation at all. So:
48
- *
49
- * - {@link mergeAuthoredPresentation} is the object-axis entry point: series
50
- * merge + axis presentation, for a caller whose axes are `ChartAxis` objects.
51
- * - {@link mergeAuthoredSeries} is the series merge ALONE, for a caller whose
52
- * axes are names. That split is structural, not a guard: handing a bare
53
- * string to {@link mergeAuthoredPresentation} would run it through
54
- * `axisPresentation`, which reads nothing off a string and would synthesise a
55
- * `yAxis: [{}]` entry — a y-axis declaring nothing but its own existence,
56
- * which is precisely how the COUNT of entries turns on a secondary axis. A
57
- * surface that cannot author axis presentation should not be able to reach
58
- * the code that reads it.
59
- */
60
8
  import type { ChartSeriesBinding } from './chart-series.js';
61
9
  /** Authored spec `ChartSeries` presentation, in the renderer's internal spelling. */
62
10
  export interface AuthoredSeriesPresentation {
@@ -76,39 +24,25 @@ export type MergedChartSeries = ChartSeriesBinding & AuthoredSeriesPresentation;
76
24
  * One authored `ChartSeries`, minus its `name` — i.e. everything about it that
77
25
  * is presentation rather than membership.
78
26
  *
79
- * `type` is narrowed to the three families that COMPOSE on one cartesian plot,
80
- * because this array reaches the renderer already speaking the internal shape
81
- * (`ChartRenderer` forwards a `dataKey`-shaped array untouched, so
82
- * `normalizeChartSchema`'s own identical narrowing never sees it). Without the
83
- * narrowing a `type: 'pie'` would not merely be inert — it would count as a
84
- * family disagreement in `effectiveChartFamily`, flip the whole chart into a
85
- * combo, and then draw that series as a bar anyway.
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.
86
40
  */
87
41
  export declare function seriesPresentation(raw: Record<string, unknown>): AuthoredSeriesPresentation;
88
- /**
89
- * One authored `ChartAxis`, minus its `field` — the axis's presentation.
90
- *
91
- * `field` is the one DATA key on an axis (it names the plotted column), and
92
- * dropping it here is what keeps membership with the dataset **structurally**
93
- * rather than by a guard: `normalizeChartSchema` synthesises series out of
94
- * `yAxis[].field` when a chart declares no series, so a forwarded `field`
95
- * would be a live membership channel on an empty selection. With it gone the
96
- * axis carries scale and chrome only, and the count of entries — which is what
97
- * turns on the secondary axis (`yAxes.length > 1`) — survives, including for
98
- * an entry that declares nothing but its own existence.
99
- *
100
- * Keys the renderer does not read on a given axis are dropped by
101
- * `normalizeChartSchema`, the ONE normalization layer (#2880 S1): today it
102
- * keeps `format`/`title`/`showGridLines` on the x-axis and the full set on the
103
- * y-axes. That narrowing is deliberately NOT mirrored here — a second copy
104
- * would drift from the renderer's real capability the moment it grew.
105
- */
106
- export declare function axisPresentation(raw: unknown): Record<string, unknown>;
107
42
  /**
108
43
  * Merge an authored `series[]` array's PRESENTATION onto the bindings the
109
- * dataset selection derived — the series half of #4229's split, and the whole
110
- * of it for a surface whose axes are names rather than objects (see the file
111
- * header).
44
+ * dataset selection derived — the series half of #4229's split, and the only
45
+ * half this module lowers (see the file header).
112
46
  *
113
47
  * The match rule is **by name/key**: an authored `series[].name` is paired with
114
48
  * the derived binding whose `dataKey` it equals, and the pairing decides
@@ -136,28 +70,10 @@ export declare function axisPresentation(raw: unknown): Record<string, unknown>;
136
70
  */
137
71
  export declare function mergeAuthoredSeries(derived: ChartSeriesBinding[], authoredSeries: unknown): MergedChartSeries[];
138
72
  /**
139
- * Merge an authored chart config's PRESENTATION onto the series and axes the
140
- * dataset selection derived — the one place that happens for a surface whose
141
- * axes are spec `ChartAxis` OBJECTS (#4229).
142
- *
143
- * The series half is {@link mergeAuthoredSeries}; see it for the match rule and
144
- * for why membership is safe. The axes half reads `xAxis` / `yAxis` through
145
- * {@link axisPresentation}, which is why a surface spelling those as bare NAME
146
- * strings must call `mergeAuthoredSeries` directly instead (file header).
147
- *
148
- * @param derived the bindings {@link buildChartSeries} produced from the selection
149
- * @param raw the authored chart config (anything, incl. absent)
150
- * @returns the merged series, plus the presentation-only axes to spread onto
151
- * the chart schema (absent when the author declared none)
152
- */
153
- export declare function mergeAuthoredPresentation(derived: ChartSeriesBinding[], raw: unknown): {
154
- series: MergedChartSeries[];
155
- axes: Record<string, unknown>;
156
- };
157
- /**
158
- * Lower an authored chart config's CHROME (spec `ChartConfigSchema` /
159
- * `ReportChartSchema` — the same keys on both) onto the chart schema a
160
- * dataset-bound surface hands the renderer.
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.
161
77
  *
162
78
  * ## Why this is a whitelist and not a spread
163
79
  *
@@ -181,20 +97,48 @@ export declare function mergeAuthoredPresentation(derived: ChartSeriesBinding[],
181
97
  * exists to remove.
182
98
  * 2. **It does not fight the dataset derivation.** `type` stays out: the
183
99
  * calling surface's own type already picks the chart family, which is the
184
- * dataset path's chart-family channel.
185
- *
186
- * `aria` is the one declared key with **no reader at all**: `AdvancedChartImpl`
187
- * has no `aria` prop, and `SchemaRenderer`'s ARIA injection reads the FLAT
188
- * `ariaLabel`/`ariaDescribedBy`/`role`, never a nested `aria` object. It is
189
- * therefore left unforwarded on purpose (criterion 1) rather than papered over
190
- * with a caller-side flattening that would also collide with the accessible
191
- * name `description` already sets — reported back to objectstack#5175's
192
- * narrowing half instead.
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.
193
112
  *
194
113
  * `title` IS emitted here. A caller that paints the title itself (the report
195
114
  * renderer's own `h3` above the chart) must drop it from the result, or the
196
115
  * chart draws a second one.
197
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
+ *
198
142
  * @param raw the authored chart config (anything, incl. absent)
199
143
  * @param fieldCategoryColors per-category colours resolved from the category
200
144
  * dimension's own select/lookup option colours, merged UNDER an explicit
@@ -203,3 +147,42 @@ export declare function mergeAuthoredPresentation(derived: ChartSeriesBinding[],
203
147
  * derived chart schema and every undeclared key keeps the renderer's default
204
148
  */
205
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;
@@ -8,8 +8,11 @@
8
8
  const isRecord = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
9
9
  /**
10
10
  * An i18n label is a plain string or a `{ en, zh-CN, … }` record; charts render
11
- * a string. Same pick `normalizeChartSchema` makes, so a label reads the same
12
- * on both paths.
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).
13
16
  *
14
17
  * **First-string-wins, deliberately, and deliberately NOT locale-aware** — this
15
18
  * package is React-free and holds no i18n provider, so it cannot know which
@@ -27,17 +30,83 @@ function labelText(v) {
27
30
  }
28
31
  return undefined;
29
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
+ }
30
93
  /**
31
94
  * One authored `ChartSeries`, minus its `name` — i.e. everything about it that
32
95
  * is presentation rather than membership.
33
96
  *
34
- * `type` is narrowed to the three families that COMPOSE on one cartesian plot,
35
- * because this array reaches the renderer already speaking the internal shape
36
- * (`ChartRenderer` forwards a `dataKey`-shaped array untouched, so
37
- * `normalizeChartSchema`'s own identical narrowing never sees it). Without the
38
- * narrowing a `type: 'pie'` would not merely be inert — it would count as a
39
- * family disagreement in `effectiveChartFamily`, flip the whole chart into a
40
- * combo, and then draw that series as a bar anyway.
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.
41
110
  */
42
111
  export function seriesPresentation(raw) {
43
112
  const out = {};
@@ -61,54 +130,10 @@ export function seriesPresentation(raw) {
61
130
  out.opacity = raw.opacity;
62
131
  return out;
63
132
  }
64
- /**
65
- * One authored `ChartAxis`, minus its `field` — the axis's presentation.
66
- *
67
- * `field` is the one DATA key on an axis (it names the plotted column), and
68
- * dropping it here is what keeps membership with the dataset **structurally**
69
- * rather than by a guard: `normalizeChartSchema` synthesises series out of
70
- * `yAxis[].field` when a chart declares no series, so a forwarded `field`
71
- * would be a live membership channel on an empty selection. With it gone the
72
- * axis carries scale and chrome only, and the count of entries — which is what
73
- * turns on the secondary axis (`yAxes.length > 1`) — survives, including for
74
- * an entry that declares nothing but its own existence.
75
- *
76
- * Keys the renderer does not read on a given axis are dropped by
77
- * `normalizeChartSchema`, the ONE normalization layer (#2880 S1): today it
78
- * keeps `format`/`title`/`showGridLines` on the x-axis and the full set on the
79
- * y-axes. That narrowing is deliberately NOT mirrored here — a second copy
80
- * would drift from the renderer's real capability the moment it grew.
81
- */
82
- export function axisPresentation(raw) {
83
- const out = {};
84
- if (!isRecord(raw))
85
- return out;
86
- const title = labelText(raw.title);
87
- if (title)
88
- out.title = title;
89
- if (typeof raw.format === 'string' && raw.format)
90
- out.format = raw.format;
91
- if (typeof raw.min === 'number' && Number.isFinite(raw.min))
92
- out.min = raw.min;
93
- if (typeof raw.max === 'number' && Number.isFinite(raw.max))
94
- out.max = raw.max;
95
- if (typeof raw.stepSize === 'number' && Number.isFinite(raw.stepSize) && raw.stepSize > 0) {
96
- out.stepSize = raw.stepSize;
97
- }
98
- if (typeof raw.showGridLines === 'boolean')
99
- out.showGridLines = raw.showGridLines;
100
- if (raw.position === 'left' || raw.position === 'right' || raw.position === 'top' || raw.position === 'bottom') {
101
- out.position = raw.position;
102
- }
103
- if (typeof raw.logarithmic === 'boolean')
104
- out.logarithmic = raw.logarithmic;
105
- return out;
106
- }
107
133
  /**
108
134
  * Merge an authored `series[]` array's PRESENTATION onto the bindings the
109
- * dataset selection derived — the series half of #4229's split, and the whole
110
- * of it for a surface whose axes are names rather than objects (see the file
111
- * header).
135
+ * dataset selection derived — the series half of #4229's split, and the only
136
+ * half this module lowers (see the file header).
112
137
  *
113
138
  * The match rule is **by name/key**: an authored `series[].name` is paired with
114
139
  * the derived binding whose `dataKey` it equals, and the pairing decides
@@ -149,43 +174,10 @@ export function mergeAuthoredSeries(derived, authoredSeries) {
149
174
  });
150
175
  }
151
176
  /**
152
- * Merge an authored chart config's PRESENTATION onto the series and axes the
153
- * dataset selection derived — the one place that happens for a surface whose
154
- * axes are spec `ChartAxis` OBJECTS (#4229).
155
- *
156
- * The series half is {@link mergeAuthoredSeries}; see it for the match rule and
157
- * for why membership is safe. The axes half reads `xAxis` / `yAxis` through
158
- * {@link axisPresentation}, which is why a surface spelling those as bare NAME
159
- * strings must call `mergeAuthoredSeries` directly instead (file header).
160
- *
161
- * @param derived the bindings {@link buildChartSeries} produced from the selection
162
- * @param raw the authored chart config (anything, incl. absent)
163
- * @returns the merged series, plus the presentation-only axes to spread onto
164
- * the chart schema (absent when the author declared none)
165
- */
166
- export function mergeAuthoredPresentation(derived, raw) {
167
- const config = isRecord(raw) ? raw : {};
168
- const series = mergeAuthoredSeries(derived, config.series);
169
- const axes = {};
170
- const xAxis = axisPresentation(config.xAxis);
171
- if (Object.keys(xAxis).length > 0)
172
- axes.xAxis = xAxis;
173
- // The COUNT of y-axis entries is itself presentation — it is what declares a
174
- // secondary axis — so every declared entry keeps its slot even when it
175
- // carries nothing but `field` (which is data and does not travel).
176
- const yAxisRaw = Array.isArray(config.yAxis)
177
- ? config.yAxis
178
- : config.yAxis !== undefined
179
- ? [config.yAxis]
180
- : [];
181
- if (yAxisRaw.length > 0)
182
- axes.yAxis = yAxisRaw.map(axisPresentation);
183
- return { series, axes };
184
- }
185
- /**
186
- * Lower an authored chart config's CHROME (spec `ChartConfigSchema` /
187
- * `ReportChartSchema` — the same keys on both) onto the chart schema a
188
- * dataset-bound surface hands the renderer.
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.
189
181
  *
190
182
  * ## Why this is a whitelist and not a spread
191
183
  *
@@ -209,20 +201,48 @@ export function mergeAuthoredPresentation(derived, raw) {
209
201
  * exists to remove.
210
202
  * 2. **It does not fight the dataset derivation.** `type` stays out: the
211
203
  * calling surface's own type already picks the chart family, which is the
212
- * dataset path's chart-family channel.
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).
213
208
  *
214
- * `aria` is the one declared key with **no reader at all**: `AdvancedChartImpl`
215
- * has no `aria` prop, and `SchemaRenderer`'s ARIA injection reads the FLAT
216
- * `ariaLabel`/`ariaDescribedBy`/`role`, never a nested `aria` object. It is
217
- * therefore left unforwarded on purpose (criterion 1) rather than papered over
218
- * with a caller-side flattening that would also collide with the accessible
219
- * name `description` already sets — reported back to objectstack#5175's
220
- * narrowing half instead.
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.
221
216
  *
222
217
  * `title` IS emitted here. A caller that paints the title itself (the report
223
218
  * renderer's own `h3` above the chart) must drop it from the result, or the
224
219
  * chart draws a second one.
225
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
+ *
226
246
  * @param raw the authored chart config (anything, incl. absent)
227
247
  * @param fieldCategoryColors per-category colours resolved from the category
228
248
  * dimension's own select/lookup option colours, merged UNDER an explicit
@@ -233,18 +253,20 @@ export function mergeAuthoredPresentation(derived, raw) {
233
253
  export function chartConfigPresentation(raw, fieldCategoryColors) {
234
254
  const config = isRecord(raw) ? raw : {};
235
255
  const out = {};
236
- const text = (v) => (typeof v === 'string' && v ? v : undefined);
237
256
  if (typeof config.showLegend === 'boolean')
238
257
  out.showLegend = config.showLegend;
239
258
  if (typeof config.showDataLabels === 'boolean')
240
259
  out.showDataLabels = config.showDataLabels;
241
- const title = text(config.title);
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);
242
264
  if (title)
243
265
  out.title = title;
244
- const subtitle = text(config.subtitle);
266
+ const subtitle = forwardedI18nLabel(config.subtitle);
245
267
  if (subtitle)
246
268
  out.subtitle = subtitle;
247
- const description = text(config.description);
269
+ const description = forwardedI18nLabel(config.description);
248
270
  if (description)
249
271
  out.description = description;
250
272
  // A non-positive height would collapse the plot; the container default is the
@@ -278,3 +300,46 @@ export function chartConfigPresentation(raw, fieldCategoryColors) {
278
300
  }
279
301
  return out;
280
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
+ }