@lionad/cx-comps-tanstack-charts 0.1.0-alpha.28 → 0.1.0-alpha.30

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
@@ -32,7 +32,7 @@ import { createChartRendererAdapter } from "@tanstack/charts/adapter/renderer";
32
32
  import { motion } from "@tanstack/charts/motion";
33
33
  import { compileTrigger, createTriggerRegistry } from "@lionad/cx-stream";
34
34
  //#region src/shared/chart-skeleton.vue
35
- const _sfc_main$7 = /*@__PURE__*/ defineComponent({
35
+ const _sfc_main$8 = /*@__PURE__*/ defineComponent({
36
36
  __name: "chart-skeleton",
37
37
  props: { height: { default: 320 } },
38
38
  setup(__props) {
@@ -123,6 +123,85 @@ const _sfc_main$7 = /*@__PURE__*/ defineComponent({
123
123
  }
124
124
  });
125
125
  //#endregion
126
+ //#region src/shared/chart-empty.vue
127
+ const _sfc_main$7 = /*@__PURE__*/ defineComponent({
128
+ __name: "chart-empty",
129
+ props: { height: { default: 320 } },
130
+ setup(__props) {
131
+ const props = __props;
132
+ const ns = useCxBEM("tanstack-charts-chart-empty");
133
+ const normalizedHeight = computed(() => Number.isFinite(props.height) && props.height > 0 ? props.height : 320);
134
+ const shellStyle = computed(() => ({
135
+ display: "flex",
136
+ flexDirection: "column",
137
+ alignItems: "center",
138
+ justifyContent: "center",
139
+ gap: "0.375rem",
140
+ width: "100%",
141
+ boxSizing: "border-box",
142
+ height: `${normalizedHeight.value}px`,
143
+ border: "1px dashed rgba(148, 163, 184, 0.35)",
144
+ borderRadius: "0.75rem",
145
+ color: "rgba(148, 163, 184, 0.9)",
146
+ textAlign: "center"
147
+ }));
148
+ const iconStyle = {
149
+ width: "2rem",
150
+ height: "2rem",
151
+ opacity: .7
152
+ };
153
+ const titleStyle = {
154
+ margin: 0,
155
+ fontSize: "0.875rem",
156
+ fontWeight: 500
157
+ };
158
+ const descStyle = {
159
+ margin: 0,
160
+ fontSize: "0.75rem",
161
+ opacity: .75
162
+ };
163
+ return (_ctx, _cache) => {
164
+ return openBlock(), createElementBlock(Fragment, null, [createCommentVNode(" 图表终态空态:数据集占位/不可渲染时替换 Chart 占位(空壳 svg 无反馈等同未渲染)。\n 全 inline style:保持包零 css 分发契约(同 chart-skeleton 模式) "), createElementVNode("div", {
165
+ class: normalizeClass(unref(ns).b()),
166
+ style: normalizeStyle(shellStyle.value),
167
+ "data-testid": "cx-tanstack-charts-chart-empty",
168
+ role: "status"
169
+ }, [
170
+ (openBlock(), createElementBlock("svg", {
171
+ style: iconStyle,
172
+ viewBox: "0 0 24 24",
173
+ fill: "none",
174
+ stroke: "currentColor",
175
+ "stroke-width": "1.5",
176
+ "aria-hidden": "true"
177
+ }, [..._cache[0] || (_cache[0] = [
178
+ createElementVNode("circle", {
179
+ cx: "12",
180
+ cy: "12",
181
+ r: "9"
182
+ }, null, -1),
183
+ createElementVNode("line", {
184
+ x1: "12",
185
+ y1: "7.5",
186
+ x2: "12",
187
+ y2: "13",
188
+ "stroke-linecap": "round"
189
+ }, null, -1),
190
+ createElementVNode("circle", {
191
+ cx: "12",
192
+ cy: "16.5",
193
+ r: "0.9",
194
+ fill: "currentColor",
195
+ stroke: "none"
196
+ }, null, -1)
197
+ ])])),
198
+ createElementVNode("p", { style: titleStyle }, "图表数据不可用"),
199
+ createElementVNode("p", { style: descStyle }, "数据集中缺少可渲染的数据,已跳过绘制")
200
+ ], 6)], 2112);
201
+ };
202
+ }
203
+ });
204
+ //#endregion
126
205
  //#region src/shared/streaming.ts
127
206
  /**
128
207
  * 读取 trigger 注入的流式骨架标记(_cx_streaming,下划线前缀键不会透传物料)。
@@ -322,6 +401,55 @@ function translateAxis(spec, fallbackKind) {
322
401
  return axis;
323
402
  }
324
403
  //#endregion
404
+ //#region src/shared/translate/geometry.ts
405
+ /**
406
+ * 占位几何检测:LLM 在数据集缺席时可能产出 geometry 为占位字符串的示意 spec
407
+ * (如 "…(命名数据集)…")。翻译层与渲染层对缺失数据容错回退空数组,占位字符串
408
+ * 则被库静默忽略——两者都渲染为空壳 svg 且无任何反馈。本模块提供「数据集整体
409
+ * 不可渲染」的判定与翻译期警告,让空壳显式化为空态反馈。
410
+ *
411
+ * 流式安全性:占位字符串只可能出现在完整闭合的 JSON 中(流式截断停在字符层,
412
+ * 截断帧不可解析即不会产出 spec),检测不存在流式中间态误报面。
413
+ */
414
+ /** 数据集为 GeoJSON Feature 数组(rows 平铺 features 与命名 geo 数据集同形态) */
415
+ function isFeatureArray(rows) {
416
+ return rows.length > 0 && rows.every((f) => f?.type === "Feature");
417
+ }
418
+ /**
419
+ * 单数据集整体不可渲染判定:Feature 数组且全部条目几何缺失或为占位字符串。
420
+ * 混合场景(部分真实几何)不算——部分数据仍可渲染,整体空态会误杀;
421
+ * geometry: null 是 GeoJSON 合法形态,但全部条目皆 null 时渲染结果同为空壳。
422
+ */
423
+ function isPlaceholderGeometryDataset(rows) {
424
+ if (!isFeatureArray(rows)) return false;
425
+ return rows.every((f) => {
426
+ const geometry = f.geometry;
427
+ return geometry == null || typeof geometry === "string";
428
+ });
429
+ }
430
+ /**
431
+ * 组件层空态判定:数据集表中存在 GeoJSON 形态数据集,且全部占位。
432
+ * 存在任一可用数据集时返回 false(部分数据仍可渲染)。
433
+ */
434
+ function hasOnlyPlaceholderGeometry(datasets) {
435
+ if (!datasets) return false;
436
+ const geoSets = Object.values(datasets).filter((arr) => arr !== void 0 && isFeatureArray(arr));
437
+ return geoSets.length > 0 && geoSets.every((arr) => isPlaceholderGeometryDataset(arr));
438
+ }
439
+ /** 翻译期警告去重:同数组引用只警告一次(Vue computed 重算与多 mark 共享数据集会重复调用) */
440
+ const warned = /* @__PURE__ */ new WeakSet();
441
+ /**
442
+ * 翻译期占位几何警告:数据集整体占位时 console.warn 指出空壳根因与修复方向。
443
+ * 只挂在 geoShape(占位字符串的高发地——示意 spec 的外部轮廓数据集);普通 mark
444
+ * 的数据集在途是流式常态,warn 会成噪音。WeakSet 按引用去重,数据集内容更新
445
+ * (新引用)视为新数据集重新警告。
446
+ */
447
+ function warnPlaceholderGeometry(rows, datasetName, markType) {
448
+ if (!isPlaceholderGeometryDataset(rows) || warned.has(rows)) return;
449
+ warned.add(rows);
450
+ console.warn(`[cx-charts] ${markType} 数据集${datasetName ? ` "${datasetName}"` : ""} 的 geometry 全部缺失或为占位字符串,渲染为空壳——须以真实 GeoJSON 数据集整体替换示意占位(技能侧见「数据供给」表)。`);
451
+ }
452
+ //#endregion
325
453
  //#region src/shared/translate/transforms.ts
326
454
  /**
327
455
  * 数据预处理管道翻译:spec 顶层 transforms 按序执行,产物以 name 注册进数据集表。
@@ -1154,6 +1282,7 @@ const GEO_PROJECTION_FACTORIES = {
1154
1282
  /** geoShape:projection 名称枚举 → descriptor {type, fit, inset};fit:'data' 由库拟合数据本体,{data:'<name>'} 拟合另一命名数据集 */
1155
1283
  function translateGeoShape(spec, datasets) {
1156
1284
  const rows = resolveMarkData(spec.data, datasets);
1285
+ warnPlaceholderGeometry(rows, typeof spec.data === "string" ? spec.data : void 0, "geoShape");
1157
1286
  const projectionName = spec.projection ?? "mercator";
1158
1287
  if (typeof projectionName === "object") throw new Error("translateGeoShape: projection {$by} 仅允许出现在 facet chart 子模板内");
1159
1288
  const factory = GEO_PROJECTION_FACTORIES[projectionName];
@@ -1603,6 +1732,7 @@ const _sfc_main$5 = /*@__PURE__*/ defineComponent({
1603
1732
  const { spec, datasets, hostProps, ariaLabel } = useChartProps(attrs);
1604
1733
  const definition = computed(() => translateChartSpec(spec.value, datasets.value));
1605
1734
  const isStreaming = computed(() => streamingFields(attrs).includes("definition"));
1735
+ const hasPlaceholderGeometry = computed(() => hasOnlyPlaceholderGeometry(datasets.value));
1606
1736
  const skeletonHeight = computed(() => {
1607
1737
  const h = Number(attrs.height);
1608
1738
  return Number.isFinite(h) && h > 0 ? h : 320;
@@ -1611,10 +1741,10 @@ const _sfc_main$5 = /*@__PURE__*/ defineComponent({
1611
1741
  return openBlock(), createElementBlock(Fragment, null, [createCommentVNode(" 库根元素 inheritAttrs:false 且多根(host + tooltip Teleport),testid/BEM 类须由本层 wrapper 承担。\n cx-charts 为主题桥作用域类(cx-comps charts-theme 映射 design token 到 --ts-chart-N/--chart-N):\n 随组件自动声明,宿主无需手动加类——shadcn 系 spec 的 var(--chart-N, ...) 引用在此获得色板 "), createElementVNode("div", {
1612
1742
  class: normalizeClass([unref(ns).b(), "cx-charts"]),
1613
1743
  "data-testid": "cx-tanstack-charts-chart"
1614
- }, [createCommentVNode(" definition 未闭合期间骨架占位:空壳帧的空 marks 只渲染不可见空 svg,无反馈等同未渲染 "), isStreaming.value ? (openBlock(), createBlock(_sfc_main$7, {
1744
+ }, [createCommentVNode(" definition 未闭合期间骨架占位:空壳帧的空 marks 只渲染不可见空 svg,无反馈等同未渲染 "), isStreaming.value ? (openBlock(), createBlock(_sfc_main$8, {
1615
1745
  key: 0,
1616
1746
  height: skeletonHeight.value
1617
- }, null, 8, ["height"])) : unref(spec).motion !== void 0 ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createCommentVNode(" motion 声明走自研挂载分支:库 vue adapter 无 motion renderer 注入口子 "), createVNode(_sfc_main$6, mergeProps({
1747
+ }, null, 8, ["height"])) : hasPlaceholderGeometry.value ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createCommentVNode(" 终态占位几何空态:全部 GeoJSON 数据集均占位/缺失时渲染必为空壳,显式降级为空态反馈 "), createVNode(_sfc_main$7, { height: skeletonHeight.value }, null, 8, ["height"])], 2112)) : unref(spec).motion !== void 0 ? (openBlock(), createElementBlock(Fragment, { key: 2 }, [createCommentVNode(" motion 声明走自研挂载分支:库 vue adapter 无 motion renderer 注入口子 "), createVNode(_sfc_main$6, mergeProps({
1618
1748
  definition: definition.value,
1619
1749
  ariaLabel: unref(ariaLabel),
1620
1750
  "motion-options": unref(spec).motion
@@ -1622,7 +1752,7 @@ const _sfc_main$5 = /*@__PURE__*/ defineComponent({
1622
1752
  "definition",
1623
1753
  "ariaLabel",
1624
1754
  "motion-options"
1625
- ])], 2112)) : (openBlock(), createBlock(unref(Chart), mergeProps({ key: 2 }, unref(hostProps), {
1755
+ ])], 2112)) : (openBlock(), createBlock(unref(Chart), mergeProps({ key: 3 }, unref(hostProps), {
1626
1756
  definition: definition.value,
1627
1757
  "aria-label": unref(ariaLabel)
1628
1758
  }), null, 16, ["definition", "aria-label"]))], 2)], 2112);
@@ -2426,4 +2556,4 @@ const CxTanstackChartsBundle = {
2426
2556
  materials: [...CxTanstackCharts]
2427
2557
  };
2428
2558
  //#endregion
2429
- export { COMPOSITE_TYPES, CX_CHART_CURVE_NAMES, CxTanstackCharts, area_default as CxTanstackChartsArea, bar_default as CxTanstackChartsBar, CxTanstackChartsBundle, chart_default as CxTanstackChartsChart, dot_default as CxTanstackChartsDot, line_default as CxTanstackChartsLine, pie_default as CxTanstackChartsPie, POLAR_FAMILY_TYPES, TANSTACK_CHARTS_STREAM_TRIGGERS, applyTransforms, createTanstackChartsTriggerRegistry, mainArrayOf, resolveMarkData, resolveRScaleOption, translateAxis, translateChartSpec, translateCompositeMark, translateCurve, translateMark, translateOutputs, translatePie, translatePolar, translateReduce, translateScale, translateTransform };
2559
+ export { COMPOSITE_TYPES, CX_CHART_CURVE_NAMES, CxTanstackCharts, area_default as CxTanstackChartsArea, bar_default as CxTanstackChartsBar, CxTanstackChartsBundle, chart_default as CxTanstackChartsChart, dot_default as CxTanstackChartsDot, line_default as CxTanstackChartsLine, pie_default as CxTanstackChartsPie, POLAR_FAMILY_TYPES, TANSTACK_CHARTS_STREAM_TRIGGERS, applyTransforms, createTanstackChartsTriggerRegistry, hasOnlyPlaceholderGeometry, isPlaceholderGeometryDataset, mainArrayOf, resolveMarkData, resolveRScaleOption, translateAxis, translateChartSpec, translateCompositeMark, translateCurve, translateMark, translateOutputs, translatePie, translatePolar, translateReduce, translateScale, translateTransform, warnPlaceholderGeometry };
@@ -0,0 +1,8 @@
1
+ type __VLS_Props = {
2
+ height?: number;
3
+ };
4
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
5
+ height: number;
6
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
@@ -0,0 +1,19 @@
1
+ import type { CxChartDatasets } from './types';
2
+ /**
3
+ * 单数据集整体不可渲染判定:Feature 数组且全部条目几何缺失或为占位字符串。
4
+ * 混合场景(部分真实几何)不算——部分数据仍可渲染,整体空态会误杀;
5
+ * geometry: null 是 GeoJSON 合法形态,但全部条目皆 null 时渲染结果同为空壳。
6
+ */
7
+ export declare function isPlaceholderGeometryDataset(rows: readonly unknown[]): boolean;
8
+ /**
9
+ * 组件层空态判定:数据集表中存在 GeoJSON 形态数据集,且全部占位。
10
+ * 存在任一可用数据集时返回 false(部分数据仍可渲染)。
11
+ */
12
+ export declare function hasOnlyPlaceholderGeometry(datasets: CxChartDatasets): boolean;
13
+ /**
14
+ * 翻译期占位几何警告:数据集整体占位时 console.warn 指出空壳根因与修复方向。
15
+ * 只挂在 geoShape(占位字符串的高发地——示意 spec 的外部轮廓数据集);普通 mark
16
+ * 的数据集在途是流式常态,warn 会成噪音。WeakSet 按引用去重,数据集内容更新
17
+ * (新引用)视为新数据集重新警告。
18
+ */
19
+ export declare function warnPlaceholderGeometry(rows: readonly unknown[], datasetName: string | undefined, markType: string): void;
@@ -8,6 +8,7 @@ export type { CxChartAxisSpec, CxChartCurveName, CxChartDatasets, CxChartForceSp
8
8
  export { CX_CHART_CURVE_NAMES, translateCurve } from './curve';
9
9
  export { translateScale } from './scale';
10
10
  export { translateAxis } from './axis';
11
+ export { hasOnlyPlaceholderGeometry, isPlaceholderGeometryDataset, warnPlaceholderGeometry } from './geometry';
11
12
  export { resolveMarkData, resolveRScaleOption, translateMark } from './mark';
12
13
  export { applyTransforms, translateOutputs, translateReduce, translateTransform } from './transforms';
13
14
  export { POLAR_FAMILY_TYPES, translatePie, translatePolar } from './polar';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lionad/cx-comps-tanstack-charts",
3
- "version": "0.1.0-alpha.28",
3
+ "version": "0.1.0-alpha.30",
4
4
  "description": "cx material components wrapping TanStack Charts (@tanstack/charts/vue)",
5
5
  "keywords": [
6
6
  "charts",