@object-ui/plugin-dashboard 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 (58) hide show
  1. package/CHANGELOG.md +5044 -0
  2. package/README.md +465 -85
  3. package/dist/DashboardConfigPanel.d.ts +1 -1
  4. package/dist/DashboardFilterBar.d.ts.map +1 -1
  5. package/dist/DashboardGridLayout.d.ts +10 -8
  6. package/dist/DashboardGridLayout.d.ts.map +1 -1
  7. package/dist/DashboardRenderer.d.ts +60 -2
  8. package/dist/DashboardRenderer.d.ts.map +1 -1
  9. package/dist/DashboardWithConfig.d.ts +1 -1
  10. package/dist/DashboardWithConfig.d.ts.map +1 -1
  11. package/dist/DatasetWidget.d.ts +41 -14
  12. package/dist/DatasetWidget.d.ts.map +1 -1
  13. package/dist/DrillDownDrawer.d.ts +14 -5
  14. package/dist/DrillDownDrawer.d.ts.map +1 -1
  15. package/dist/MetricCard.d.ts +7 -1
  16. package/dist/MetricCard.d.ts.map +1 -1
  17. package/dist/MetricWidget.d.ts +48 -6
  18. package/dist/MetricWidget.d.ts.map +1 -1
  19. package/dist/ObjectDataTable.d.ts +407 -18
  20. package/dist/ObjectDataTable.d.ts.map +1 -1
  21. package/dist/ObjectMetricWidget.d.ts +35 -8
  22. package/dist/ObjectMetricWidget.d.ts.map +1 -1
  23. package/dist/ObjectPivotTable.d.ts +47 -7
  24. package/dist/ObjectPivotTable.d.ts.map +1 -1
  25. package/dist/PivotTable.d.ts +20 -3
  26. package/dist/PivotTable.d.ts.map +1 -1
  27. package/dist/RecordDetailDrawer.d.ts +21 -2
  28. package/dist/RecordDetailDrawer.d.ts.map +1 -1
  29. package/dist/WidgetConfigPanel.d.ts +2 -2
  30. package/dist/WidgetConfigPanel.d.ts.map +1 -1
  31. package/dist/WidgetEmptyState.d.ts +27 -0
  32. package/dist/WidgetEmptyState.d.ts.map +1 -0
  33. package/dist/chartRenderHandoff.d.ts +9 -0
  34. package/dist/chartRenderHandoff.d.ts.map +1 -0
  35. package/dist/domPassthroughPins.d.ts.map +1 -1
  36. package/dist/index.d.ts +22 -21
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +1977 -1715
  39. package/dist/index.umd.cjs +3 -2
  40. package/dist/legacyRetiredWidget.d.ts +28 -3
  41. package/dist/legacyRetiredWidget.d.ts.map +1 -1
  42. package/dist/measureRefusal.d.ts +50 -0
  43. package/dist/measureRefusal.d.ts.map +1 -0
  44. package/dist/metricTile.d.ts +87 -0
  45. package/dist/metricTile.d.ts.map +1 -0
  46. package/dist/recordFields.d.ts +119 -8
  47. package/dist/recordFields.d.ts.map +1 -1
  48. package/dist/retired-node-types.d.ts +29 -0
  49. package/dist/retired-node-types.d.ts.map +1 -0
  50. package/dist/schemaHostProps.d.ts +55 -11
  51. package/dist/schemaHostProps.d.ts.map +1 -1
  52. package/dist/useDashboardAutoRefresh.d.ts +47 -0
  53. package/dist/useDashboardAutoRefresh.d.ts.map +1 -0
  54. package/dist/utils.d.ts +60 -13
  55. package/dist/utils.d.ts.map +1 -1
  56. package/dist/widgetDispatch.d.ts +174 -18
  57. package/dist/widgetDispatch.d.ts.map +1 -1
  58. package/package.json +15 -16
@@ -1,4 +1,4 @@
1
- import { DashboardWidgetSchema } from '@object-ui/types';
1
+ import { DashboardWidgetSlotEntry } from './widgetDispatch.js';
2
2
  /**
3
3
  * The placeholder schema rendered in place of a retired inline-analytics widget.
4
4
  *
@@ -10,7 +10,7 @@ import { DashboardWidgetSchema } from '@object-ui/types';
10
10
  */
11
11
  export declare const LEGACY_RETIRED_WIDGET_SCHEMA: {
12
12
  readonly type: "text";
13
- readonly value: "This widget uses a retired data format. Edit it to bind a dataset.";
13
+ readonly content: "This widget uses a retired data format. Edit it to bind a dataset.";
14
14
  readonly variant: "caption";
15
15
  readonly align: "center";
16
16
  readonly className: "flex h-full w-full items-center justify-center rounded border border-dashed border-destructive/40 bg-destructive/5 p-4 text-center text-destructive";
@@ -37,5 +37,30 @@ export declare const LEGACY_RETIRED_WIDGET_SCHEMA: {
37
37
  * surface no observable behavior, and it keeps the predicate true on its own
38
38
  * terms for any surface that has no such fork.
39
39
  */
40
- export declare function isLegacyRetiredWidget(widget: DashboardWidgetSchema | null | undefined): boolean;
40
+ export declare function isLegacyRetiredWidget(widget: DashboardWidgetSlotEntry | null | undefined): boolean;
41
+ /**
42
+ * Is `node`, read from a widget's legacy `component` envelope
43
+ * (`{ id, component, layout }`, objectui's own format, which the spec's widget
44
+ * has no member for), a node the surfaces no longer draw? True for every node
45
+ * but a `metric-card` (objectui#12254).
46
+ *
47
+ * The envelope takes a `metric-card` and nothing else: the 2026-08-14 slot
48
+ * ruling (objectstack#8593) allowed that one node, and since objectui#12163
49
+ * (ruling C) both zod faces and `DashboardWidgetSchema['component']` refuse any
50
+ * other node by name. objectui#12254 retires the draw to match, after a census
51
+ * of stored use found no producer. So the accept set is the authoring one, read
52
+ * from the same closed list, `DASHBOARD_COMPONENT_WIDGET_TYPES`: a node whose
53
+ * `type` is a member draws as before, and anything else in the envelope (a
54
+ * node of another type, a node with no `type`, a string or a number) draws
55
+ * {@link LEGACY_RETIRED_WIDGET_SCHEMA}, the rebind prompt the metric and pivot
56
+ * arms return, and sends no query. The `object-metric` node objectui#11466
57
+ * retired here first is one case of the rule.
58
+ *
59
+ * An empty envelope (`component` absent, or a falsy value) holds no node, so
60
+ * this answers false and the widget draws by its family, as before.
61
+ *
62
+ * ⛔ Read only at the envelope: the same node types authored on a page, or a
63
+ * `metric-card` placed directly in `widgets[]`, are not judged here.
64
+ */
65
+ export declare function isRetiredEnvelopeNode(node: unknown): boolean;
41
66
  //# sourceMappingURL=legacyRetiredWidget.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"legacyRetiredWidget.d.ts","sourceRoot":"","sources":["../src/legacyRetiredWidget.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAE9D;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B;;;;;;CAM/B,CAAC;AAoBX;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAM/F"}
1
+ {"version":3,"file":"legacyRetiredWidget.d.ts","sourceRoot":"","sources":["../src/legacyRetiredWidget.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAuDH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAEjE;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B;;;;;;CAM/B,CAAC;AAoBX;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,wBAAwB,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAMlG;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAI5D"}
@@ -0,0 +1,50 @@
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
+ /**
9
+ * objectui#8894 — what the widget door says about a widget's MEASURES.
10
+ *
11
+ * Returns the message of the `custom` issue objectui's authoring door raises at
12
+ * `values` for this type, these measures and these dimensions, or `undefined`
13
+ * when the door accepts them. The door is `DashboardWidgetSchema` from
14
+ * `@object-ui/types/zod`, which re-attaches the spec's own object-level checks,
15
+ * so its verdict and its message are the spec's (pinned by
16
+ * `packages/types/src/__tests__/dashboard-widget-metric-measure-door-8894.test.ts`).
17
+ *
18
+ * The door is ASKED, never restated: no widget-type list lives here. Whichever
19
+ * types the spec refuses a second measure on — the metric family since
20
+ * `@objectstack/spec` 17.5.0 (`checkDashboardWidgetMetricMeasureArity`), and
21
+ * any further shape once a spec release refuses it and the mirror re-attaches
22
+ * that check (objectstack#20958 is the pending one) — the authoring pickers that
23
+ * read this follow without an edit here.
24
+ *
25
+ * Only the keys a measure rule reads go into the probe — `id` (the message
26
+ * names it), `type`, `values` and `dimensions` — each kept to a shape the door
27
+ * accepts. zod skips object-level checks once a field-level issue aborts the
28
+ * parse, and a draft in an editor is half-written by nature: measured on the
29
+ * mirror, a numeric `id`, a non-string dimension or a malformed `title`
30
+ * anywhere in the document each hide the measure verdict completely, which
31
+ * would read as "accepted". The probe keeps those faults out of the question it
32
+ * asks.
33
+ *
34
+ * Module-private to this package (not re-exported from its index).
35
+ */
36
+ export declare function measureRefusal(widget: {
37
+ id?: unknown;
38
+ type?: unknown;
39
+ values?: unknown;
40
+ dimensions?: unknown;
41
+ }): string | undefined;
42
+ /**
43
+ * A stand-in for "one more measure", used to ask the door whether a picker may
44
+ * offer another one before the author has chosen which. The door's measure
45
+ * rules judge how many measures a widget declares, not their names (whether a
46
+ * measure exists in the bound dataset is out of the widget schema's reach), so
47
+ * any name the widget does not already carry asks the same question.
48
+ */
49
+ export declare const NEXT_MEASURE_PROBE = "__next_measure__";
50
+ //# sourceMappingURL=measureRefusal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"measureRefusal.d.ts","sourceRoot":"","sources":["../src/measureRefusal.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE;IACrC,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,GAAG,MAAM,GAAG,SAAS,CAgBrB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,qBAAqB,CAAC"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * The metric tile's layout rule — ONE home, read by every metric tile in this
3
+ * package: the plain cards (`MetricCard`, `MetricWidget`, and through it
4
+ * `ObjectMetricWidget` and every `object-metric` page block) and the
5
+ * dataset-bound tile (`DatasetWidget`) (objectui#12623).
6
+ *
7
+ * - The title owns the first line: 13 px / 500, up to two lines, an ellipsis
8
+ * on the second. Nothing shares its line but the plain card's icon chip.
9
+ * - The value never wraps, and it scales before it would clip
10
+ * (objectui#12942). A short value is one step smaller (20 px) in a tile
11
+ * narrower than 160 px, 24 px from 160 px up. A longer one — a seven-digit
12
+ * money figure with cents — takes a size ladder from 14 px to 24 px whose
13
+ * steps start where that size fits (`metricTileValueClass`).
14
+ * - The measure label (dataset-bound tile only) is an 11 px muted line under
15
+ * the value, dropped below 160 px. The title's tooltip carries it, so it is
16
+ * still there at every width.
17
+ *
18
+ * Width rules key off the TILE's own box (`@container` on the tile, `@[160px]:`
19
+ * variants), never the viewport: a dashboard grid, a page block and the
20
+ * Studio's folded canvas each size the same tile differently.
21
+ *
22
+ * Class strings stay static literals so Tailwind's content scanner sees them
23
+ * (`apps/console/src/index.css` sources this package's `src/**`).
24
+ */
25
+ /** Put on the tile's root: the box the width rules below are measured against. */
26
+ export declare const METRIC_TILE_CONTAINER_CLASS = "@container";
27
+ /** The title: owns the first line, at most two lines, an ellipsis on the second. */
28
+ export declare const METRIC_TILE_TITLE_CLASS = "min-w-0 line-clamp-2 break-words text-[13px] font-medium leading-snug text-foreground";
29
+ /**
30
+ * The value: never wraps; 20 px below a 160 px tile, 24 px from 160 px up. The
31
+ * short value's face on a plain tile, and the dataset-bound tile's value at
32
+ * every length: that tile has no Card inset, so its value has the container's
33
+ * whole width and the ladder below, measured against a Card, does not apply.
34
+ */
35
+ export declare const METRIC_TILE_VALUE_CLASS = "max-w-full overflow-hidden text-ellipsis whitespace-nowrap tabular-nums text-xl @[160px]:text-2xl";
36
+ /**
37
+ * The value's size ladder, chosen by how wide the value is (objectui#12942).
38
+ *
39
+ * A plain tile (`MetricCard`, `MetricWidget`: a `Card`, 1 px border, the value
40
+ * in `CardContent`'s 24 px side padding) gives its value the container's width
41
+ * less {@link METRIC_TILE_VALUE_INSET} px — the container query reads the
42
+ * Card's content box, inside the border. A value `em` wide fits at a size of
43
+ * `px` from a container `INSET + em * px` wide, so each `@[N px]:` step below
44
+ * starts at `ceil(INSET + maxEm * px)` for its rung's widest value. The
45
+ * `metricTileValueScale-12942` test re-derives every step from that formula.
46
+ *
47
+ * The first rung is the short value's face, {@link METRIC_TILE_VALUE_CLASS}'s
48
+ * sizes unchanged. Every later rung starts at 14 px; past the last one, 14 px
49
+ * at every width. A value still wider than its tile at 14 px keeps the
50
+ * ellipsis, and the value node carries the full figure as its tooltip.
51
+ *
52
+ * Literals only: Tailwind's content scanner must see every class.
53
+ */
54
+ export declare const METRIC_TILE_VALUE_INSET = 48;
55
+ export declare const METRIC_TILE_VALUE_LADDER: ReadonlyArray<{
56
+ readonly maxEm: number;
57
+ readonly sizes: string;
58
+ }>;
59
+ /** Past the ladder's last rung: 14 px at every width. */
60
+ export declare const METRIC_TILE_VALUE_FINAL_SIZES = "text-sm";
61
+ /**
62
+ * How wide `text` draws, in em, at the tile's bold tabular figures — an upper
63
+ * bound read off Inter, the console's face: a digit is 0.65 em, a digit
64
+ * separator (`,` `.` `'` or a space) 0.27 em, a sign or currency symbol 0.7 em,
65
+ * and any other character a full em (letters, CJK).
66
+ */
67
+ export declare function metricTileValueEm(text: string): number;
68
+ /**
69
+ * The value node's class for `value`: the rung of {@link METRIC_TILE_VALUE_LADDER}
70
+ * its width falls in, or the 14 px final face past the last rung.
71
+ */
72
+ export declare function metricTileValueClass(value: string | number): string;
73
+ /** The measure label: an 11 px muted line under the value, dropped below 160 px. */
74
+ export declare const METRIC_TILE_MEASURE_LABEL_CLASS = "hidden max-w-full truncate text-[11px] text-muted-foreground @[160px]:block";
75
+ /**
76
+ * The title's tooltip: the description when one is authored, else the title,
77
+ * then the measure label on its own line — the one place the label survives in
78
+ * a tile too narrow to draw it.
79
+ */
80
+ export declare function metricTileTooltip(title: string | undefined, description?: string, measureLabel?: string): string | undefined;
81
+ /**
82
+ * The value node's tooltip: the full figure, for a value past the short rung —
83
+ * the one place it survives in a tile too narrow even for 14 px. A short value
84
+ * draws none, as before.
85
+ */
86
+ export declare function metricTileValueTitle(value: string | number): string | undefined;
87
+ //# sourceMappingURL=metricTile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metricTile.d.ts","sourceRoot":"","sources":["../src/metricTile.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,kFAAkF;AAClF,eAAO,MAAM,2BAA2B,eAAe,CAAC;AAExD,oFAAoF;AACpF,eAAO,MAAM,uBAAuB,0FAA0F,CAAC;AAE/H;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,sGAAsG,CAAC;AAE3I;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C,eAAO,MAAM,wBAAwB,EAAE,aAAa,CAAC;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAOtG,CAAC;AAEF,yDAAyD;AACzD,eAAO,MAAM,6BAA6B,YAAY,CAAC;AAIvD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAStD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAInE;AAED,oFAAoF;AACpF,eAAO,MAAM,+BAA+B,gFAAgF,CAAC;AAE7H;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG5H;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAG/E"}
@@ -8,6 +8,74 @@ export declare const SYSTEM_FIELDS: Set<string>;
8
8
  export declare function isSystemField(name: string, def?: any): boolean;
9
9
  /** Numeric-flavoured field types (right-aligned in tables). */
10
10
  export declare const NUMERIC_FIELD_TYPES: Set<string>;
11
+ /**
12
+ * The override vocabulary this package's two field surfaces share.
13
+ *
14
+ * ⚠️ This type is a DERIVATION SOURCE, not only a shape: `ObjectDataTable`'s
15
+ * two column bands are both `Exclude<keyof FieldMeta, …>` — `EnrichedColumn`'s
16
+ * write-side tombstones (objectui#6373) and `AuthoredColumnOverrides`' read-side
17
+ * refusal band (objectui#6425). So a member REMOVED here silently leaves both
18
+ * bands, and any refusal that rode on its membership stops enforcing with
19
+ * nothing going red. A member being retired must therefore be re-refused
20
+ * explicitly at that seam before it leaves this type — see
21
+ * `ObjectDataTableRetiredDecimalsTombstone` in `ObjectDataTable.tsx`, which is
22
+ * what `decimals` left behind.
23
+ *
24
+ * ⛔ `decimals` was RETIRED from this type by objectui#6625 — written from the
25
+ * schema def on every call and read by nothing (zero `.decimals` member reads
26
+ * across `@object-ui/fields`, `@object-ui/i18n`, `@object-ui/components`,
27
+ * `@object-ui/core` and this package, measured against a `.scale` positive
28
+ * control that hits `NumberField.tsx` / `GridField.tsx` / `index.tsx`). If a
29
+ * reader is ever wanted here it reads `scale` — the field def's decimal-places
30
+ * key, which is what the retired write resolved to anyway and what
31
+ * `NumberCellRenderer` already reads (`precision` is the total digit count, a
32
+ * different question — objectui#2131). ⛔ Do not resurrect `decimals`.
33
+ *
34
+ * ⛔ `referenceTo` was RETIRED from this type by objectui#6597 (enforce-or-remove,
35
+ * withdraw branch — maintainer's standing startup-stage rule, 2026-08-27: no
36
+ * measured demand retires immediately). It was documented by this package's
37
+ * README as an author-facing column override, but the promise was never kept:
38
+ * `LookupCellRenderer` (`@object-ui/fields`) resolves its lookup target from
39
+ * `field.reference_to` / `field.reference` — never `field.referenceTo` — and
40
+ * `computeLookupExpand` (`ObjectDataTable.tsx`) builds `$expand` from the OBJECT
41
+ * SCHEMA's field types, never from an authored override. Measured with the
42
+ * `referenceTo`-vs-`options` positive control in
43
+ * `ObjectDataTable.overrideSource-6425.test.tsx`: `options` (a live override)
44
+ * separates two equal-valued columns, `referenceTo` does not — an authored
45
+ * override renders byte-identical to its absence. No authoring story survived
46
+ * the search either: the sibling `ObjectGrid` producer's own relational-meta
47
+ * pass-through (`applyRelationalMeta`, `plugin-grid/src/ObjectGrid.tsx`) copies
48
+ * `reference_to` / `reference` from the SCHEMA field def only, never from an
49
+ * authored column override, and no example/doc/fixture in this repo shows a
50
+ * table column pinning a lookup's target to something other than what its
51
+ * schema field already says. If a reader for the resolved reference target is
52
+ * ever wanted here, it reads `reference_to` / `reference` off the schema field
53
+ * def directly — the spelling `LookupCellRenderer` and `computeLookupExpand`
54
+ * actually use. ⛔ Do not resurrect `referenceTo`.
55
+ *
56
+ * ⭐ That reader ARRIVED (objectui#6694): `reference` / `displayField` below
57
+ * (with `reference_to` beside them until objectui#11070 round 4 retired it,
58
+ * and `display_field` until objectui#11070's text-family round did), copied
59
+ * from the SCHEMA field def by {@link buildFieldMeta} and justified per key on
60
+ * `CELL_RELATIONAL_META_KEYS`.
61
+ * They are the "future reader" both retirement notes predicted, in the spelling
62
+ * they named, and they change neither verdict — the source is the schema field
63
+ * def, never an authored column override, which is the exact distinction
64
+ * objectui#6597 measured and withdrew on.
65
+ *
66
+ * ⚠️ Being `FieldMeta` members they GROW both derived bands in
67
+ * `ObjectDataTable.tsx` — `EnrichedColumn`'s emit tombstones and
68
+ * `UnheldFieldMetaOverrideKey`'s read-side refusal. `reference_to` left the
69
+ * type in objectui#11070 round 4 and `display_field` in its text-family round,
70
+ * so — by the rule at the top of this docblock — each one's refusal is
71
+ * re-stated by hand there (`ObjectDataTableRetiredReferenceToSnakeTombstone`,
72
+ * `ObjectDataTableRetiredDisplayFieldSnakeTombstone`). That is the intended
73
+ * verdict rather than a side effect: an AUTHORED column may not source a
74
+ * lookup's reference target (objectui#6597 measured no authoring story for
75
+ * one), while the schema-derived write is reached by neither band. They landed
76
+ * in both without anyone editing a list — the property this derivation exists
77
+ * for.
78
+ */
11
79
  export interface FieldMeta {
12
80
  name: string;
13
81
  label: string;
@@ -17,10 +85,34 @@ export interface FieldMeta {
17
85
  label: string;
18
86
  color?: string;
19
87
  }>;
20
- referenceTo?: unknown;
21
88
  format?: string;
22
89
  currency?: string;
23
- decimals?: number;
90
+ /**
91
+ * The field def's declared upper bound, which on a `percent` field is its
92
+ * STORAGE statement: the spec's `percentScaleOf` reads a fraction unless
93
+ * `max` is above 1 (objectui#11475). Copied from the SCHEMA field def by
94
+ * {@link buildFieldMeta} so the percent cell and the `%` branch of
95
+ * {@link renderFieldValue} read the field's storage rather than guess it
96
+ * from the value. Never an authored column override: as a `FieldMeta`
97
+ * member it lands in `ObjectDataTable`'s read-side refusal band, which is
98
+ * the intended verdict — a column may not restate how its field stores.
99
+ */
100
+ max?: number;
101
+ /**
102
+ * Lookup target object — the spelling `@objectstack/spec`'s `FieldSchema`
103
+ * declares and the only one `LookupCellRenderer` reads. Its snake_case twin
104
+ * `reference_to` was RETIRED from this type by objectui#11070 round 4.
105
+ */
106
+ reference?: string;
107
+ /**
108
+ * Author-declared display field on the lookup, in the SPEC spelling
109
+ * (`FieldSchema.displayField`) — it beats every resolver in the cell. The
110
+ * first leg of `LookupCellRenderer`'s `displayField || reference_field`
111
+ * chain, and the only leg a spec-compliant producer can emit
112
+ * (objectui#6875). Its snake_case twin `display_field` was RETIRED from this
113
+ * type by objectui#11070's text-family round: no reader read it.
114
+ */
115
+ displayField?: string;
24
116
  }
25
117
  /**
26
118
  * Normalize an object schema's `fields` into a `{ name → def }` map. Accepts
@@ -37,20 +129,39 @@ export interface BuildFieldMetaParams {
37
129
  objectName?: string;
38
130
  /** Translator for per-option labels (from `useSafeFieldLabel`). */
39
131
  fieldOptionLabel?: (objectName: string, field: string, value: string, fallback: string) => string;
40
- /** Per-column overrides (table columns may pin type/format/options). */
132
+ /**
133
+ * Per-column overrides (table columns may pin type/format/options).
134
+ *
135
+ * A subset of {@link FieldMeta}, which is what makes that type the pool
136
+ * `ObjectDataTable`'s refusal band derives from. `decimals` left with the
137
+ * member (objectui#6625): its last feeder went when objectui#6425's ruling
138
+ * removed the authored read from `ObjectDataTable.enrich()`, and
139
+ * `RecordDetailDrawer` — the only other caller — passes no overrides at all.
140
+ * `referenceTo` left the same way (objectui#6597): `ObjectDataTable.enrich()`
141
+ * no longer reads it off the authored column, and `RecordDetailDrawer` never
142
+ * did.
143
+ */
41
144
  overrides?: {
42
145
  type?: string;
43
146
  format?: string;
44
147
  options?: any;
45
- referenceTo?: unknown;
46
148
  currency?: string;
47
- decimals?: number;
48
149
  };
49
150
  }
50
151
  /**
51
- * Build the `FieldMeta` for a single field, resolving `referenceTo`, currency
52
- * and decimals from the schema field def and translating select options.
53
- * Column-level overrides win over schema-derived values.
152
+ * Build the `FieldMeta` for a single field, resolving currency from the
153
+ * schema field def and translating select options. Column-level overrides
154
+ * win over schema-derived values.
155
+ *
156
+ * The field def's currency goes through `resolveFieldCurrency`
157
+ * (`@object-ui/i18n`) rather than a chain of this helper's own (objectui#10463).
158
+ * The resolver is what reads `currencyConfig`, the spec's one fixed-currency
159
+ * declaration, and only under `currencyMode: 'fixed'`. The chain it replaced
160
+ * read the flat `currency` / `defaultCurrency` spellings only, so a fixed
161
+ * field painted in the tenant currency in both the table cell and the record
162
+ * drawer. The call passes NO tenant default: `renderFieldValue` backstops with
163
+ * the tenant only after the symbol it infers from a `format`, and a tenant code
164
+ * resolved here would jump ahead of that symbol.
54
165
  */
55
166
  export declare function buildFieldMeta(params: BuildFieldMetaParams): FieldMeta;
56
167
  /** Whether a `FieldMeta` should be right-aligned (numeric / currency / percent). */
@@ -1 +1 @@
1
- {"version":3,"file":"recordFields.d.ts","sourceRoot":"","sources":["../src/recordFields.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAoB1B;;;GAGG;AACH,eAAO,MAAM,aAAa,aAIxB,CAAC;AAEH,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,OAAO,CAG9D;AAKD,+DAA+D;AAC/D,eAAO,MAAM,mBAAmB,aAE9B,CAAC;AAEH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,GAAG,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAUxE;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IAClG,wEAAwE;IACxE,SAAS,CAAC,EAAE;QACV,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,GAAG,CAAC;QACd,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,SAAS,CAoCtE;AAED,oFAAoF;AACpF,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,CAAC,GAAG,OAAO,CAKzF;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,GAAG,EACV,SAAS,EAAE,SAAS,EACpB,cAAc,CAAC,EAAE,MAAM,EAKvB,aAAa,CAAC,EAAE,MAAM,GACrB,KAAK,CAAC,SAAS,CAiDjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAMhD"}
1
+ {"version":3,"file":"recordFields.d.ts","sourceRoot":"","sources":["../src/recordFields.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAyB1B;;;GAGG;AACH,eAAO,MAAM,aAAa,aAIxB,CAAC;AAEH,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,OAAO,CAG9D;AAKD,+DAA+D;AAC/D,eAAO,MAAM,mBAAmB,aAE9B,CAAC;AAyIH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,GAAG,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;;OASG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAUxE;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IAClG;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,EAAE;QACV,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,GAAG,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,SAAS,CA8CtE;AAED,oFAAoF;AACpF,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,CAAC,GAAG,OAAO,CAKzF;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,GAAG,EACV,SAAS,EAAE,SAAS,EACpB,cAAc,CAAC,EAAE,MAAM,EAKvB,aAAa,CAAC,EAAE,MAAM,GACrB,KAAK,CAAC,SAAS,CA4EjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAMhD"}
@@ -0,0 +1,29 @@
1
+ import { default as React } from 'react';
2
+ /**
3
+ * The bare name of every retired spelling, mapped to the refusal text the
4
+ * tombstone renders and logs. Keyed by the BARE name because that is what
5
+ * `ComponentRegistry.register` takes; the namespace is spelled at the call.
6
+ *
7
+ * The message names BOTH halves deliberately — the spelling that was written
8
+ * and the spelling that replaces it. A refusal that only says "unknown" is the
9
+ * one this table exists to stop being.
10
+ */
11
+ export declare const RETIRED_DASHBOARD_NODE_TYPES: Readonly<Record<string, string>>;
12
+ /**
13
+ * Log a retired node-type spelling once. Exported for the pin test, which has
14
+ * to be able to observe a SECOND occurrence being suppressed without inventing
15
+ * its own copy of the set.
16
+ */
17
+ export declare function reportRetiredDashboardNodeType(spelling: string): void;
18
+ /** Test seam: forget what has been reported, so a pin can measure the first one. */
19
+ export declare function resetRetiredDashboardNodeTypeReports(): void;
20
+ /**
21
+ * The widget a RETIRED dashboard node-type spelling renders.
22
+ *
23
+ * The rendered text and the logged text are the SAME string, read from
24
+ * {@link RETIRED_DASHBOARD_NODE_TYPES}. A pin that asserts only "nothing
25
+ * rendered" cannot tell this refusal apart from the key having been deleted
26
+ * outright, which is why the text is part of the contract and not decoration.
27
+ */
28
+ export declare const RetiredDashboardNodeTombstone: React.FC<Record<string, unknown>>;
29
+ //# sourceMappingURL=retired-node-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retired-node-types.d.ts","sourceRoot":"","sources":["../src/retired-node-types.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOxE,CAAC;AAKH;;;;GAIG;AACH,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAOrE;AAED,oFAAoF;AACpF,wBAAgB,oCAAoC,IAAI,IAAI,CAE3D;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,6BAA6B,EAAE,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAwB3E,CAAC"}
@@ -1,10 +1,4 @@
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
- */
1
+ import { DomProps } from '@object-ui/core';
8
2
  /**
9
3
  * The props `SchemaRenderer` hands a widget that are NOT DOM attributes
10
4
  * (objectui#4357).
@@ -43,10 +37,21 @@
43
37
  *
44
38
  * The line this type draws is **"is the key an HTML attribute name"**. None of
45
39
  * the seven is (the two dashed `aria-*` forms the renderer emits itself are, and
46
- * they keep flowing). Everything that IS one stays in the spread and reaches the
47
- * DOM exactly as before: `id`, `name`, `role`, `disabled`, `aria-*`, `data-*`,
48
- * `className`. Removing the spread instead would have been the wrong fix — it is
49
- * the component's only accessibility passthrough.
40
+ * they keep flowing). Removing the spread instead would have been the wrong fix —
41
+ * it is the component's only accessibility passthrough.
42
+ *
43
+ * ⚠️ That line was a DENY-LIST, and on the renderer's door it is no longer the
44
+ * judge. It stripped every one of the seven and the leak gate
45
+ * (`packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsx`) still
46
+ * found the OPEN TAIL on both components — an authored key neither declares
47
+ * (`zzcanary`, `reference_to`, a `props: { colorVariant }` on `metric-card`, a
48
+ * `label` on `metric-card`) and `name` all landed as attributes, because a
49
+ * deny-list can only name what already exists. Per objectui#4425's phase-2
50
+ * ruling, what a component reached through `SchemaRenderer` may put on its
51
+ * element is decided by `toDomProps`' WHITELIST — see {@link hostDomProps}
52
+ * below. The seven are still destructured by name: they are this type's keys,
53
+ * and on the direct React door (below) the destructure is still what keeps them
54
+ * off the element.
50
55
  *
51
56
  * The renderer strips its own schema metadata (`type` / `children` / `visible` /
52
57
  * the schema's `dataSource` binding / …) before spreading, so those never
@@ -77,4 +82,43 @@ export interface SchemaHostProps {
77
82
  */
78
83
  dataSource?: unknown;
79
84
  }
85
+ /**
86
+ * What a KPI card may spread onto its host element, decided by the DOOR the
87
+ * render came through (objectui#4425, phase-2 ruling: "a registered SDUI
88
+ * widget's host element receives only what passes the `toDomProps` whitelist").
89
+ *
90
+ * Both metric components have two doors, and they hand the component two
91
+ * different populations:
92
+ *
93
+ * - **The renderer's door.** `SchemaRenderer` — the registry render path, and
94
+ * so also an app that registers the exported component under a key of its
95
+ * own — hands the component the authored node's own keys, the contents of
96
+ * its `props` container, the runtime props it injects and its host's
97
+ * trailing props. That set is unbounded, so only the whitelist may decide
98
+ * what reaches the element: `toDomProps` keeps the SDUI contract's declared
99
+ * pass-through set (`SDUI_DOM_PASS_THROUGH_KEYS`) and the `aria-*` /
100
+ * `data-*` families, and drops everything else — `name`, `disabled`, the
101
+ * injected adapter, every authored key neither component declares. One
102
+ * executor, `@object-ui/core`'s, the
103
+ * one `DashboardRenderer`'s grid container and `plugin-chatbot`'s
104
+ * registrations already use — not a third list.
105
+ * - **The direct React door.** A host rendering the exported component passes
106
+ * typed props, and both props interfaces DECLARE `React.HTMLAttributes` as
107
+ * their DOM pass-through (objectui#4426). That declaration is the
108
+ * objectui#4435 pattern the ruling keeps available, so this door stays as it
109
+ * was: narrowing it at runtime while the exported interface still extends
110
+ * `HTMLAttributes` would type-check a `title` or an `onMouseEnter` and drop
111
+ * it — declared but not delivered. Narrowing the interface itself is a
112
+ * public prop-surface change, which this card's fence excludes.
113
+ *
114
+ * The discriminator is `schema`: the one key `SchemaRenderer` injects on EVERY
115
+ * render (the table above), declared on this type as the renderer's private
116
+ * door and never on either exported props interface. A direct consumer that
117
+ * passes it anyway has chosen the renderer's door and gets the whitelist, so a
118
+ * mistaken reading DROPS an attribute and never leaks one. If the renderer ever
119
+ * stopped injecting it, every canary the leak gate plants would reach the DOM
120
+ * again and the gate would go red — that sweep is what holds this function to
121
+ * its claim, alongside `__tests__/MetricWidget.domProps.test.tsx`.
122
+ */
123
+ export declare function hostDomProps<P extends object>(schema: unknown, rest: P): P | DomProps<P>;
80
124
  //# sourceMappingURL=schemaHostProps.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schemaHostProps.d.ts","sourceRoot":"","sources":["../src/schemaHostProps.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,MAAM,WAAW,eAAe;IAC9B,kFAAkF;IAClF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,sDAAsD;IACtD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,2EAA2E;IAC3E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wEAAwE;IACxE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,sEAAsE;IACtE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB"}
1
+ {"version":3,"file":"schemaHostProps.d.ts","sourceRoot":"","sources":["../src/schemaHostProps.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AACH,MAAM,WAAW,eAAe;IAC9B,kFAAkF;IAClF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,sDAAsD;IACtD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,2EAA2E;IAC3E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wEAAwE;IACxE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,sEAAsE;IACtE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAExF"}
@@ -0,0 +1,47 @@
1
+ import { DashboardComponentSchema } from '@object-ui/types';
2
+ /**
3
+ * The dashboard auto-refresh timer: ONE implementation, two mount points
4
+ * (objectui#8820).
5
+ *
6
+ * `DashboardGridLayout` and `DashboardRenderer` each used to carry their own
7
+ * copy of the same three things: a `refreshing` flag, a `handleRefresh`
8
+ * callback shared by the manual "Refresh All" button and the timer, and an
9
+ * effect that read the authored period and drove `setInterval`. The copies were
10
+ * identical in logic, and two copies of one rule is two places to change and
11
+ * one place to forget: the `refreshInterval` to `refreshIntervalSeconds` rename
12
+ * (objectui#7783) had to be made twice, and a copy that drifted would still
13
+ * pass its own component's tests.
14
+ *
15
+ * This hook is that logic, moved here. Both surfaces call it, so the authored
16
+ * period is read in exactly one place.
17
+ *
18
+ * - The period is `refreshIntervalSeconds`, in seconds, and nothing else. The
19
+ * retired `refreshInterval` spelling is refused by the spec's
20
+ * `DashboardSchema`, so no fallback reads it (AGENTS.md #0.1).
21
+ * - The timer runs only when the host wired `onRefresh` AND the period is
22
+ * greater than zero: an absent period, `0` and a negative value all mean
23
+ * "off".
24
+ * - The interval is armed on VALUES only: the period and whether a handler is
25
+ * wired. The handler itself is read through a ref when a run happens, so a
26
+ * new handler identity, from the host or from React discarding a memo, never
27
+ * re-arms the interval or moves its phase, and the run still calls the
28
+ * CURRENT handler (AGENTS.md #10, objectui#11004).
29
+ * - The indicator clears itself 600ms after each run, manual or timed.
30
+ *
31
+ * `__tests__/dashboardAutoRefreshTimer.test.tsx` mounts both surfaces and
32
+ * counts `onRefresh` calls under fake timers. It pins the first three points,
33
+ * the third under a forced memo discard; nothing pins the 600ms indicator.
34
+ */
35
+ /** What a dashboard surface needs to drive its refresh affordance. */
36
+ export interface DashboardAutoRefresh {
37
+ /** True while a refresh is in flight; drives the spinner and disables the button. */
38
+ refreshing: boolean;
39
+ /** Run a refresh now. Wired to both the manual button and the timer. */
40
+ handleRefresh: () => void;
41
+ }
42
+ /**
43
+ * Owns the refresh indicator, the manual refresh handler, and the auto-refresh
44
+ * interval for one dashboard surface.
45
+ */
46
+ export declare function useDashboardAutoRefresh(schema: DashboardComponentSchema, onRefresh?: () => void): DashboardAutoRefresh;
47
+ //# sourceMappingURL=useDashboardAutoRefresh.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDashboardAutoRefresh.d.ts","sourceRoot":"","sources":["../src/useDashboardAutoRefresh.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,sEAAsE;AACtE,MAAM,WAAW,oBAAoB;IACnC,qFAAqF;IACrF,UAAU,EAAE,OAAO,CAAC;IACpB,wEAAwE;IACxE,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAoBD;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,wBAAwB,EAChC,SAAS,CAAC,EAAE,MAAM,IAAI,GACrB,oBAAoB,CAsCtB"}
package/dist/utils.d.ts CHANGED
@@ -1,10 +1,4 @@
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
- */
1
+ import { useObjectLabel, useObjectTranslation } from '@object-ui/i18n';
8
2
  /** Returns true when the widget data config uses provider: 'object' (async data source). */
9
3
  export declare function isObjectProvider(widgetData: unknown): widgetData is {
10
4
  provider: 'object';
@@ -21,6 +15,41 @@ export declare function isObjectProvider(widgetData: unknown): widgetData is {
21
15
  * path derives headers with it when no columns were declared at all.
22
16
  */
23
17
  export declare function humanizeFieldKey(key: string): string;
18
+ /**
19
+ * Resolve a chart series label — the SINGLE authority both dashboard relays
20
+ * (`DashboardRenderer`, `DashboardGridLayout`) call, so the three-arm decision
21
+ * below lives once instead of as two copies that could drift.
22
+ *
23
+ * Originated as a `DashboardRenderer`-local `useCallback` (objectui#9055,
24
+ * which fixed arms 2 and 3 there). `DashboardGridLayout` composed
25
+ * `series: [{ dataKey }]` with no `label` key on either of its branches — the
26
+ * legend and tooltip rendered the raw field key while the sibling relay's
27
+ * chart for the same widget rendered the humanized one (objectui#9172). That
28
+ * card is what moved this here rather than growing a second copy of the
29
+ * three-arm logic: a helper duplicated across the two relays is exactly the
30
+ * "one value, two spellings" class `56f4e34c0` ruled out.
31
+ *
32
+ * ⭐ THREE ARMS, and only the last two derive a display string from a FIELD
33
+ * KEY:
34
+ *
35
+ * arm 1 synthetic y-field ('value' / 'count' / '') + an aggregate function
36
+ * -> an i18n'd aggregate name (Count / Sum / Average …), so a
37
+ * fieldless count aggregate doesn't leak the placeholder 'value' /
38
+ * 'count' string into the legend / tooltip.
39
+ * arm 2 a real object + a real field -> `fieldLabel`'s bundle lookup, with
40
+ * the HUMANIZED key (not the raw one) as its fallback.
41
+ * arm 3 no object name at all (a static-data chart) -> the humanized key
42
+ * outright.
43
+ *
44
+ * `t` and `fieldLabel` are injected rather than the hooks themselves, so this
45
+ * stays a plain function callable from either relay's own
46
+ * `useObjectTranslation()` / `useObjectLabel()` instances — no shared hook
47
+ * identity to manage, and each relay keeps memoizing its own `useCallback`
48
+ * wrapper over this (objectui#10's caution against depending on a memo's
49
+ * IDENTITY is about correctness, not about composing plain functions like
50
+ * this one).
51
+ */
52
+ export declare function composeSeriesLabel(t: ReturnType<typeof useObjectTranslation>['t'], fieldLabel: ReturnType<typeof useObjectLabel>['fieldLabel'], objectName: string | undefined, yField: string, aggFn: string | undefined): string;
24
53
  /**
25
54
  * Columns for a STATIC (inline `options.data`) table widget whose author
26
55
  * declared none — objectui#4618.
@@ -48,6 +77,19 @@ export declare function deriveStaticTableColumns(rows: unknown): Array<{
48
77
  }>;
49
78
  export { resolveDateMacros, resolveFilterPlaceholders } from '@object-ui/core';
50
79
  export { shiftFilterByCompareTo, compareToTrendLabelKey, type CompareToConfig, } from '@object-ui/core';
80
+ /**
81
+ * A period-over-period delta as `computeMetricDelta` returns it.
82
+ *
83
+ * `value` is the absolute percentage change, or `null` when the change has no
84
+ * percentage: a zero baseline with a non-zero current value (objectui#12677).
85
+ * Going from 0 to 10 is not a 100% increase — a percent change from zero is
86
+ * undefined — so the delta keeps its direction and states no number. Every
87
+ * reader draws it through {@link formatMetricDeltaPercent}.
88
+ */
89
+ export interface MetricDelta {
90
+ value: number | null;
91
+ direction: 'up' | 'down' | 'neutral';
92
+ }
51
93
  /**
52
94
  * Compute a percentage delta between the current and previous metric values.
53
95
  *
@@ -56,13 +98,18 @@ export { shiftFilterByCompareTo, compareToTrendLabelKey, type CompareToConfig, }
56
98
  * - previousValue is null / undefined (no comparison data)
57
99
  *
58
100
  * Edge cases:
59
- * - both 0 → neutral
60
- * - previous is 0 and current ≠ 0 → 100% in current's direction
101
+ * - both 0 → neutral, `value: 0`
102
+ * - previous is 0 and current ≠ 0 → current's direction, `value: null` (no
103
+ * percentage is defined from a zero baseline; objectui#12677)
61
104
  * - previous is negative → uses |previous| as denominator so that "less
62
105
  * negative" reads as a positive (up) delta.
63
106
  */
64
- export declare function computeMetricDelta(current: number | null | undefined, previous: number | null | undefined): {
65
- value: number;
66
- direction: 'up' | 'down' | 'neutral';
67
- } | null;
107
+ export declare function computeMetricDelta(current: number | null | undefined, previous: number | null | undefined): MetricDelta | null;
108
+ /**
109
+ * The text a delta badge draws for {@link MetricDelta.value}: `20%` for a
110
+ * defined percentage, and an em dash — never a number — when there is none
111
+ * (a zero baseline, objectui#12677). A symbol, not a word, so no locale pack
112
+ * carries it. The direction arrow beside it still says which way it moved.
113
+ */
114
+ export declare function formatMetricDeltaPercent(value: number | null): string;
68
115
  //# sourceMappingURL=utils.d.ts.map