@lionad/cx-comps-tanstack-charts 0.1.0-alpha.11 → 0.1.0-alpha.13

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/dist/index.mjs CHANGED
@@ -26,6 +26,7 @@ import { sankeyDiagram } from "@tanstack/charts/network/sankey";
26
26
  import { geoAlbersUsa, geoEqualEarth, geoIdentity, geoMercator, geoNaturalEarth1, geoOrthographic } from "d3-geo";
27
27
  import { decorative } from "@tanstack/charts/mark/decorative";
28
28
  import { tooltip } from "@tanstack/charts/tooltip";
29
+ import { viewGrid } from "@tanstack/charts/view";
29
30
  import { compileTrigger, createTriggerRegistry } from "@lionad/cx-stream";
30
31
  //#region src/shared/chart-skeleton.vue
31
32
  const _sfc_main$6 = /*@__PURE__*/ defineComponent({
@@ -765,11 +766,16 @@ function translatePolarGuide(spec) {
765
766
  return factory(options);
766
767
  }
767
768
  /** polar axis 声明式(复用 scale 枚举;wrap 仅 angle 轴)→ PolarAngleOptions/PolarRadiusOptions */
768
- function translatePolarAxis(spec) {
769
+ function translatePolarAxis(spec, { allowRange = false } = {}) {
769
770
  if (!spec) return void 0;
770
771
  const axis = {};
771
772
  if (spec.scale) axis.scale = translateScale(spec.scale);
772
773
  if (spec.nice !== void 0) axis.nice = spec.nice;
774
+ if (allowRange && spec.range !== void 0) {
775
+ const [r0, r1] = spec.range;
776
+ if (!Number.isFinite(r0) || !Number.isFinite(r1) || r0 < 0 || r1 < r0) throw new Error(`translatePolar: radiusAxis.range 须为有限非负且递增的比例对,收到 [${r0}, ${r1}]`);
777
+ axis.range = [ratioToLength(r0), ratioToLength(r1)];
778
+ }
773
779
  return axis;
774
780
  }
775
781
  /**
@@ -785,7 +791,7 @@ function translatePolar(spec, datasets, extraMarks = []) {
785
791
  if (spec.inset !== void 0) options.inset = spec.inset;
786
792
  if (spec.id !== void 0) options.id = spec.id;
787
793
  const angle = translatePolarAxis(spec.angleAxis);
788
- const radius = translatePolarAxis(spec.radiusAxis);
794
+ const radius = translatePolarAxis(spec.radiusAxis, { allowRange: true });
789
795
  const hasRadialBarRadius = (spec.marks ?? []).some((m) => m.type === "radialBarRadius");
790
796
  const hasRadialBarAngle = (spec.marks ?? []).some((m) => m.type === "radialBarAngle");
791
797
  options.angle = angle ?? { scale: () => hasRadialBarRadius ? scaleBand() : scalePoint() };
@@ -1122,6 +1128,31 @@ function translateCompositeMark(spec, datasets, translateChild) {
1122
1128
  }
1123
1129
  }
1124
1130
  //#endregion
1131
+ //#region src/shared/translate/view.ts
1132
+ function translateViewGrid(spec, datasets, hooks) {
1133
+ const grid = spec.views;
1134
+ const views = grid.items.map((item) => {
1135
+ if (item.chart.views !== void 0) throw new Error(`translateViewGrid: 子视图 "${item.id}" 不支持嵌套 views`);
1136
+ return {
1137
+ id: item.id,
1138
+ row: item.row,
1139
+ column: item.column,
1140
+ ...item.share ? { share: item.share } : {},
1141
+ ...item.align ? { align: item.align } : {},
1142
+ chart: hooks.buildChild(item.chart, datasets)
1143
+ };
1144
+ });
1145
+ const composed = viewGrid({
1146
+ rows: grid.rows,
1147
+ columns: grid.columns,
1148
+ ...grid.gap !== void 0 ? { gap: grid.gap } : {},
1149
+ ...grid.rowGap !== void 0 ? { rowGap: grid.rowGap } : {},
1150
+ ...grid.columnGap !== void 0 ? { columnGap: grid.columnGap } : {},
1151
+ views
1152
+ });
1153
+ return hooks.applyHost(composed, spec);
1154
+ }
1155
+ //#endregion
1125
1156
  //#region src/shared/translate/definition.ts
1126
1157
  /**
1127
1158
  * definition 组装:声明式 spec → DomChartDefinition(可直接喂给 <Chart>)。
@@ -1132,6 +1163,11 @@ function translateCompositeMark(spec, datasets, translateChild) {
1132
1163
  * 置 null 显式无轴(库对 polar mark 的 scale 类型为 never,缺省 axis 注入会抛错)
1133
1164
  * - tree/forceGraph 展开为多 mark;forceGraph 回传的轴域在 x/y 未显式声明时注入
1134
1165
  * linear scale 实例(仿真坐标以 0 为中心,缺省推断 domain 不包含负半轴余量)
1166
+ * - spec.views 存在时分流 viewGrid 多视图组合(views 与 marks 互斥),各子视图以
1167
+ * child 模式递归构建——host 权属字段(tooltip/pointer/keyboard/focus/focusRing)
1168
+ * 归外层组合定义持有(官方 assertChildDefinition 拒绝子 view 携带),child 模式
1169
+ * 下显式声明即抛错;theme 仅携带 spec 差异(defaultChartTheme.background 会触发
1170
+ * 库拒绝,外层 mergeTheme 负责补齐缺省)
1135
1171
  */
1136
1172
  /** color.legend 声明 → 图例实例;true 等价 { kind:'color' } 缺省配置 */
1137
1173
  function translateLegend(legend) {
@@ -1193,7 +1229,7 @@ function coerceTemporalChannels(spec, table) {
1193
1229
  if (!set) fieldsByDataset.set(name, set = /* @__PURE__ */ new Set());
1194
1230
  set.add(field);
1195
1231
  };
1196
- for (const mark of spec.marks) {
1232
+ for (const mark of spec.marks ?? []) {
1197
1233
  if (xTemporal) for (const f of [
1198
1234
  "x",
1199
1235
  "x1",
@@ -1225,22 +1261,24 @@ function coerceTemporalChannels(spec, table) {
1225
1261
  return changed ? next : table;
1226
1262
  }
1227
1263
  /**
1228
- * 单 spec → definition 核心(facet 子 spec 复用):返回原始 spec 对象而非
1229
- * defineChart 产物——facet chart 回调要求返回 ChartSpec 字面量形态。
1264
+ * 单 spec → definition 核心(facet 子 spec、viewGrid 子视图复用):返回原始 spec
1265
+ * 对象而非 defineChart 产物——facet chart 回调要求返回 ChartSpec 字面量形态。
1230
1266
  * rowsOverride:facet 分组行注入(子 spec marks 未声明 data 时绑定分组行)。
1267
+ * mode 'child':viewGrid 子视图构建——剥除 host 权属字段(见文件头约定)。
1231
1268
  */
1232
- function buildDefinition(spec, datasets, rowsOverride) {
1269
+ function buildDefinition(spec, datasets, rowsOverride, mode = "host") {
1233
1270
  const base = { ...datasets };
1234
1271
  if (rowsOverride !== void 0) base.rows = rowsOverride;
1235
1272
  const table = coerceTemporalChannels(spec, spec.transforms?.length ? applyTransforms(spec.transforms, base) : base);
1273
+ const markSpecs = spec.marks ?? [];
1236
1274
  const marks = [];
1237
1275
  let derivedXDomain;
1238
1276
  let derivedYDomain;
1239
- let polarOnly = spec.marks.length > 0;
1277
+ let polarOnly = markSpecs.length > 0;
1240
1278
  const pushMark = (specMark, runtimeMark) => {
1241
1279
  marks.push(specMark.decorative === true ? decorative(runtimeMark) : runtimeMark);
1242
1280
  };
1243
- for (const mark of spec.marks) {
1281
+ for (const mark of spec.marks ?? []) {
1244
1282
  const bound = rowsOverride !== void 0 && mark.data === void 0 ? {
1245
1283
  ...mark,
1246
1284
  data: "rows"
@@ -1255,12 +1293,14 @@ function buildDefinition(spec, datasets, rowsOverride) {
1255
1293
  if (result.yDomain) derivedYDomain = result.yDomain;
1256
1294
  } else pushMark(bound, translateMark(bound, table));
1257
1295
  }
1258
- const definition = {
1296
+ if (mode === "child" && spec.theme?.background !== void 0) throw new Error("translateViewGrid 子视图不得声明 theme.background(库拒绝子图持有场景背景;背景用子图内普通 mark 表达)");
1297
+ const theme = mode === "child" ? spec.theme === void 0 ? void 0 : { ...spec.theme } : {
1298
+ ...defaultChartTheme,
1299
+ ...spec.theme
1300
+ };
1301
+ const definition = theme === void 0 ? { marks } : {
1259
1302
  marks,
1260
- theme: {
1261
- ...defaultChartTheme,
1262
- ...spec.theme
1263
- }
1303
+ theme
1264
1304
  };
1265
1305
  const xSpec = spec.x === void 0 && polarOnly ? null : spec.x;
1266
1306
  const ySpec = spec.y === void 0 && polarOnly ? null : spec.y;
@@ -1289,6 +1329,16 @@ function buildDefinition(spec, datasets, rowsOverride) {
1289
1329
  if (spec.color.legend !== void 0) color.legend = translateLegend(spec.color.legend);
1290
1330
  definition.color = color;
1291
1331
  }
1332
+ if (mode === "child") {
1333
+ for (const key of [
1334
+ "tooltip",
1335
+ "pointer",
1336
+ "keyboard",
1337
+ "focusRing",
1338
+ "focus"
1339
+ ]) if (spec[key] !== void 0) throw new Error(`translateViewGrid 子视图不得声明 "${key}"(host 权属字段,配置在外层 spec)`);
1340
+ return definition;
1341
+ }
1292
1342
  if (spec.pointer !== void 0) definition.pointer = spec.pointer;
1293
1343
  if (spec.keyboard !== void 0) definition.keyboard = spec.keyboard;
1294
1344
  if (spec.focusRing !== void 0) definition.focusRing = spec.focusRing;
@@ -1302,6 +1352,24 @@ function buildDefinition(spec, datasets, rowsOverride) {
1302
1352
  * datasets:命名数据集表(数据顶层化契约),marks 内字符串引用在此解析。
1303
1353
  */
1304
1354
  function translateChartSpec(spec, datasets) {
1355
+ if (spec.views !== void 0) {
1356
+ if (spec.marks?.length) throw new Error("translateChartSpec: views 与 marks 互斥——viewGrid 顶层不直接绘制 marks,图元属于各子视图");
1357
+ return defineChart(translateViewGrid(spec, datasets, {
1358
+ buildChild: (child, table) => defineChart(buildDefinition(child, table, void 0, "child")),
1359
+ applyHost: (composed, outer) => {
1360
+ const hosted = {
1361
+ ...composed,
1362
+ theme: {
1363
+ ...defaultChartTheme,
1364
+ ...outer.theme
1365
+ }
1366
+ };
1367
+ const tooltip$3 = outer.tooltip === void 0 ? tooltip : translateTooltip(outer.tooltip);
1368
+ if (tooltip$3 !== void 0) hosted.tooltip = tooltip$3;
1369
+ return hosted;
1370
+ }
1371
+ }));
1372
+ }
1305
1373
  return defineChart(buildDefinition(spec, datasets));
1306
1374
  }
1307
1375
  //#endregion
@@ -2,9 +2,9 @@
2
2
  * 声明式 JSON → TanStack Charts 运行时定义的翻译层(桶文件)。
3
3
  * 模块分工:types(声明式契约)/ curve / scale / axis / mark(基础+spatial)/
4
4
  * transforms(数据预处理管道)/ polar(极坐标族)/ composite(七类命名复合)/
5
- * definition(translateChartSpec 组装入口)。
5
+ * view(viewGrid 多视图组合)/ definition(translateChartSpec 组装入口)。
6
6
  */
7
- export type { CxChartAxisSpec, CxChartCurveName, CxChartDatasets, CxChartForceSpec, CxChartLayoutSpec, CxChartMarkSpec, CxChartMarkType, CxChartPolarGuideSpec, CxChartReduce, CxChartScaleSpec, CxChartSpec, CxChartTransformOutputs, CxChartTransformSpec, } from './types';
7
+ export type { CxChartAxisSpec, CxChartCurveName, CxChartDatasets, CxChartForceSpec, CxChartLayoutSpec, CxChartMarkSpec, CxChartMarkType, CxChartPolarGuideSpec, CxChartReduce, CxChartScaleSpec, CxChartSpec, CxChartTransformOutputs, CxChartTransformSpec, CxChartViewGridSpec, CxChartViewItem, CxChartViewLink, CxChartViewTrack, } from './types';
8
8
  export { CX_CHART_CURVE_NAMES, translateCurve } from './curve';
9
9
  export { translateScale } from './scale';
10
10
  export { translateAxis } from './axis';
@@ -43,6 +43,12 @@ export interface CxChartAxisSpec {
43
43
  nice?: boolean | number;
44
44
  reverse?: boolean;
45
45
  grid?: boolean;
46
+ /**
47
+ * 半径像素 range 的比例对(相对 polar 最终半径,0–1),仅 polar 容器的
48
+ * radiusAxis 消费;物化为官方 PolarLength 回调数组(resize 时随最终半径重解析)。
49
+ * 用途:rose/径向条等需要「语义零映射到内径偏移」而非物理圆心的场景。
50
+ */
51
+ range?: [number, number];
46
52
  axis?: false | {
47
53
  label?: string;
48
54
  ticks?: {
@@ -424,8 +430,49 @@ export type CxChartTransformSpec = {
424
430
  };
425
431
  /** 命名数据集表:物料 data 顶层除 definition 外的数组字段(GenUI 契约 rows/nodes/links) */
426
432
  export type CxChartDatasets = Record<string, readonly unknown[] | undefined>;
433
+ /** viewGrid 网格轨道:size 固定像素 / grow 按比例分配剩余空间(min/max 钳制) */
434
+ export type CxChartViewTrack = {
435
+ id: string;
436
+ size: number;
437
+ } | {
438
+ id: string;
439
+ grow: number;
440
+ min?: number;
441
+ max?: number;
442
+ };
443
+ /** viewGrid 轴链接目标:share 共享 scale(domain/序/方向/bandwidth 须一致,不一致显式失败);align 仅对齐 plot 端点、domain 各自独立 */
444
+ export interface CxChartViewLink {
445
+ x?: string;
446
+ y?: string;
447
+ }
448
+ export interface CxChartViewItem {
449
+ id: string;
450
+ /** 轨道引用:row/column 分别指向 views.rows/views.columns 的轨道 id */
451
+ row: string;
452
+ column: string;
453
+ share?: CxChartViewLink;
454
+ align?: CxChartViewLink;
455
+ /**
456
+ * 子视图 spec(marks/x/y/color/margin/guides/transforms 等归属子图);
457
+ * host 权属字段(tooltip/pointer/keyboard/focus/focusRing)归外层组合定义,
458
+ * 子视图声明即拒绝(官方 assertChildDefinition 契约);不支持嵌套 views。
459
+ */
460
+ chart: CxChartSpec;
461
+ }
462
+ /** viewGrid 多视图组合(非重叠网格;叠层/内嵌等组合形态官方 composeViews 支持、契约暂不暴露) */
463
+ export interface CxChartViewGridSpec {
464
+ rows: CxChartViewTrack[];
465
+ columns: CxChartViewTrack[];
466
+ gap?: number;
467
+ rowGap?: number;
468
+ columnGap?: number;
469
+ items: CxChartViewItem[];
470
+ }
427
471
  export interface CxChartSpec {
428
- marks: CxChartMarkSpec[];
472
+ /** viewGrid 顶层省略(views 与 marks 互斥);其余场景必填 */
473
+ marks?: CxChartMarkSpec[];
474
+ /** 多视图组合:存在时 marks 须为空,各子视图在网格单元内独立成图、共享外层 host */
475
+ views?: CxChartViewGridSpec;
429
476
  x?: CxChartAxisSpec | null;
430
477
  y?: CxChartAxisSpec | null;
431
478
  theme?: Partial<ChartTheme>;
@@ -0,0 +1,16 @@
1
+ import type { CxChartDatasets, CxChartSpec } from './types';
2
+ /**
3
+ * viewGrid 多视图组合:spec.views → 官方 viewGrid(命名视图 + 网格轨道 + 轴链接)。
4
+ *
5
+ * 权属分界(官方 assertChildDefinition 契约):子 view 保留 marks/scales/guides/
6
+ * margin/color/clip;host 字段(tooltip/keyboard/pointer/focus/focusRing/motion/
7
+ * gradients/theme.background)只能由外层组合定义持有,子 view 携带即被库拒绝。
8
+ * 子 view 经 buildChild 以 child 模式递归构建(剥除 host 字段,见 definition.ts);
9
+ * 顶层 host 字段(theme 合并、tooltip 缺省注入)由 applyHost 施加于组合产物。
10
+ * hooks 回调注入保持依赖单向(definition.ts → view.ts,同 composite 先例)。
11
+ */
12
+ export interface CxChartViewGridHooks {
13
+ buildChild: (child: CxChartSpec, datasets: CxChartDatasets | undefined) => unknown;
14
+ applyHost: (composed: Record<string, unknown>, spec: CxChartSpec) => Record<string, unknown>;
15
+ }
16
+ export declare function translateViewGrid(spec: CxChartSpec, datasets: CxChartDatasets | undefined, hooks: CxChartViewGridHooks): Record<string, unknown>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lionad/cx-comps-tanstack-charts",
3
- "version": "0.1.0-alpha.11",
3
+ "version": "0.1.0-alpha.13",
4
4
  "description": "cx material components wrapping TanStack Charts (@tanstack/charts/vue)",
5
5
  "keywords": [
6
6
  "charts",