@close-by/clay 0.2.19 → 0.2.20

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/docs/api.md CHANGED
@@ -1220,6 +1220,21 @@ ui.radialChart({
1220
1220
  });
1221
1221
  ```
1222
1222
 
1223
+ #### Live chart updates
1224
+
1225
+ Every chart factory and `ui.chart.*` builder returns a `ChartElement`:
1226
+
1227
+ | Method | What it sends |
1228
+ |--------|---------------|
1229
+ | `appendData(rows, { max? })` | `appendRows` with only the new rows; client trims to the last `max` |
1230
+ | `setData(rows)` | `updateProps` (`data`) |
1231
+ | `upsertData(row, { key?, max? })` | Replaces the row with the same `key` (default `xKey`, or `nameKey` for pies) or appends it |
1232
+ | `setSeries(series)` / `setHeadline(h)` / `setTitle(t)` / `setDescription(d)` / `setLoading(b)` | `updateProps` for that prop |
1233
+
1234
+ `ui.stat(items)` returns a `StatElement` with `setItems(items)`, `updateItem(titleOrIndex, patch)` and `appendSparkline(titleOrIndex, points, { max? })`. Each sends one `updateProps` (`items`).
1235
+
1236
+ All charts accept `animate?: boolean` (default `true`). See [Updating in place](./updating-in-place.md#live-charts-appenddata--setdata--upsertdata).
1237
+
1223
1238
  #### Structured charts (`ui.chart`)
1224
1239
 
1225
1240
  Optional sugar over the props APIs above. Mode-first builders compile to the same `Element` types — legacy `ui.areaChart` / `ui.pieChart` / etc. remain fully supported.
package/docs/examples.md CHANGED
@@ -26,6 +26,7 @@ Each page sets `group` / `groupIcon` in its `pageMeta`, so the sidebar and the h
26
26
  | `/examples/reui-batches` | `ReuiBatchesDemo.ts` | ReUI batches 3–6: `buttonGroup`, `empty`, `pagination`, `filterBar`, form inputs, `navigationMenu`, `notice`, `stat`+sparkline, `eventCalendar`, datatable `showFilterChips` |
27
27
  | `/examples/reui-batch-7` | `ReuiBatch7Demo.ts` | ReUI batch 7: `inputGroup`, `toggle`, `descriptionList`, `staticTable`, `aspectRatio`, `itemList`, `checkboxGroup` |
28
28
  | `/examples/charts` | `ChartDemo.ts` | `ui.chart.*`, `ui.sparkline`, composed/scatter charts |
29
+ | `/examples/live-charts` | `LiveCharts.ts` | Live charts: `appendData` sliding windows, `setData` tweens, `upsertData` buckets, live gauge and sparklines |
29
30
  | `/examples/slider-demo` | `SliderDemo.ts` | Slider, checkbox, select + bindings |
30
31
  | `/examples/themes` | `ThemesDemo.ts` | Palette gallery: `ui.theme.palette`, light/dark, component preview |
31
32
  | `/examples/layout` | `LayoutDemo.ts` | Fill layout: `fillViewport`, `grow`, `scroll`, `ui.grid` |
package/docs/protocol.md CHANGED
@@ -79,6 +79,7 @@ Browser Back/Forward (or other `popstate`) changed the query and/or hash **witho
79
79
  | `replace` | `id`, `node` | Replace entire subtree at `id` |
80
80
  | `updateProps` | `id`, `props` | Shallow-merge props |
81
81
  | `setChildren` | `id`, `children` | Replace children array |
82
+ | `appendRows` | `id`, `prop`, `rows`, `max?` | Append `rows` to the array prop `prop`, then keep the last `max` items (chart streaming) |
82
83
  | `remove` | `id` | Remove node from parent |
83
84
 
84
85
  ### `navigate`
@@ -20,6 +20,7 @@ The flicker is caused by the remount, not by the new value. If a frame carries o
20
20
  | Change one value (a label, badge, or input) | `label.setText(…)`, `el.setValue(…)`, `el.update({ … })` | `updateProps` for that element |
21
21
  | Live table data | `table.setRows(rows)` on a table created with a stable `keyField` (add `highlightChanges: true` to flash changed rows) | `updateProps` on the table; rows keyed by `keyField` keep their DOM |
22
22
  | Table refetch | `table.setLoading(true)` / `table.withLoading(fn)` | `updateProps` (`loading`); previous rows stay, dimmed, instead of blanking |
23
+ | Live chart data (any chart) | `chart.appendData(point, { max })` for a stream, `chart.setData(rows)` for a snapshot, `chart.upsertData(row)` for a growing bucket | `appendRows` with only the new points, or `updateProps` (`data`); the chart stays mounted |
23
24
  | A section whose structure depends on data | `ui.refreshable(() => …)` + `.refresh()`, or `ui.auto(() => …)` | Reconciled: matched children get `updateProps` only; added, removed, or reordered children cause one `setChildren` per affected parent |
24
25
  | Items in a list inside either of the above | `.key(item.id)` on each item | Items keep their identity through reorder, filter, and prepend |
25
26
 
@@ -31,7 +32,7 @@ When a `refreshable` or `auto` block re-runs, Clay builds a fresh tree and match
31
32
 
32
33
  1. **Keyed first.** A new child with `.key(k)` matches the old child with the same key and the same element type. Keyed children are matched only by key.
33
34
  2. **Unkeyed by position.** Unkeyed children are matched in order, by element type. If the next old child matches the current new one, the current old child was removed and is skipped. If the current old child matches the next new one, the new child was inserted. Otherwise the pair is treated as a replacement at that position. This lookahead covers a single insert or remove; larger reshuffles should be keyed.
34
- 3. **Matched pairs keep the old element and its id.** The fresh element's handlers and props are copied onto the old one. Only props that actually changed are sent, as one `updateProps`.
35
+ 3. **Matched pairs keep the id on screen.** The freshly built element takes over the old element's id and the old one is destroyed. Only props that actually changed are sent, as one `updateProps`. Because the fresh instance is the one that stays live, its built-in handlers (table search, sort and paging, map drags) and any reference you saved while building (`table = ui.dataTable(...)`) keep working after the refresh.
35
36
  4. **Leftovers.** Old children with no match are destroyed. New children with no match are mounted. The parent sends one `setChildren` only if its list of ids changed. A child whose type changed at a position is replaced; its siblings keep their ids.
36
37
  5. **Nested blocks keep their instance.** A nested `refreshable` or `auto` keeps its id and its subscriptions and adopts the new builder, so its own refreshes keep working.
37
38
 
@@ -103,11 +104,35 @@ async function refresh() {
103
104
 
104
105
  If you want columns to stay fixed across refreshes, pass `columns` explicitly. Otherwise columns are inferred again on each `setRows`.
105
106
 
107
+ ### Live charts (`appendData` / `setData` / `upsertData`)
108
+
109
+ Every chart (`ui.lineChart`, `areaChart`, `barChart`, `composedChart`, `scatterChart`, `pieChart`, `radarChart`, `radialChart`, `sparkline`, and the `ui.chart.*` builders) returns a `ChartElement`. Create it once and push data into it.
110
+
111
+ ```typescript
112
+ const latency = ui.lineChart({ data: seed, xKey: 't', series: [{ key: 'p50', label: 'p50' }] });
113
+ const byRegion = ui.barChart({ data: regions, xKey: 'region', series: [{ key: 'orders', label: 'Orders' }] });
114
+
115
+ ui.timer(1, () => {
116
+ latency.appendData({ t: clock(), p50: sample() }, { max: 60 }); // only the new point is sent
117
+ latency.setHeadline({ value: `${p50} ms` });
118
+ });
119
+ ui.timer(5, async () => byRegion.setData(await fetchRegions())); // bars tween to the new values
120
+ ```
121
+
122
+ - **`appendData(rows, { max })`** sends only the new rows. The client appends them and drops the oldest past `max`, so a sliding window costs one point per tick.
123
+ - **`setData(rows)`** replaces the data. When the row count and first x value are unchanged, the marks tween to their new values.
124
+ - **`upsertData(row, { key, max })`** replaces the row with the same x value (or `nameKey` for pies), or appends it. Use it for a bucket that keeps growing, such as the current minute.
125
+ - **`setSeries`, `setHeadline`, `setTitle`, `setDescription`, `setLoading`** patch one prop each. Anything else goes through `update({ … })`.
126
+
127
+ KPI stat grids work the same way: `ui.stat(items)` returns a `StatElement`. `updateItem(titleOrIndex, patch)` changes one card, `appendSparkline(titleOrIndex, points, { max })` extends that card's sparkline, and `setItems(items)` replaces them all.
128
+
129
+ Appends and sliding windows draw without animation, so lines don't slide sideways or replay their entrance. Pass `animate: false` to turn chart animation off entirely. The interactive period tabs keep the selected period while data streams in.
130
+
106
131
  ## Anti-patterns
107
132
 
108
133
  - **Rebuilding a whole section when one value changes.** Wrapping a whole page in `ui.refreshable` and calling `.refresh()` every second to update a clock. Keep a reference to the clock label and call `setText`.
109
134
  - **Unkeyed lists that get sorted or filtered.** Without `.key()`, position is the identity. After a sort, the element in slot 3 is reused for whatever item is now third, so focus and typed text stay in the slot instead of following the row.
110
- - **Creating a new table instead of `setRows`.** A `ui.dataTable(...)` created inside a refreshable on every tick remounts the table and loses sort, scroll, selection, and search. Create the table once and call `setRows`.
135
+ - **Creating a new table instead of `setRows`.** A `ui.dataTable(...)` rebuilt inside a refreshable keeps its id and stays interactive, but each rebuild starts the table's server-side state over, so sort, search, page and selection reset. Create the table once and call `setRows`.
111
136
  - **Tables without `keyField`.** Rows are stamped `__rowId` by their index, so inserting or re-sorting the source shifts every row's identity. Set `keyField` to your primary key.
112
137
  - **Toggling a constructor option by rebuilding.** Recreating a table with `zebra: on` inside an `auto` block. Use the setters instead: `setZebra`, `setDensity`, `setHighlightChanges`.
113
138
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@close-by/clay",
3
- "version": "0.2.19",
3
+ "version": "0.2.20",
4
4
  "description": "NiceGUI-like ui.* facade for Clay apps",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -37,10 +37,10 @@
37
37
  "access": "public"
38
38
  },
39
39
  "dependencies": {
40
- "@close-by/clay-compiler": "0.2.19",
41
- "@close-by/clay-core": "0.2.19",
42
- "@close-by/clay-components": "0.2.19",
43
- "@close-by/clay-server": "0.2.19",
40
+ "@close-by/clay-compiler": "0.2.20",
41
+ "@close-by/clay-core": "0.2.20",
42
+ "@close-by/clay-components": "0.2.20",
43
+ "@close-by/clay-server": "0.2.20",
44
44
  "@tailwindcss/cli": "^4.3.3",
45
45
  "tailwindcss": "^4.3.3"
46
46
  }
package/src/index.ts CHANGED
@@ -203,6 +203,8 @@ import {
203
203
  type CheckboxGroupProps,
204
204
  type CheckboxGroupOption,
205
205
  type StatItem,
206
+ type StatItemRef,
207
+ type StatElement,
206
208
  type DataTableProps,
207
209
  type DataTableAction,
208
210
  type DataTableView,
@@ -235,6 +237,7 @@ import {
235
237
  type ComposedChartProps,
236
238
  type ComposedChartSeries,
237
239
  type ComposedSeriesType,
240
+ type ChartElement,
238
241
  type CartesianChartProps,
239
242
  type ChartSeries,
240
243
  type ChartHeadline,
@@ -423,7 +426,6 @@ import {
423
426
  DialogElement,
424
427
  DialogStackElement,
425
428
  StepperElement,
426
- NavigationMenuElement,
427
429
  AlertDialogElement,
428
430
  DropdownMenuElement,
429
431
  ContextMenuElement,
@@ -527,6 +529,9 @@ export {
527
529
  } from './tailwind';
528
530
 
529
531
 
532
+ export { ChartElement, StatElement } from '@close-by/clay-components';
533
+ export type { StatItemRef } from '@close-by/clay-components';
534
+
530
535
  export type {
531
536
  GridProps,
532
537
  LayoutProps,
@@ -1223,7 +1228,7 @@ export function flow(a: any, b?: any): FlowElement {
1223
1228
  }
1224
1229
 
1225
1230
  /** Stat strip (label/value items). Prefer `ui.stat`. */
1226
- export function stat(items: StatItem[], props?: { className?: string }): Element {
1231
+ export function stat(items: StatItem[], props?: { className?: string }): StatElement {
1227
1232
  return statFactory(items, props);
1228
1233
  }
1229
1234
 
@@ -1247,47 +1252,47 @@ export const chart = chartNamespace;
1247
1252
  export const ai = aiNamespace;
1248
1253
 
1249
1254
  /** Area chart. Prefer `ui.areaChart`. See {@link AreaChartProps}. */
1250
- export function areaChart(props: AreaChartProps): Element {
1255
+ export function areaChart(props: AreaChartProps): ChartElement {
1251
1256
  return areaChartFactory(props);
1252
1257
  }
1253
1258
 
1254
1259
  /** Bar chart. Prefer `ui.barChart`. See {@link BarChartProps}. */
1255
- export function barChart(props: BarChartProps): Element {
1260
+ export function barChart(props: BarChartProps): ChartElement {
1256
1261
  return barChartFactory(props);
1257
1262
  }
1258
1263
 
1259
1264
  /** Line chart. Prefer `ui.lineChart`. See {@link LineChartProps}. */
1260
- export function lineChart(props: LineChartProps): Element {
1265
+ export function lineChart(props: LineChartProps): ChartElement {
1261
1266
  return lineChartFactory(props);
1262
1267
  }
1263
1268
 
1264
1269
  /** Pie / donut chart. Prefer `ui.pieChart`. See {@link PieChartProps}. */
1265
- export function pieChart(props: PieChartProps): Element {
1270
+ export function pieChart(props: PieChartProps): ChartElement {
1266
1271
  return pieChartFactory(props);
1267
1272
  }
1268
1273
 
1269
1274
  /** Radar chart. Prefer `ui.radarChart`. See {@link RadarChartProps}. */
1270
- export function radarChart(props: RadarChartProps): Element {
1275
+ export function radarChart(props: RadarChartProps): ChartElement {
1271
1276
  return radarChartFactory(props);
1272
1277
  }
1273
1278
 
1274
1279
  /** Radial / gauge chart. Prefer `ui.radialChart`. See {@link RadialChartProps}. */
1275
- export function radialChart(props: RadialChartProps): Element {
1280
+ export function radialChart(props: RadialChartProps): ChartElement {
1276
1281
  return radialChartFactory(props);
1277
1282
  }
1278
1283
 
1279
1284
  /** Scatter chart. Prefer `ui.scatterChart`. See {@link ScatterChartProps}. */
1280
- export function scatterChart(props: ScatterChartProps): Element {
1285
+ export function scatterChart(props: ScatterChartProps): ChartElement {
1281
1286
  return scatterChartFactory(props);
1282
1287
  }
1283
1288
 
1284
1289
  /** Composed multi-geometry chart. Prefer `ui.composedChart`. See {@link ComposedChartProps}. */
1285
- export function composedChart(props: ComposedChartProps): Element {
1290
+ export function composedChart(props: ComposedChartProps): ChartElement {
1286
1291
  return composedChartFactory(props);
1287
1292
  }
1288
1293
 
1289
1294
  /** KPI card with inline sparkline. Prefer `ui.sparkline`. See {@link SparklineProps}. */
1290
- export function sparkline(props: SparklineProps): Element {
1295
+ export function sparkline(props: SparklineProps): ChartElement {
1291
1296
  return sparklineFactory(props);
1292
1297
  }
1293
1298