@object-ui/plugin-charts 16.1.0 → 17.2.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 CHANGED
@@ -1,5 +1,476 @@
1
1
  # @object-ui/plugin-charts
2
2
 
3
+ ## 17.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 4a51e77: Stop declaring 14 symbols across ten packages under names `@objectstack/spec`
8
+ owns (objectui#3161, objectstack#4115 batch 7 — the long tail, one or two
9
+ entries per package). All ten packages leave the ledger, which drops from 17
10
+ collisions across 11 packages to 3 across 1.
11
+
12
+ **Renamed exports** — in every case the spec exports the same name for a
13
+ _different_ thing, so the old name was a mis-description rather than a dialect:
14
+
15
+ | package | was | now | what the spec's same-named export is |
16
+ | :------------------------- | :--------------------------------- | :--------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------- |
17
+ | `@object-ui/fields` | `FieldWidgetProps` | `FieldWidgetComponentProps` | the DECLARED field-widget plugin props contract (a zod object; `field.type` is the `FieldType` enum, `readonly`/`required` carry defaults) |
18
+ | `@object-ui/layout` | `PageHeaderProps` | `PageHeaderComponentProps` | the authored `page:header` node — a zod schema of `title`, `subtitle`, an icon NAME, `breadcrumb`, `actions: string[]` |
19
+ | `@object-ui/layout` | `Page` | `PageNodeRenderer` | the authored page metadata DOCUMENT (`name`, `label`, `type`, `regions`) |
20
+ | `@object-ui/plugin-detail` | `ObjectFieldLike` | `ObjectDefFieldLike` | the i18n duck type `translateObject` walks (`help`/`description`, plus `[key: string]: any`) |
21
+ | `@object-ui/plugin-grid` | `ColumnSummaryConfig` | `ColumnSummarySetting` | the OBJECT form of `ListColumn.summary` **only** — the local one was the whole union, shorthand included |
22
+ | `@object-ui/plugin-grid` | `isMultiValueField` | `hasMultiValueShape` | the spec's classifier, which requires a def with a `type`; the local one is called with `undefined` |
23
+ | `@object-ui/collaboration` | `RealtimeConfig` | `RealtimeSubscriptionConfig` | the app's realtime DECLARATION (`enabled`, `transport`, `subscriptions[]`) |
24
+ | `@object-ui/plugin-charts` | `ChartConfig` | `ChartContainerConfig` | the authored chart document (`type`, `xAxis`, `series`, `showLegend`, …) |
25
+ | `@object-ui/plugin-form` | `FormSection` / `FormSectionProps` | `FormSectionContainer` / `FormSectionContainerProps` | the authored form-section metadata (`name`, `pane`, `visibleWhen`, `fields`) |
26
+ | `@object-ui/providers` | `Theme` | `ThemePreference` | a whole theme DOCUMENT (`name`, `label`, `colors`, `typography`) |
27
+ | `@object-ui/runner` | `App` (default export) | `RunnerApp` | the authored application metadata type **and** the `App.create()` builder |
28
+ | `@object-ui/sdui-parser` | `ValidationResult` | `ManifestValidationResult` | plugin-manifest validation (`{ valid, errors?, warnings? }`), exported from both `kernel` and `contracts` |
29
+
30
+ `ManifestValidationResult` follows the `<what was validated>Validation<Error|Result>`
31
+ convention registered on objectstack#4115 (`@object-ui/core` took
32
+ `SchemaNodeValidationResult` in batch 4). `PageHeaderComponentProps` deliberately
33
+ reuses the name `@object-ui/app-shell` already chose for its own header props in
34
+ batch 3, so one concept does not acquire two dialect names one package apart.
35
+
36
+ **Now derived from the spec instead of hand-written:**
37
+
38
+ - `@object-ui/fields` — `isFileIdToken` is re-exported from
39
+ `@objectstack/spec/data`. The local copy was character-for-character identical
40
+ to the spec's function while its comment said it "mirrors" it, so every
41
+ behaviour test passed and only reference identity could tell the two apart.
42
+ The regex is a wire decision: widening it server-side while a copy here kept
43
+ the old bound would make every new id read as "not a reference", and the
44
+ widget would submit the legacy inline blob to a backend expecting a reference.
45
+ - `@object-ui/plugin-detail` — `FeedFilterMode` is re-exported from
46
+ `@objectstack/spec/data`, in a file that already imported the sibling
47
+ `FeedItemType` from the spec.
48
+ - `@object-ui/plugin-grid` — the eleven-member aggregation union is now the
49
+ spec's `ColumnSummary` enum, so the total `Record<ColumnSummaryType, string>`
50
+ label map turns a member the spec adds into a compile error instead of a
51
+ blank footer cell. `ColumnSummarySetting` is `NonNullable<ListColumn['summary']>`,
52
+ i.e. whatever forms the spec itself accepts. `hasMultiValueShape` delegates to
53
+ the spec's `isMultiValueField` rather than re-deriving it from
54
+ `MULTI_OPTION_TYPES` / `MULTI_CAPABLE_TYPES`.
55
+ - `@object-ui/providers` — `ThemePreference` is the spec's `ThemeMode` union
56
+ plus the one legacy `'system'` spelling this provider still honours for stored
57
+ preferences, read off the schema's own `_zod` carrier so the package takes no
58
+ zod dependency.
59
+
60
+ `@objectstack/spec` moves from `devDependencies` to `dependencies` in
61
+ `@object-ui/fields` (it re-exports a runtime function) and `@object-ui/providers`
62
+ (its public `.d.ts` now references the spec).
63
+
64
+ Scored `minor`, not `major`, per this repo's fixed-group rule — objectui's major
65
+ tracks `@objectstack`, so breaking changes of our own ship as minor with the
66
+ semantics spelled out above (see AGENTS.md §版本号策略). A `major` here would carry
67
+ all 39 packages of the fixed group to `18.0.0` and off objectstack's 17.x line.
68
+
69
+ ### Patch Changes
70
+
71
+ - Updated dependencies [4ae0ac4]
72
+ - Updated dependencies [696e3c1]
73
+ - Updated dependencies [bca45cc]
74
+ - Updated dependencies [a889e31]
75
+ - Updated dependencies [09d30a4]
76
+ - Updated dependencies [4bf612c]
77
+ - Updated dependencies [335041c]
78
+ - Updated dependencies [b414983]
79
+ - Updated dependencies [256f8cc]
80
+ - Updated dependencies [d9668a7]
81
+ - Updated dependencies [4b470b9]
82
+ - Updated dependencies [cb82705]
83
+ - Updated dependencies [f572849]
84
+ - Updated dependencies [f6e8d78]
85
+ - Updated dependencies [ea96284]
86
+ - Updated dependencies [d3584c6]
87
+ - Updated dependencies [a8ad6c0]
88
+ - Updated dependencies [444457c]
89
+ - Updated dependencies [850033c]
90
+ - Updated dependencies [022e4c3]
91
+ - Updated dependencies [009e25d]
92
+ - Updated dependencies [726b89c]
93
+ - @object-ui/types@17.2.0
94
+ - @object-ui/components@17.2.0
95
+ - @object-ui/core@17.2.0
96
+ - @object-ui/react@17.2.0
97
+ - @object-ui/i18n@17.2.0
98
+
99
+ ## 17.1.0
100
+
101
+ ### Minor Changes
102
+
103
+ - f1c04b6: fix(charts): a spec `series[].type` override actually draws, and a spec-shape `series` plots at all (#2945)
104
+
105
+ #2945 listed `combo` (`plugin-charts`) as renderer-local dialect to "promote or
106
+ delete". Neither: the spec **already models a combo chart**, per-series, and its
107
+ own field comment says so —
108
+
109
+ ```ts
110
+ // spec/src/ui/chart.zod.ts — ChartSeriesSchema
111
+ /** Series type override (combo charts) */
112
+ type: ChartTypeSchema.optional().describe('Override chart type for this series'),
113
+ ```
114
+
115
+ — exactly as it models stacking with `ChartSeries.stack` rather than a
116
+ `stacked-bar` family. So `combo` is not a name an author should reach for; it is
117
+ what "the series disagree about their family" looks like from the renderer's
118
+ side. `effectiveChartFamily` now derives it, and `combo` stays a documented
119
+ renderer-local marker (internal callers pass it directly today).
120
+
121
+ Chasing that turned up two live bugs, both silent, on the path a spec author
122
+ takes.
123
+
124
+ **1. The per-series override was parsed, carried, and then dropped.** Only the
125
+ renderer's `chartType === 'combo'` branch read `series[].chartType`, so
126
+
127
+ ```ts
128
+ { type: 'bar', series: [{ name: 'revenue' }, { name: 'margin', type: 'line' }] }
129
+ ```
130
+
131
+ drew `margin` as a bar. Nothing was wrong at any layer but the last — a unit test
132
+ even asserted the value was carried.
133
+
134
+ **2. A spec-shape `series` rendered nothing at all.** `series` is the one binding
135
+ both shapes spell with the same key, so `ChartRenderer`'s blanket "internal props
136
+ win" rule let the author's `[{ name }]` shadow the normalized `[{ dataKey }]` and
137
+ reach a renderer that reads `dataKey`. Blank chart. Every other spec binding has a
138
+ distinct name (`xAxis` vs `xAxisKey`), which is why only this one broke — and why
139
+ the isolated normalization tests all passed over a dead path. The raw array is now
140
+ preferred only when it already speaks the internal shape, so internal callers are
141
+ byte-for-byte unchanged and a mixed array works too.
142
+
143
+ **Also fixed in passing:** the combo branch had an `area` arm under a `BarChart`
144
+ container, and Recharts renders an `<Area>` child of `BarChart` as nothing — so an
145
+ authored combo with an `area` series drew a blank series. The container is now
146
+ `ComposedChart`, which is what Recharts provides for mixed marks.
147
+
148
+ Widening only. A chart whose series all resolve to one family keeps its own
149
+ family, an explicit `combo` is untouched, and a family with no per-series meaning
150
+ (`pie`, `horizontal-bar`, …) is never widened. A derived combo binds series to the
151
+ left axis unless one asks for `yAxis: 'right'` — the spec's own default — so
152
+ widening changes the series' mark and not its scale; the legacy bar→left/line→right
153
+ guess is kept for an authored `combo`, where it was historically the only way to
154
+ reach a second axis.
155
+
156
+ Guards: `effectiveChartFamily` / `comboBaseFamily` are unit-tested over the whole
157
+ family matrix; DOM-level tests assert the **marks** rather than the derived family,
158
+ since the carry was already covered and the drawing was what broke; and
159
+ `spec-derived-unions.test.ts` asserts `combo` is absent from the spec's
160
+ `ChartTypeSchema`, so the day it is adopted upstream the derivation is named as the
161
+ thing to retire.
162
+
163
+ ### Patch Changes
164
+
165
+ - Updated dependencies [62311b6]
166
+ - Updated dependencies [fc0272a]
167
+ - Updated dependencies [9e7349e]
168
+ - Updated dependencies [8864971]
169
+ - Updated dependencies [1cf0de7]
170
+ - Updated dependencies [752e18f]
171
+ - Updated dependencies [c785740]
172
+ - Updated dependencies [b41f401]
173
+ - Updated dependencies [19e9fa0]
174
+ - Updated dependencies [d61efd1]
175
+ - Updated dependencies [95b7214]
176
+ - Updated dependencies [7d9734d]
177
+ - Updated dependencies [6ae818e]
178
+ - Updated dependencies [9eb932b]
179
+ - Updated dependencies [746dd00]
180
+ - Updated dependencies [aebfa4f]
181
+ - Updated dependencies [38ca8be]
182
+ - Updated dependencies [3cb9646]
183
+ - Updated dependencies [68ef584]
184
+ - Updated dependencies [4952edf]
185
+ - Updated dependencies [7f0252e]
186
+ - Updated dependencies [c4d7b20]
187
+ - Updated dependencies [c769d3d]
188
+ - Updated dependencies [7639a61]
189
+ - Updated dependencies [94e63ef]
190
+ - Updated dependencies [c735bf7]
191
+ - Updated dependencies [02aef0c]
192
+ - Updated dependencies [6f29aa5]
193
+ - Updated dependencies [d21794c]
194
+ - Updated dependencies [c4db402]
195
+ - Updated dependencies [5319bf1]
196
+ - Updated dependencies [49e5671]
197
+ - Updated dependencies [9a04d25]
198
+ - Updated dependencies [b5b97e2]
199
+ - Updated dependencies [f59f2c1]
200
+ - Updated dependencies [07de839]
201
+ - Updated dependencies [2a40b5e]
202
+ - Updated dependencies [df613fa]
203
+ - Updated dependencies [4874117]
204
+ - Updated dependencies [ad0183a]
205
+ - Updated dependencies [ce08d55]
206
+ - Updated dependencies [eb4b740]
207
+ - Updated dependencies [5b084eb]
208
+ - Updated dependencies [aa1240a]
209
+ - Updated dependencies [2374a49]
210
+ - Updated dependencies [390c071]
211
+ - Updated dependencies [d10f526]
212
+ - Updated dependencies [2d5d594]
213
+ - Updated dependencies [ea7f477]
214
+ - Updated dependencies [379728f]
215
+ - Updated dependencies [7f23cd0]
216
+ - Updated dependencies [0ded602]
217
+ - Updated dependencies [24e0e0a]
218
+ - Updated dependencies [f8a95e5]
219
+ - Updated dependencies [3a6cf24]
220
+ - Updated dependencies [aa35561]
221
+ - Updated dependencies [03bd53b]
222
+ - Updated dependencies [3c1f321]
223
+ - Updated dependencies [a045a32]
224
+ - Updated dependencies [912496d]
225
+ - Updated dependencies [80edbd4]
226
+ - Updated dependencies [9867281]
227
+ - @object-ui/core@17.1.0
228
+ - @object-ui/components@17.1.0
229
+ - @object-ui/react@17.1.0
230
+ - @object-ui/types@17.1.0
231
+ - @object-ui/i18n@17.1.0
232
+
233
+ ## 17.0.0
234
+
235
+ ### Minor Changes
236
+
237
+ - aa88056: feat(charts): honor `ChartAxis.stepSize`, `ChartConfig.description` and `ChartConfig.height` (framework#3752)
238
+
239
+ The tail of the declared-≠-delivered sweep from framework#3729 / #2880. Three
240
+ `ChartConfig` props reached the renderer and did nothing:
241
+
242
+ - **`ChartAxis.stepSize`** — Recharts has no "a tick every N units" prop
243
+ (`tickCount` is a hint it may ignore, `interval` is for categorical axes), so
244
+ honoring a step means handing it the tick array outright. `ticksFor` builds it
245
+ from the axis's own `min`/`max` where declared and from the plotted values
246
+ otherwise, so a step works with or without a pinned domain. A data-derived max
247
+ rounds UP to the next step (otherwise the topmost bar sits above the last
248
+ gridline and the axis reads as truncated); an explicit `max` clamps instead,
249
+ since a tick outside a pinned domain would be drawn outside the plot. A step
250
+ that would produce more than 200 ticks is refused rather than rendered — that
251
+ is a wrong config, and drawing it would hang the page instead of surfacing the
252
+ mistake.
253
+ - **`ChartConfig.description`** — the accessibility description. A chart is a
254
+ picture to a screen reader; the container now carries `role="img"` +
255
+ `aria-label`. Without a description it stays an ordinary div, because
256
+ stamping `role="img"` on an _unlabelled_ graphic is worse than leaving one a
257
+ screen reader can skip.
258
+ - **`ChartConfig.height`** — was read only by the legacy `ChartBarRenderer`, not
259
+ by the advanced path that draws every real chart. Now applied to the chart
260
+ container as an inline style, which beats its default `h-[350px]` class.
261
+
262
+ `height` and `description` ride on the shared container props, so they apply to
263
+ all eight chart families rather than one branch.
264
+
265
+ - 9b53d72: feat(charts): ObjectChart honors the spec `ChartConfig` author shape (objectui#2880 / framework#3729)
266
+
267
+ `ChartConfigSchema` is the chart protocol, but the renderer only ever read a
268
+ Recharts-flavoured internal shape — `chartType`, `xAxisKey`, `series[].dataKey`.
269
+ Everything an author wrote in the SPEC shape reached the renderer and was
270
+ silently dropped, which is exactly what ADR-0078 forbids. framework#3725
271
+ documented the gap by trimming the published contract down to the props that
272
+ actually worked; this closes it the other way round.
273
+
274
+ **S1 — one normalization boundary.** `normalizeChartSchema` translates the
275
+ author shape into the internal pipeline contract in a single place, rather than
276
+ scattering `??` fallbacks through the render tree (framework PD #12: one
277
+ translation is a contract mapping, N fallbacks are a second dialect):
278
+
279
+ - `type` → `chartType`, `xAxis: { field }` → `xAxisKey`, `series: [{ name }]` →
280
+ `series: [{ dataKey }]`
281
+ - the report surface's bare-string `xAxis`/`yAxis` resolve too
282
+ - `yAxis: [{ field }]` alone plots, with no `series` declared
283
+ - **internal props win**, so `DashboardRenderer`, `ObjectView` and the dataset
284
+ path are byte-for-byte unaffected — there is no migration
285
+
286
+ **The `type` collision.** `ChartConfig.type` is the chart family, but on any
287
+ surface that flattens chart config into a props bag `type` is already the SDUI
288
+ envelope's component discriminator. Spreading props last let an author's
289
+ `type="bar"` replace `object-chart` so the block stopped resolving; stamping the
290
+ discriminator last ate the author's value instead. The react-page wrapper now
291
+ keeps both: the discriminator wins the `type` slot and the author's value is
292
+ preserved beside it as `specType`, which the normalizer reads back.
293
+
294
+ **S2 — axis presentation.** `ChartAxis.format` drives the tick formatter (via
295
+ `Intl.NumberFormat`, no new dependency), `min`/`max` pin the domain,
296
+ `logarithmic` swaps the scale, `title` labels the axis, and `showGridLines` is
297
+ honored. A second `yAxis` entry (or `position: 'right'`) turns on the secondary
298
+ axis that `series[].yAxis` binds to — in combo charts an explicit binding now
299
+ beats the family-derived bar→left/line→right guess. `showLegend` is honored,
300
+ and `title`/`subtitle` render above the plot instead of only titling the
301
+ drill-down drawer.
302
+
303
+ **S3 — `series[].stack`, `annotations`, `interaction`.** Stacking passes the
304
+ author's group name through as Recharts' `stackId`. Annotations render as
305
+ `ReferenceLine` (`type: 'line'`) / `ReferenceArea` (`type: 'region'`) with the
306
+ declared axis, colour, style and label. `interaction.tooltips: false` suppresses
307
+ the hover card and `interaction.brush: true` adds the range selector;
308
+ `showDataLabels` prints values on the marks. `interaction.zoom` has no Recharts
309
+ primitive behind it and is deliberately still unimplemented rather than faked.
310
+
311
+ ### Patch Changes
312
+
313
+ - 7b35e4b: fix(dashboard,charts): resolve `{current_user_id}` in widget filters (framework #3574)
314
+
315
+ A dashboard widget filtered on `{current_user_id}` rendered `0`. The token
316
+ reached SQL as a literal, matched no row, and nothing was logged on the client
317
+ or the server — a silent zero that reads as "you have no work" rather than
318
+ "this filter did not resolve". The same token in a list-view filter resolved
319
+ correctly, so a user-scoped list and a user-scoped widget over the same data
320
+ disagreed.
321
+
322
+ There was no shared resolver. Three ad-hoc implementations had grown up
323
+ independently — `ObjectView` for list views, `ObjectDataPage` for URL filter
324
+ triples, `NavigationRenderer` for hrefs — and each understood only the filter
325
+ shape its own surface used. `ObjectView`'s opened with
326
+ `if (!Array.isArray(filter)) return filter`, so it could not have been reused
327
+ by dashboard widgets even in principle: widget filters are MongoDB-style
328
+ objects. Widgets therefore got no resolution at all — `DatasetWidget` called
329
+ `resolveDateMacros` and nothing else, which is why `{today}` worked in a widget
330
+ and `{current_user_id}` silently did not.
331
+
332
+ - **`@object-ui/core`** — new `utils/filter-tokens.ts` with
333
+ `resolveContextTokens` and `resolveFilterPlaceholders`. The latter expands
334
+ _every_ placeholder vocabulary in one call and is what surfaces should use;
335
+ resolving only some of them is the whole defect. The walk handles arrays and
336
+ plain objects uniformly, so one resolver covers both platform filter shapes.
337
+ - **`@object-ui/react`** — new `FilterScopeProvider` / `useFilterScope`. The
338
+ renderer packages deliberately do not depend on `@object-ui/auth`, so the
339
+ shell supplies the session values. This is a separate context from
340
+ `PredicateScopeContext`, which is the expression evaluation scope and carries
341
+ no organization.
342
+ - **`@object-ui/plugin-dashboard` / `@object-ui/plugin-charts`** — all six
343
+ widgets that previously resolved date macros only now resolve both
344
+ vocabularies: `DatasetWidget`, `ObjectMetricWidget`, `ObjectDataTable`,
345
+ `ObjectPivotTable`, and `ObjectChart` (dataset-bound and inline paths). The
346
+ chart's `compareTo` comparison filter gets the session pass too — otherwise
347
+ the overlay series silently ignored the owner clause the primary series
348
+ honoured.
349
+ - **`@object-ui/app-shell`** — `ObjectView`'s local `substituteFilterTokens`
350
+ and `ObjectDataPage`'s inline `=== '{current_user_id}'` ternary now delegate
351
+ to the shared resolver, so both also gain `{current_org_id}` and date macros.
352
+ Two of the three ad-hoc implementations are gone rather than joined by a
353
+ fourth.
354
+
355
+ An unresolvable token is left intact rather than dropped: leaving it yields an
356
+ empty result, whereas dropping the clause would _widen_ the result set and show
357
+ a signed-out viewer everyone's data. It is no longer silent — the resolver
358
+ warns, naming the token, and suggests the intended spelling for known
359
+ near-misses (`{current_user}`, `{user_id}`, `{organization_id}`). Authoring-time
360
+ enforcement lands separately as `filter-token-unknown` in `@objectstack/lint`.
361
+
362
+ - e16ed2d: fix(dashboard,charts): send widget `dateGranularity`/`sortBy`/`limit` to the query, and give funnels a real stage order (framework#3588)
363
+
364
+ `DatasetWidget` never read `widget.options`. Four keys an author writes there
365
+ change the query the server compiles, so a widget declaring
366
+ `options: { dateGranularity: 'month' }` grouped by the raw timestamp and drew
367
+ one bar per record, and `sortBy`/`sortOrder` produced no ordering at all.
368
+
369
+ - `DatasetWidget` lowers `options.dateGranularity`, `options.sortBy` +
370
+ `options.sortOrder`, and `options.limit` into the `DatasetSelection` it posts.
371
+ A `sortBy` naming something the widget does not project is dropped rather than
372
+ sent, so a stale sort key left by an edit degrades to "unordered" instead of
373
+ failing the widget against the server's stricter validation. These keys also
374
+ join the refetch signature, so editing one in the designer refetches instead
375
+ of re-rendering the previous grid.
376
+ - Funnel stages follow a **declared order**. `AdvancedChartImpl` sorted funnel
377
+ segments by value descending, unconditionally — which overrode any server
378
+ ordering and rendered a sales pipeline as a tidy narrowing shape whatever the
379
+ stages' real sequence, hiding the very anomaly (a bulge at Proposal) the chart
380
+ exists to show. It now honours a `categoryOrder`, which `DatasetWidget`
381
+ derives from the dimension field's picklist option order — the pipeline order
382
+ an author already declared on the object — or from an explicit
383
+ `options.stageOrder`. With no declared order the value-descending default is
384
+ unchanged, and a category missing from the order is kept (after the declared
385
+ ones), never dropped.
386
+ - New `@object-ui/core` helpers `buildCategoryOrder` / `buildCategoryRank`,
387
+ keyed by both the stored value and the display label like the existing
388
+ `buildOptionColorMap`, so ordering works whether or not the server resolved
389
+ the dimension's labels.
390
+
391
+ Requires the framework-side fix in objectstack#3588 for the selection keys to
392
+ take effect server-side.
393
+
394
+ - 6e8fd3c: fix(charts): a fieldless `count` aggregate keyed its value column `undefined`, so the chart plotted nothing (framework#3701)
395
+
396
+ framework#3701 pinned down what an OBJECT-bound chart aggregate names its result
397
+ columns — the raw field names it was given (`groupBy` for the category, `field`
398
+ for the value; no `sum_`-style decoration, unlike a dataset measure), plus the
399
+ literal `count` when a `count` omits `field`, which is the alias the engine
400
+ projects `COUNT(*)` under. `os validate` now lints page sources against that
401
+ convention, so the paths that build these rows have to honour it exactly.
402
+
403
+ Three of the four did. The odd one out was `count` — the one function that may
404
+ legitimately omit `field` — because every row builder read `params.field`
405
+ directly:
406
+
407
+ - `aggregateRecords` / `ObjectDataSource.aggregateClientSide` emitted
408
+ `{ [groupBy]: key, [undefined]: value }`, i.e. a column literally named
409
+ `undefined` that no axis binding could ever name;
410
+ - the legacy analytics path was worse: it remapped the server's `count` measure
411
+ onto `params.field` and **deleted** the original key, so the value the server
412
+ did return was thrown away before the chart saw it.
413
+
414
+ All of them now resolve the column through one helper (`aggregateValueKey`) so a
415
+ fieldless count lands under `count`, matching the framework contract. The
416
+ comparison-overlay column is derived from the same key (`count__comparison`
417
+ instead of `undefined__comparison`), and `aggregate.field` is typed optional to
418
+ match the spec's `ChartAggregateSchema`. Charts that name a field are unchanged.
419
+
420
+ - Updated dependencies [7b21891]
421
+ - Updated dependencies [0b3be01]
422
+ - Updated dependencies [3c4d935]
423
+ - Updated dependencies [4b60d2d]
424
+ - Updated dependencies [952b978]
425
+ - Updated dependencies [de5e40c]
426
+ - Updated dependencies [1a03af6]
427
+ - Updated dependencies [3e886eb]
428
+ - Updated dependencies [cfc675e]
429
+ - Updated dependencies [20df08c]
430
+ - Updated dependencies [1767124]
431
+ - Updated dependencies [8ecf5a6]
432
+ - Updated dependencies [af705b9]
433
+ - Updated dependencies [0502a7c]
434
+ - Updated dependencies [7b35e4b]
435
+ - Updated dependencies [8fb1295]
436
+ - Updated dependencies [e16ed2d]
437
+ - Updated dependencies [c6fd752]
438
+ - Updated dependencies [f9bbddb]
439
+ - Updated dependencies [dfd3705]
440
+ - Updated dependencies [c77108c]
441
+ - Updated dependencies [2735de6]
442
+ - Updated dependencies [c19ac11]
443
+ - Updated dependencies [6dee2cb]
444
+ - Updated dependencies [e05f052]
445
+ - Updated dependencies [0502a7c]
446
+ - Updated dependencies [faad45e]
447
+ - Updated dependencies [09c6a17]
448
+ - Updated dependencies [c7cff19]
449
+ - Updated dependencies [ba73a02]
450
+ - Updated dependencies [cd09a7b]
451
+ - Updated dependencies [f1abf0e]
452
+ - Updated dependencies [f05b84e]
453
+ - Updated dependencies [9b4b952]
454
+ - Updated dependencies [2f947e4]
455
+ - Updated dependencies [7d46648]
456
+ - Updated dependencies [9b53d72]
457
+ - Updated dependencies [bb4aa25]
458
+ - Updated dependencies [75f1cdf]
459
+ - Updated dependencies [662bdf9]
460
+ - Updated dependencies [059a052]
461
+ - Updated dependencies [53642d4]
462
+ - Updated dependencies [8aae006]
463
+ - Updated dependencies [c6cfdf1]
464
+ - Updated dependencies [d147a13]
465
+ - Updated dependencies [c6aaed8]
466
+ - Updated dependencies [263f885]
467
+ - Updated dependencies [dc334da]
468
+ - @object-ui/components@17.0.0
469
+ - @object-ui/i18n@17.0.0
470
+ - @object-ui/react@17.0.0
471
+ - @object-ui/types@17.0.0
472
+ - @object-ui/core@17.0.0
473
+
3
474
  ## 16.1.0
4
475
 
5
476
  ### Patch Changes
@@ -429,7 +900,7 @@
429
900
  `ObjectView` emit the dataset shape. The legacy inline aggregate is kept as a
430
901
  deprecated fallback so pre-ADR-0021 metadata keeps rendering.
431
902
 
432
- Refs objectstack-ai/framework#1890
903
+ Refs objectstack-ai/objectstack#1890
433
904
 
434
905
  - ab168e4: Dashboard charts no longer render blank on first paint. Recharts'
435
906
  `ResponsiveContainer` was a child of a `flex … justify-center` box, so it
@@ -1,4 +1,5 @@
1
- import { ChartConfig } from './ChartContainerImpl';
1
+ import { ChartContainerConfig } from './ChartContainerImpl';
2
+ import { NormalizedAxis, NormalizedSeries } from './normalizeChartSchema';
2
3
  /**
3
4
  * ObjectUI
4
5
  * Copyright (c) 2024-present ObjectStack Inc.
@@ -8,18 +9,20 @@ import { ChartConfig } from './ChartContainerImpl';
8
9
  */
9
10
  import * as React from "react";
10
11
  export interface AdvancedChartImplProps {
12
+ /**
13
+ * Chart family. `combo` is renderer-local and rarely needs to be passed:
14
+ * series declaring different families derive it (`effectiveChartFamily`),
15
+ * which is how `@objectstack/spec` expresses a combo chart.
16
+ */
11
17
  chartType?: 'bar' | 'column' | 'horizontal-bar' | 'line' | 'area' | 'pie' | 'donut' | 'radar' | 'scatter' | 'funnel' | 'combo' | 'treemap' | 'sankey';
12
18
  data?: Array<Record<string, any>>;
13
- config?: ChartConfig;
19
+ config?: ChartContainerConfig;
14
20
  xAxisKey?: string;
15
- series?: Array<{
16
- dataKey: string;
17
- chartType?: 'bar' | 'line' | 'area';
18
- variant?: 'current' | 'comparison';
19
- opacity?: number;
20
- dashArray?: string;
21
- label?: string;
22
- }>;
21
+ /**
22
+ * Plotted series, in the renderer's internal shape. Authors write the spec
23
+ * `series: [{ name, stack, yAxis, … }]`; `normalizeChartSchema` translates.
24
+ */
25
+ series?: NormalizedSeries[];
23
26
  className?: string;
24
27
  /** Categorical/series colour palette. Overrides the theme's `--chart-1..n`
25
28
  * defaults so a page/dashboard can brand its charts (data-screens). */
@@ -33,6 +36,20 @@ export interface AdvancedChartImplProps {
33
36
  * categories absent here fall back to the positional palette, then the theme.
34
37
  */
35
38
  categoryColors?: Record<string, string>;
39
+ /**
40
+ * Declared category order for ordered-sequence charts (funnel / pyramid),
41
+ * keyed like {@link categoryColors} by the category VALUE or its display
42
+ * LABEL, listed in domain order.
43
+ *
44
+ * A funnel's shape asserts a sequence, so without this the renderer can only
45
+ * guess at one and sorts by value descending. That is right for a generic
46
+ * "biggest first" funnel and wrong for a sales pipeline, where the stages
47
+ * have a declared order (Qualification → Needs Analysis → Proposal →
48
+ * Negotiation) that a healthy pipeline happens to narrow along — and an
49
+ * unhealthy one does not. When supplied, this order wins; categories not
50
+ * listed keep their incoming relative order after the listed ones.
51
+ */
52
+ categoryOrder?: string[];
36
53
  /**
37
54
  * Optional drill-down click handler. Fires when a chart segment is clicked
38
55
  * with `{ category, series, value }`. Wired for bar/horizontal-bar/line/
@@ -43,6 +60,40 @@ export interface AdvancedChartImplProps {
43
60
  series?: string;
44
61
  value?: number;
45
62
  }) => void;
63
+ /**
64
+ * Spec `ChartAxis` presentation for the category axis — `format` (tick
65
+ * formatter), `title`, `showGridLines`. Its `field` already arrived as
66
+ * {@link AdvancedChartImplProps.xAxisKey}. Resolved by
67
+ * `normalizeChartSchema`, so the renderer never parses the author shape.
68
+ */
69
+ xAxis?: NormalizedAxis;
70
+ /**
71
+ * Spec `ChartConfig.yAxis` — one entry per value axis, in declaration order.
72
+ * Index 0 is the primary axis; a second entry (or one with
73
+ * `position: 'right'`) turns on the secondary axis that `series[].yAxis`
74
+ * binds to. Carries `min`/`max` (domain), `format` (ticks), `logarithmic`
75
+ * (scale) and `title`.
76
+ */
77
+ yAxes?: NormalizedAxis[];
78
+ /** Spec `ChartConfig.showLegend`. Omitted → shown (the schema default). */
79
+ showLegend?: boolean;
80
+ /** Spec `ChartConfig.showDataLabels` — print each point's value on the mark. */
81
+ showDataLabels?: boolean;
82
+ /** Spec `ChartConfig.title` / `.subtitle`, rendered above the plot. */
83
+ title?: string;
84
+ subtitle?: string;
85
+ /**
86
+ * Spec `ChartConfig.description` — the accessibility description. A chart is
87
+ * a picture to a screen reader; without this it announces as an unlabelled
88
+ * graphic, so the container carries it as `role="img"` + `aria-label`.
89
+ */
90
+ description?: string;
91
+ /** Spec `ChartConfig.height` — fixed plot height in pixels. */
92
+ height?: number;
93
+ /** Spec `ChartConfig.annotations` — reference lines / bands. */
94
+ annotations?: Array<Record<string, any>>;
95
+ /** Spec `ChartConfig.interaction` — `tooltips`, `brush`. */
96
+ interaction?: Record<string, any>;
46
97
  /**
47
98
  * Disable Recharts' entrance animation when `false`. Animations drive the
48
99
  * reveal via `requestAnimationFrame`, which browsers throttle/pause in
@@ -55,7 +106,10 @@ export interface AdvancedChartImplProps {
55
106
  isAnimationActive?: boolean;
56
107
  }
57
108
  /**
58
- * AdvancedChartImpl - The heavy implementation that imports Recharts with full features
59
- * This component is lazy-loaded to avoid including Recharts in the initial bundle
109
+ * Public entry point. A THIN wrapper purely so the unreadable-data guard can
110
+ * short-circuit without breaking the rules of hooks: the condition depends on
111
+ * `data`, which arrives asynchronously, so an early return inside the renderer
112
+ * would change its hook count between renders. The wrapper's own hook list is
113
+ * fixed, and the renderer below is reached with data it can actually plot.
60
114
  */
61
- export default function AdvancedChartImpl({ chartType: rawChartType, data: rawData, config, xAxisKey, series, className, colors, categoryColors, onChartClick, isAnimationActive, }: AdvancedChartImplProps): React.JSX.Element;
115
+ export default function AdvancedChartImpl(props: AdvancedChartImplProps): React.JSX.Element;
@@ -11,7 +11,23 @@ declare const THEMES: {
11
11
  readonly light: "";
12
12
  readonly dark: ".dark";
13
13
  };
14
- export type ChartConfig = {
14
+ /**
15
+ * Per-series presentation for `<ChartContainer>`: a map from series dataKey to
16
+ * the legend label, icon and colour (or per-theme colours) that key renders
17
+ * with. Shadcn's chart primitive, kept in this package's own copy of it.
18
+ *
19
+ * Named `ChartContainerConfig`, not `ChartConfig` (objectui#3161,
20
+ * objectstack#4115 ledger batch 7). `@objectstack/spec/ui` owns `ChartConfig`
21
+ * for the authored chart METADATA — `type: bar | line | pie | …`, `xAxis`,
22
+ * `yAxis`, `series`, `showLegend`, `annotations` — and the collision was not
23
+ * theoretical here: `AdvancedChartImpl.tsx` declares `config?: ChartConfig`
24
+ * meaning THIS map, three lines above doc comments that say "Spec
25
+ * `ChartConfig.yAxis`" and "Spec `ChartConfig.showLegend`" meaning the other
26
+ * one. One identifier, two concepts, one file. `normalizeChartSchema` is the
27
+ * seam between them: it reads the authored spec chart and produces the
28
+ * renderer's axes/series, of which this map is the styling half.
29
+ */
30
+ export type ChartContainerConfig = {
15
31
  [k in string]: {
16
32
  label?: React.ReactNode;
17
33
  icon?: React.ComponentType;
@@ -24,7 +40,7 @@ export type ChartConfig = {
24
40
  });
25
41
  };
26
42
  declare function ChartContainer({ id, className, children, config, disableSettleRemount, ...props }: React.ComponentProps<"div"> & {
27
- config: ChartConfig;
43
+ config: ChartContainerConfig;
28
44
  children: React.ComponentProps<typeof ResponsiveContainer>["children"];
29
45
  /**
30
46
  * Skip the settle re-mount below. Set by callers that render their series with
@@ -36,7 +52,7 @@ declare function ChartContainer({ id, className, children, config, disableSettle
36
52
  }): React.JSX.Element;
37
53
  declare const ChartStyle: ({ id, config }: {
38
54
  id: string;
39
- config: ChartConfig;
55
+ config: ChartContainerConfig;
40
56
  }) => React.JSX.Element | null;
41
57
  declare const ChartTooltip: typeof Tooltip;
42
58
  declare function ChartTooltipContent({ active, payload, className, indicator, hideLabel, hideIndicator, label, labelFormatter, labelClassName, formatter, color, nameKey, labelKey, }: any): React.JSX.Element | null;