@contractspec/lib.presentation-runtime-core 3.7.6 → 3.8.3

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 (39) hide show
  1. package/README.md +60 -54
  2. package/dist/browser/index.js +473 -1
  3. package/dist/browser/table.js +21 -0
  4. package/dist/browser/visualization.echarts.js +148 -0
  5. package/dist/browser/visualization.js +307 -0
  6. package/dist/browser/visualization.model.builders.js +290 -0
  7. package/dist/browser/visualization.model.helpers.js +199 -0
  8. package/dist/browser/visualization.model.js +306 -0
  9. package/dist/browser/visualization.types.js +0 -0
  10. package/dist/browser/visualization.utils.js +65 -0
  11. package/dist/index.d.ts +5 -0
  12. package/dist/index.js +473 -1
  13. package/dist/node/index.js +473 -1
  14. package/dist/node/table.js +21 -0
  15. package/dist/node/visualization.echarts.js +148 -0
  16. package/dist/node/visualization.js +307 -0
  17. package/dist/node/visualization.model.builders.js +290 -0
  18. package/dist/node/visualization.model.helpers.js +199 -0
  19. package/dist/node/visualization.model.js +306 -0
  20. package/dist/node/visualization.types.js +0 -0
  21. package/dist/node/visualization.utils.js +65 -0
  22. package/dist/table.d.ts +99 -0
  23. package/dist/table.js +22 -0
  24. package/dist/visualization.d.ts +3 -0
  25. package/dist/visualization.echarts.d.ts +3 -0
  26. package/dist/visualization.echarts.js +149 -0
  27. package/dist/visualization.js +308 -0
  28. package/dist/visualization.model.builders.d.ts +20 -0
  29. package/dist/visualization.model.builders.js +291 -0
  30. package/dist/visualization.model.d.ts +3 -0
  31. package/dist/visualization.model.helpers.d.ts +60 -0
  32. package/dist/visualization.model.helpers.js +200 -0
  33. package/dist/visualization.model.js +307 -0
  34. package/dist/visualization.model.test.d.ts +1 -0
  35. package/dist/visualization.types.d.ts +74 -0
  36. package/dist/visualization.types.js +1 -0
  37. package/dist/visualization.utils.d.ts +5 -0
  38. package/dist/visualization.utils.js +66 -0
  39. package/package.json +121 -5
package/README.md CHANGED
@@ -1,67 +1,73 @@
1
1
  # @contractspec/lib.presentation-runtime-core
2
2
 
3
- Website: https://contractspec.io/
3
+ Website: https://contractspec.io
4
4
 
5
+ **Core presentation runtime for contract-driven UIs.**
5
6
 
6
- Core logic for ContractSpec presentation runtimes.
7
+ ## What It Provides
7
8
 
8
- ## Purpose
9
-
10
- To provide the shared, framework-agnostic state management and logic for executing workflows and rendering presentations defined in ContractSpec.
9
+ - **Layer**: lib.
10
+ - **Consumers**: presentation-runtime-react, presentation-runtime-react-native.
11
+ - Related ContractSpec packages include `@contractspec/lib.contracts-spec`, `@contractspec/tool.bun`, `@contractspec/tool.typescript`.
12
+ - Related ContractSpec packages include `@contractspec/lib.contracts-spec`, `@contractspec/tool.bun`, `@contractspec/tool.typescript`.
11
13
 
12
14
  ## Installation
13
15
 
14
- ```bash
15
- npm install @contractspec/lib.presentation-runtime-core
16
- # or
17
- bun add @contractspec/lib.presentation-runtime-core
18
- ```
19
-
20
- ## Key Concepts
21
-
22
- - **Workflow State**: Manages the progression of steps in a `WorkflowSpec`.
23
- - **Step Navigation**: Logic for next/previous/submit actions.
24
- - **Validation**: Integration with Zod schemas for step validation.
16
+ `npm install @contractspec/lib.presentation-runtime-core`
25
17
 
26
- ## Exports
18
+ or
27
19
 
28
- - Types and state machines for workflows.
29
- - Validation helpers.
20
+ `bun add @contractspec/lib.presentation-runtime-core`
30
21
 
31
22
  ## Usage
32
23
 
33
- Typically used internally by `@contractspec/lib.presentation-runtime-react` or `@contractspec/lib.presentation-runtime-react-native`.
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
-
54
-
55
-
56
-
57
-
58
-
59
-
60
-
61
-
62
-
63
-
64
-
65
-
66
-
67
-
24
+ Import the root entrypoint from `@contractspec/lib.presentation-runtime-core`, or choose a documented subpath when you only need one part of the package surface.
25
+
26
+ ## Architecture
27
+
28
+ - `src/index.ts` is the root public barrel and package entrypoint.
29
+ - `src/metro.cjs` is part of the package's public or composition surface.
30
+ - `src/next.mjs` is part of the package's public or composition surface.
31
+ - `src/table.ts` is part of the package's public or composition surface.
32
+ - `src/visualization.echarts.ts` is part of the package's public or composition surface.
33
+ - `src/visualization.model.builders.ts` is part of the package's public or composition surface.
34
+ - `src/visualization.model.helpers.ts` is part of the package's public or composition surface.
35
+
36
+ ## Public Entry Points
37
+
38
+ - Export `.` resolves through `./src/index.ts`.
39
+ - Export `./table` resolves through `./src/table.ts`.
40
+ - Export `./visualization` resolves through `./src/visualization.ts`.
41
+ - Export `./visualization.echarts` resolves through `./src/visualization.echarts.ts`.
42
+ - Export `./visualization.model` resolves through `./src/visualization.model.ts`.
43
+ - Export `./visualization.model.builders` resolves through `./src/visualization.model.builders.ts`.
44
+ - Export `./visualization.model.helpers` resolves through `./src/visualization.model.helpers.ts`.
45
+ - Export `./visualization.types` resolves through `./src/visualization.types.ts`.
46
+ - Export `./visualization.utils` resolves through `./src/visualization.utils.ts`.
47
+
48
+ ## Local Commands
49
+
50
+ - `bun run dev` — contractspec-bun-build dev
51
+ - `bun run build` — bun run prebuild && bun run build:bundle && bun run build:types
52
+ - `bun run lint` — bun run lint:fix
53
+ - `bun run lint:check` — biome check .
54
+ - `bun run lint:fix` — biome check --write --unsafe --only=nursery/useSortedClasses . && biome check --write .
55
+ - `bun run typecheck` — tsc --noEmit -p tsconfig.json
56
+ - `bun run publish:pkg` — bun publish --tolerate-republish --ignore-scripts --verbose
57
+ - `bun run publish:pkg:canary` — bun publish:pkg --tag canary
58
+ - `bun run clean` — rimraf dist .turbo
59
+ - `bun run build:bundle` — contractspec-bun-build transpile
60
+ - `bun run build:types` — contractspec-bun-build types
61
+ - `bun run prebuild` — contractspec-bun-build prebuild
62
+
63
+ ## Recent Updates
64
+
65
+ - Replace eslint+prettier by biomejs to optimize speed.
66
+ - Add data visualization capabilities.
67
+ - Add table capabilities.
68
+
69
+ ## Notes
70
+
71
+ - Core runtime interface is consumed by all presentation runtimes — changes here affect both web and mobile.
72
+ - Must remain platform-agnostic; no React or React Native imports allowed.
73
+ - API surface changes require coordinated updates in both downstream runtimes.
@@ -1,3 +1,471 @@
1
+ // src/table.ts
2
+ function createEmptyTableState() {
3
+ return {
4
+ sorting: [],
5
+ pagination: {
6
+ pageIndex: 0,
7
+ pageSize: 25
8
+ },
9
+ columnVisibility: {},
10
+ columnSizing: {},
11
+ columnPinning: {
12
+ left: [],
13
+ right: []
14
+ },
15
+ expanded: {},
16
+ rowSelection: {}
17
+ };
18
+ }
19
+
20
+ // src/visualization.utils.ts
21
+ function formatVisualizationValue(value, format) {
22
+ if (value == null)
23
+ return "—";
24
+ if (format === "currency" && typeof value === "number") {
25
+ return new Intl.NumberFormat(undefined, {
26
+ style: "currency",
27
+ currency: "USD"
28
+ }).format(value);
29
+ }
30
+ if (format === "percentage" && typeof value === "number") {
31
+ return `${(value * 100).toFixed(1)}%`;
32
+ }
33
+ if ((format === "date" || format === "dateTime") && value) {
34
+ const date = value instanceof Date ? value : new Date(String(value));
35
+ return Number.isNaN(date.getTime()) ? String(value) : new Intl.DateTimeFormat(undefined, {
36
+ dateStyle: "medium",
37
+ timeStyle: format === "dateTime" ? "short" : undefined
38
+ }).format(date);
39
+ }
40
+ return String(value);
41
+ }
42
+ function resolveVisualizationRows(data, resultPath) {
43
+ const value = resultPath ? getAtPath(data, resultPath) : data;
44
+ const candidate = value ?? data;
45
+ if (Array.isArray(candidate))
46
+ return candidate.map(asRow);
47
+ if (Array.isArray(getAtPath(candidate, "items"))) {
48
+ return getAtPath(candidate, "items").map(asRow);
49
+ }
50
+ if (Array.isArray(getAtPath(candidate, "rows"))) {
51
+ return getAtPath(candidate, "rows").map(asRow);
52
+ }
53
+ if (Array.isArray(getAtPath(candidate, "data"))) {
54
+ return getAtPath(candidate, "data").map(asRow);
55
+ }
56
+ return candidate && typeof candidate === "object" ? [asRow(candidate)] : [];
57
+ }
58
+ function getAtPath(source, path) {
59
+ if (!path || source == null)
60
+ return source;
61
+ return path.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean).reduce((current, segment) => {
62
+ if (current == null || typeof current !== "object")
63
+ return;
64
+ return current[segment];
65
+ }, source);
66
+ }
67
+ function toNumber(value) {
68
+ if (typeof value === "number" && Number.isFinite(value))
69
+ return value;
70
+ if (typeof value === "string" && value.trim()) {
71
+ const parsed = Number(value);
72
+ return Number.isFinite(parsed) ? parsed : null;
73
+ }
74
+ return null;
75
+ }
76
+ function asRow(value) {
77
+ return value && typeof value === "object" ? value : { value };
78
+ }
79
+
80
+ // src/visualization.model.helpers.ts
81
+ function createVisualizationMaps(spec) {
82
+ return {
83
+ dimensions: new Map((spec.visualization.dimensions ?? []).map((dimension) => [
84
+ dimension.key,
85
+ dimension
86
+ ])),
87
+ measures: new Map((spec.visualization.measures ?? []).map((measure) => [
88
+ measure.key,
89
+ measure
90
+ ]))
91
+ };
92
+ }
93
+ function createVisualizationBaseModel(spec, rows, maps) {
94
+ const config = spec.visualization;
95
+ return {
96
+ kind: config.kind,
97
+ title: config.title ?? spec.meta.title,
98
+ description: config.description ?? spec.meta.description,
99
+ summary: `${spec.meta.title ?? spec.meta.key}: ${rows.length} row${rows.length === 1 ? "" : "s"}`,
100
+ warnings: rows.length === 0 ? ["No visualization rows available."] : [],
101
+ palette: config.palette,
102
+ legend: config.legend,
103
+ tooltip: config.tooltip,
104
+ drilldown: config.drilldown,
105
+ thresholds: config.thresholds ?? [],
106
+ annotations: (config.annotations ?? []).map((annotation) => resolveAnnotation(annotation, rows[0] ?? {})),
107
+ table: createTable(config.table?.caption, config, rows)
108
+ };
109
+ }
110
+ function createMeasureSeries(measureKey, measures, rows, dimension, config) {
111
+ const measure = measures.get(measureKey);
112
+ if (!measure)
113
+ return null;
114
+ return {
115
+ key: config?.key ?? measure.key,
116
+ label: config?.label ?? measure.label,
117
+ type: config?.type,
118
+ color: config?.color ?? measure.color,
119
+ stack: config?.stack,
120
+ smooth: config?.smooth,
121
+ points: rows.map((row, index) => ({
122
+ id: `${measure.key}-${index}`,
123
+ raw: row,
124
+ x: dimension ? readDimensionValue(row, dimension) : index,
125
+ y: numericValue(row, measure),
126
+ value: numericValue(row, measure)
127
+ }))
128
+ };
129
+ }
130
+ function defaultSeries(keys) {
131
+ return keys.map((key) => ({ key, label: key, measure: key }));
132
+ }
133
+ function createAxis(dimension) {
134
+ if (!dimension)
135
+ return;
136
+ return {
137
+ key: dimension.key,
138
+ label: dimension.label,
139
+ type: dimension.type === "time" ? "time" : dimension.type === "number" ? "number" : "category",
140
+ format: dimension.format
141
+ };
142
+ }
143
+ function createValueAxis(key, measures) {
144
+ const measure = key ? measures.get(key) : undefined;
145
+ if (!measure)
146
+ return;
147
+ return {
148
+ key: measure.key,
149
+ label: measure.label,
150
+ type: "number",
151
+ format: measure.format
152
+ };
153
+ }
154
+ function readValue(row, measure) {
155
+ return measure ? getAtPath(row, measure.dataPath) : undefined;
156
+ }
157
+ function readDimensionValue(row, dimension) {
158
+ return dimension ? getAtPath(row, dimension.dataPath) : undefined;
159
+ }
160
+ function numericValue(row, measure) {
161
+ return toNumber(readValue(row, measure));
162
+ }
163
+ function numericPathValue(row, dimension) {
164
+ return toNumber(readDimensionValue(row, dimension));
165
+ }
166
+ function stringValue(row, dimension) {
167
+ const value = readDimensionValue(row, dimension);
168
+ return value == null ? undefined : String(value);
169
+ }
170
+ function createTable(caption, config, rows) {
171
+ const dimensions = config.dimensions ?? [];
172
+ const measures = config.measures ?? [];
173
+ return {
174
+ caption,
175
+ columns: [
176
+ ...dimensions.map((dimension) => ({
177
+ key: dimension.key,
178
+ label: dimension.label
179
+ })),
180
+ ...measures.map((measure) => ({
181
+ key: measure.key,
182
+ label: measure.label
183
+ }))
184
+ ],
185
+ rows: rows.map((row) => ({
186
+ ...Object.fromEntries(dimensions.map((dimension) => [
187
+ dimension.key,
188
+ readDimensionValue(row, dimension)
189
+ ])),
190
+ ...Object.fromEntries(measures.map((measure) => [measure.key, readValue(row, measure)]))
191
+ }))
192
+ };
193
+ }
194
+ function resolveAnnotation(annotation, row) {
195
+ return {
196
+ key: annotation.key,
197
+ label: annotation.label,
198
+ kind: annotation.kind,
199
+ color: annotation.color,
200
+ x: annotation.xDataPath ? getAtPath(row, annotation.xDataPath) : undefined,
201
+ y: annotation.yDataPath ? toNumber(getAtPath(row, annotation.yDataPath)) : undefined,
202
+ start: annotation.startDataPath ? getAtPath(row, annotation.startDataPath) : undefined,
203
+ end: annotation.endDataPath ? getAtPath(row, annotation.endDataPath) : undefined
204
+ };
205
+ }
206
+
207
+ // src/visualization.model.builders.ts
208
+ function buildMetricModel(base, config, rows, maps) {
209
+ const measure = maps.measures.get(config.measure);
210
+ const comparison = config.comparisonMeasure ? maps.measures.get(config.comparisonMeasure) : undefined;
211
+ const currentRow = rows.at(-1) ?? {};
212
+ const sparkline = config.sparkline ? createMeasureSeries(config.sparkline.measure ?? config.measure, maps.measures, rows, maps.dimensions.get(config.sparkline.dimension)) : null;
213
+ return {
214
+ ...base,
215
+ series: sparkline ? [sparkline] : [],
216
+ metric: {
217
+ label: measure?.label ?? config.measure,
218
+ value: readValue(currentRow, measure),
219
+ comparisonValue: comparison ? readValue(currentRow, comparison) : undefined,
220
+ format: measure?.format
221
+ }
222
+ };
223
+ }
224
+ function buildCartesianModel(base, config, rows, maps) {
225
+ const series = (config.series ?? defaultSeries(config.yMeasures ?? [])).map((seriesItem) => createMeasureSeries(seriesItem.measure, maps.measures, rows, maps.dimensions.get(config.xDimension), seriesItem)).filter((item) => Boolean(item));
226
+ return {
227
+ ...base,
228
+ series,
229
+ xAxis: createAxis(maps.dimensions.get(config.xDimension)),
230
+ yAxis: createValueAxis(config.yMeasures?.[0], maps.measures)
231
+ };
232
+ }
233
+ function buildDistributionModel(base, config, rows, maps) {
234
+ const nameDimension = maps.dimensions.get(config.nameDimension);
235
+ const valueMeasure = maps.measures.get(config.valueMeasure);
236
+ return {
237
+ ...base,
238
+ series: [
239
+ {
240
+ key: valueMeasure?.key ?? config.valueMeasure,
241
+ label: valueMeasure?.label ?? config.valueMeasure,
242
+ type: config.kind,
243
+ points: rows.map((row, index) => ({
244
+ id: `${config.kind}-${index}`,
245
+ raw: row,
246
+ name: stringValue(row, nameDimension),
247
+ value: numericValue(row, valueMeasure)
248
+ }))
249
+ }
250
+ ]
251
+ };
252
+ }
253
+ function buildHeatmapModel(base, config, rows, maps) {
254
+ const xDimension = maps.dimensions.get(config.xDimension);
255
+ const yDimension = maps.dimensions.get(config.yDimension);
256
+ const valueMeasure = maps.measures.get(config.valueMeasure);
257
+ return {
258
+ ...base,
259
+ series: [
260
+ {
261
+ key: config.valueMeasure,
262
+ label: valueMeasure?.label ?? config.valueMeasure,
263
+ type: "heatmap",
264
+ points: rows.map((row, index) => ({
265
+ id: `heatmap-${index}`,
266
+ raw: row,
267
+ name: stringValue(row, yDimension),
268
+ x: readDimensionValue(row, xDimension),
269
+ y: numericValue(row, valueMeasure),
270
+ value: numericValue(row, valueMeasure)
271
+ }))
272
+ }
273
+ ],
274
+ xAxis: createAxis(xDimension),
275
+ yAxis: createAxis(yDimension)
276
+ };
277
+ }
278
+ function buildGeoModel(base, config, rows, maps) {
279
+ return {
280
+ ...base,
281
+ series: [
282
+ {
283
+ key: "geo",
284
+ label: "Geo",
285
+ type: config.variant ?? "scatter",
286
+ points: rows.map((row, index) => ({
287
+ id: `geo-${index}`,
288
+ raw: row,
289
+ name: config.nameDimension ? stringValue(row, maps.dimensions.get(config.nameDimension)) : undefined,
290
+ value: config.valueMeasure ? numericValue(row, maps.measures.get(config.valueMeasure)) : undefined,
291
+ latitude: numericPathValue(row, maps.dimensions.get(config.latitudeDimension ?? "")),
292
+ longitude: numericPathValue(row, maps.dimensions.get(config.longitudeDimension ?? ""))
293
+ }))
294
+ }
295
+ ],
296
+ geo: {
297
+ mode: config.mode ?? "chart",
298
+ variant: config.variant ?? "scatter",
299
+ geoJson: config.geoJson
300
+ }
301
+ };
302
+ }
303
+
304
+ // src/visualization.model.ts
305
+ function createVisualizationModel(spec, data) {
306
+ const rows = resolveVisualizationRows(data, spec.source.resultPath);
307
+ const maps = createVisualizationMaps(spec);
308
+ const base = createVisualizationBaseModel(spec, rows, maps);
309
+ switch (spec.visualization.kind) {
310
+ case "metric":
311
+ return buildMetricModel(base, spec.visualization, rows, maps);
312
+ case "cartesian":
313
+ return buildCartesianModel(base, spec.visualization, rows, maps);
314
+ case "pie":
315
+ case "funnel":
316
+ return buildDistributionModel(base, spec.visualization, rows, maps);
317
+ case "heatmap":
318
+ return buildHeatmapModel(base, spec.visualization, rows, maps);
319
+ case "geo":
320
+ return buildGeoModel(base, spec.visualization, rows, maps);
321
+ }
322
+ }
323
+ // src/visualization.echarts.ts
324
+ function buildVisualizationEChartsOption(model) {
325
+ const thresholdLines = model.thresholds.map((threshold) => ({
326
+ yAxis: threshold.value,
327
+ name: threshold.label,
328
+ lineStyle: {
329
+ color: threshold.color ?? "#ef4444",
330
+ type: "dashed"
331
+ }
332
+ }));
333
+ const annotationLines = model.annotations.filter((annotation) => annotation.kind === "line" && annotation.y != null).map((annotation) => toMarkLine(annotation));
334
+ switch (model.kind) {
335
+ case "cartesian":
336
+ const cartesianSeries = model.series.map((series) => ({
337
+ name: series.label,
338
+ type: resolveCartesianSeriesType(series.type),
339
+ smooth: series.smooth,
340
+ stack: series.stack,
341
+ areaStyle: series.type === "area" ? {} : undefined,
342
+ itemStyle: series.color ? { color: series.color } : undefined,
343
+ lineStyle: series.color ? { color: series.color } : undefined,
344
+ data: series.points.filter((point) => point.y != null).map((point) => [point.x, point.y]),
345
+ markLine: thresholdLines.length || annotationLines.length ? { data: [...thresholdLines, ...annotationLines] } : undefined
346
+ }));
347
+ return {
348
+ color: model.palette,
349
+ tooltip: model.tooltip === false ? undefined : { trigger: "axis" },
350
+ legend: { show: model.legend ?? model.series.length > 1 },
351
+ xAxis: { type: model.xAxis?.type === "time" ? "time" : "category" },
352
+ yAxis: { type: "value", name: model.yAxis?.label },
353
+ series: cartesianSeries
354
+ };
355
+ case "pie":
356
+ const pieSeries = [
357
+ {
358
+ type: "pie",
359
+ radius: ["0%", "70%"],
360
+ data: model.series[0]?.points.filter((point) => point.value != null).map((point) => ({
361
+ name: point.name ?? "",
362
+ value: point.value
363
+ })) ?? []
364
+ }
365
+ ];
366
+ return {
367
+ color: model.palette,
368
+ tooltip: model.tooltip === false ? undefined : { trigger: "item" },
369
+ series: pieSeries
370
+ };
371
+ case "heatmap":
372
+ const heatmapSeries = [
373
+ {
374
+ type: "heatmap",
375
+ data: model.series[0]?.points.filter((point) => point.value != null && point.name != null).map((point) => [point.x, point.name, point.value]) ?? []
376
+ }
377
+ ];
378
+ return {
379
+ color: model.palette,
380
+ tooltip: model.tooltip === false ? undefined : { position: "top" },
381
+ xAxis: {
382
+ type: "category",
383
+ data: uniqueValues(model.series[0]?.points.map((point) => point.x))
384
+ },
385
+ yAxis: {
386
+ type: "category",
387
+ data: uniqueValues(model.series[0]?.points.map((point) => point.name))
388
+ },
389
+ visualMap: {
390
+ min: 0,
391
+ max: maxValue(model.series[0]?.points.map((point) => point.value)),
392
+ calculable: true,
393
+ orient: "horizontal",
394
+ left: "center",
395
+ bottom: 0
396
+ },
397
+ series: heatmapSeries
398
+ };
399
+ case "funnel":
400
+ const funnelSeries = [
401
+ {
402
+ type: "funnel",
403
+ data: model.series[0]?.points.filter((point) => point.value != null).map((point) => ({
404
+ name: point.name ?? "",
405
+ value: point.value
406
+ })) ?? []
407
+ }
408
+ ];
409
+ return {
410
+ color: model.palette,
411
+ tooltip: model.tooltip === false ? undefined : { trigger: "item" },
412
+ series: funnelSeries
413
+ };
414
+ case "geo":
415
+ if (!model.geo || model.geo.mode === "slippy-map" || !model.geo.geoJson) {
416
+ return {};
417
+ }
418
+ const geoSeries = [
419
+ {
420
+ type: model.geo.variant === "heatmap" ? "heatmap" : "scatter",
421
+ coordinateSystem: "geo",
422
+ data: model.series[0]?.points.filter((point) => point.longitude != null && point.latitude != null && point.value != null).map((point) => ({
423
+ name: point.name ?? "",
424
+ value: [
425
+ point.longitude,
426
+ point.latitude,
427
+ point.value
428
+ ]
429
+ })) ?? []
430
+ }
431
+ ];
432
+ return {
433
+ color: model.palette,
434
+ tooltip: model.tooltip === false ? undefined : { trigger: "item" },
435
+ geo: {
436
+ map: "contractspec-visualization-geo",
437
+ roam: true
438
+ },
439
+ series: geoSeries
440
+ };
441
+ default:
442
+ return {};
443
+ }
444
+ }
445
+ function uniqueValues(values) {
446
+ return Array.from(new Set((values ?? []).filter((value) => value != null).map(String)));
447
+ }
448
+ function maxValue(values) {
449
+ return Math.max(...(values ?? []).map((value) => value ?? 0), 1);
450
+ }
451
+ function toMarkLine(annotation) {
452
+ return {
453
+ yAxis: annotation.y,
454
+ name: annotation.label,
455
+ lineStyle: {
456
+ color: annotation.color ?? "#2563eb",
457
+ type: "solid"
458
+ }
459
+ };
460
+ }
461
+ function resolveCartesianSeriesType(type) {
462
+ if (type === "bar")
463
+ return "bar";
464
+ if (type === "scatter")
465
+ return "scatter";
466
+ return "line";
467
+ }
468
+
1
469
  // src/index.ts
2
470
  function withPresentationNextAliases(config, opts = {}) {
3
471
  const uiWeb = opts.uiKitWeb ?? "@contractspec/lib.ui-kit-web";
@@ -51,5 +519,9 @@ function withPresentationMetroAliases(config, opts = {}) {
51
519
  }
52
520
  export {
53
521
  withPresentationNextAliases,
54
- withPresentationMetroAliases
522
+ withPresentationMetroAliases,
523
+ formatVisualizationValue,
524
+ createVisualizationModel,
525
+ createEmptyTableState,
526
+ buildVisualizationEChartsOption
55
527
  };
@@ -0,0 +1,21 @@
1
+ // src/table.ts
2
+ function createEmptyTableState() {
3
+ return {
4
+ sorting: [],
5
+ pagination: {
6
+ pageIndex: 0,
7
+ pageSize: 25
8
+ },
9
+ columnVisibility: {},
10
+ columnSizing: {},
11
+ columnPinning: {
12
+ left: [],
13
+ right: []
14
+ },
15
+ expanded: {},
16
+ rowSelection: {}
17
+ };
18
+ }
19
+ export {
20
+ createEmptyTableState
21
+ };