vccs 1.0.0-beta.1 → 1.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 (43) hide show
  1. package/README.md +11 -0
  2. package/dist/es/cartesian/bar/components/BarRectangles.mjs +1 -1
  3. package/dist/es/cartesian/brush/Brush.mjs +4 -0
  4. package/dist/es/cartesian/funnel/Funnel.mjs +1 -1
  5. package/dist/es/cartesian/scatter/Scatter.mjs +1 -1
  6. package/dist/es/chart/CellGridLayer.mjs +1 -1
  7. package/dist/es/chart/JourneySankey.mjs +1 -1
  8. package/dist/es/chart/Sankey.mjs +2 -2
  9. package/dist/es/chart/Sparkline.mjs +1 -1
  10. package/dist/es/chart/SunburstChart.mjs +1 -1
  11. package/dist/es/chart/Treemap.mjs +1 -1
  12. package/dist/es/events/sync.mjs +1 -2
  13. package/dist/es/events/usePointEvents.mjs +1 -1
  14. package/dist/es/model/tooltip.mjs +3 -5
  15. package/dist/es/polar/pie/Pie.mjs +1 -1
  16. package/dist/es/polar/radial-bar/RadialBar.mjs +1 -1
  17. package/dist/model/tooltip.d.ts +4 -18
  18. package/package.json +6 -2
  19. package/usage/catalog.json +235 -0
  20. package/usage/guides/charts/area-chart.md +56 -0
  21. package/usage/guides/charts/bar-chart.md +60 -0
  22. package/usage/guides/charts/bar-list.md +46 -0
  23. package/usage/guides/charts/calendar-heatmap.md +55 -0
  24. package/usage/guides/charts/cohort-chart.md +47 -0
  25. package/usage/guides/charts/composed-chart.md +70 -0
  26. package/usage/guides/charts/funnel-chart.md +58 -0
  27. package/usage/guides/charts/heatmap.md +51 -0
  28. package/usage/guides/charts/journey-sankey.md +49 -0
  29. package/usage/guides/charts/line-chart.md +60 -0
  30. package/usage/guides/charts/pie-chart.md +59 -0
  31. package/usage/guides/charts/radar-chart.md +69 -0
  32. package/usage/guides/charts/radial-bar-chart.md +54 -0
  33. package/usage/guides/charts/sankey.md +59 -0
  34. package/usage/guides/charts/scatter-chart.md +62 -0
  35. package/usage/guides/charts/sparkline.md +51 -0
  36. package/usage/guides/charts/sunburst-chart.md +66 -0
  37. package/usage/guides/charts/tracker.md +49 -0
  38. package/usage/guides/charts/treemap.md +68 -0
  39. package/usage/guides/core/installation.md +68 -0
  40. package/usage/guides/core/ssr-testing.md +50 -0
  41. package/usage/guides/core/typed-rows.md +47 -0
  42. package/usage/guides/core/vue-api.md +72 -0
  43. package/usage/vccs-usage.mjs +191 -0
package/README.md CHANGED
@@ -76,6 +76,17 @@ export default defineConfig({
76
76
  })
77
77
  ```
78
78
 
79
+ ## Use vccs with an AI agent
80
+
81
+ ```sh
82
+ npx skills add unovue/vue-charts # add -g to install for every project
83
+ ```
84
+
85
+ Then just ask for the chart — you run nothing yourself. Claude loads the skill when the request
86
+ matches it, or you can invoke it explicitly with `/vccs-usage`:
87
+
88
+ > add a card with weekly user retention by signup month
89
+
79
90
  ## Typed rows
80
91
 
81
92
  `defineChartComponents` binds your row type to the components you pick. A wrong `data-key` is
@@ -148,7 +148,7 @@ var BarRectangles = /* @__PURE__ */ defineComponent({
148
148
  emit("mouseenter", bar, index, event);
149
149
  },
150
150
  mouseleave: ({ bar, index }, _position, event) => {
151
- tooltip.clear("hover");
151
+ tooltip.clear("hover", "item");
152
152
  emit("mouseleave", bar, index, event);
153
153
  }
154
154
  });
@@ -55,8 +55,10 @@ var BrushView = /* @__PURE__ */ defineComponent({
55
55
  const startIndex = computed(() => view.range?.startIndex ?? 0);
56
56
  const endIndex = computed(() => view.range?.endIndex ?? Math.max(0, (chartData.value?.length ?? 0) - 1));
57
57
  const calculatedY = computed(() => (y.value ?? 0) + (props.dy ?? 0));
58
+ let proposed;
58
59
  function onChange(nextState) {
59
60
  if (sameRange(nextState, view.range)) return;
61
+ proposed = nextState;
60
62
  emit("update:range", nextState);
61
63
  emit("change", nextState);
62
64
  nextTick(restoreControlledPositions);
@@ -68,6 +70,8 @@ var BrushView = /* @__PURE__ */ defineComponent({
68
70
  });
69
71
  function restoreControlledPositions() {
70
72
  if (!view.controlled()) return;
73
+ if (sameRange(view.range, proposed ?? null)) return;
74
+ proposed = void 0;
71
75
  brushState.value.startX = brushState.value.scale?.(startIndex.value);
72
76
  brushState.value.endX = brushState.value.scale?.(endIndex.value);
73
77
  }
@@ -156,7 +156,7 @@ var FunnelView = /* @__PURE__ */ defineComponent({
156
156
  });
157
157
  }
158
158
  function handleTrapezoidLeave() {
159
- tooltip.clear("hover");
159
+ tooltip.clear("hover", "item");
160
160
  }
161
161
  return () => {
162
162
  if (props.hide) return null;
@@ -125,7 +125,7 @@ var ScatterView = /* @__PURE__ */ defineComponent({
125
125
  });
126
126
  };
127
127
  const onMouseLeaveSymbol = () => {
128
- tooltip.clear("hover");
128
+ tooltip.clear("hover", "item");
129
129
  };
130
130
  let symbolData = [];
131
131
  const listeners = delegateItemEvents((index) => symbolData[index], {
@@ -203,7 +203,7 @@ var CellGridLayer = /* @__PURE__ */ defineComponent({
203
203
  coordinate: void 0,
204
204
  active: false
205
205
  });
206
- else tooltip.clear("hover");
206
+ else tooltip.clear("hover", "item");
207
207
  }
208
208
  function onEnter(cell, index, event) {
209
209
  keyboard.value = false;
@@ -369,7 +369,7 @@ function useJourneySankey(props, slots, emit) {
369
369
  }
370
370
  function leave() {
371
371
  hover.value = void 0;
372
- tooltip.clear("hover");
372
+ tooltip.clear("hover", "item");
373
373
  }
374
374
  function leaveNode(node, event) {
375
375
  leave();
@@ -292,11 +292,11 @@ function useSankey(props, slots, emit) {
292
292
  emit("link-mouseenter", link, index, e);
293
293
  }
294
294
  function handleNodeMouseLeave(node, index, e) {
295
- tooltip.clear("hover");
295
+ tooltip.clear("hover", "item");
296
296
  emit("node-mouseleave", node, index, e);
297
297
  }
298
298
  function handleLinkMouseLeave(link, index, e) {
299
- tooltip.clear("hover");
299
+ tooltip.clear("hover", "item");
300
300
  emit("link-mouseleave", link, index, e);
301
301
  }
302
302
  function handleNodeClick(node, index, e) {
@@ -247,7 +247,7 @@ function useSparkline(props, emit) {
247
247
  function setActive(index, keyboard = false) {
248
248
  const point = index === null ? void 0 : points.value[index];
249
249
  if (!point && !keyboard) {
250
- tooltip.clear("hover");
250
+ tooltip.clear("hover", "item");
251
251
  return;
252
252
  }
253
253
  const action = {
@@ -201,7 +201,7 @@ function useSunburst(props, slots, emit) {
201
201
  });
202
202
  }
203
203
  function handleMouseLeave(node, e) {
204
- tooltip.clear("hover");
204
+ tooltip.clear("hover", "item");
205
205
  emit("node-mouseleave", node, indexOf(node), e);
206
206
  }
207
207
  function handleClick(node, e) {
@@ -278,7 +278,7 @@ function useTreemap(props, slots, emit) {
278
278
  emit("node-mouseenter", node, index, e);
279
279
  }
280
280
  function handleNodeMouseLeave(node, index, e) {
281
- tooltip.clear("hover");
281
+ tooltip.clear("hover", "item");
282
282
  emit("node-mouseleave", node, index, e);
283
283
  }
284
284
  function handleNodeClick(node, index, e) {
@@ -10,8 +10,7 @@ function useTooltipChartSynchronisation(source, enabled) {
10
10
  const activeDataKey = computed(() => tooltip.target.value?.entry?.value?.settings.dataKey);
11
11
  const eventEmitterSymbol = presentation.emitter;
12
12
  const syncId = presentation.syncId;
13
- const tooltipState = computed(() => tooltip.syncInteraction.value);
14
- const isReceivingSynchronisation = computed(() => tooltipState.value?.active);
13
+ const isReceivingSynchronisation = computed(() => tooltip.receivingSynchronisation.value);
15
14
  watchEffect(() => {
16
15
  if (!enabled()) return;
17
16
  if (isReceivingSynchronisation.value) return;
@@ -30,7 +30,7 @@ function usePointEvents(emit) {
30
30
  emit("mouseenter", entry, index, event);
31
31
  },
32
32
  onMouseleave: (event) => {
33
- tooltip.clear("hover");
33
+ tooltip.clear("hover", "item");
34
34
  emit("mouseleave", entry, index, event);
35
35
  }
36
36
  };
@@ -286,8 +286,9 @@ function createTooltip(inputs) {
286
286
  coordinate: input.coordinate
287
287
  };
288
288
  }
289
- function clear(channel) {
289
+ function clear(channel, scope) {
290
290
  if (channel === "hover" && settings.value.trigger !== "hover") return;
291
+ if (scope === "item" && selection.value?.target.entry === void 0) return;
291
292
  request(null);
292
293
  if (channel === "keyboard") {
293
294
  selection.value = null;
@@ -371,10 +372,7 @@ function createTooltip(inputs) {
371
372
  index: selection.value.requestedIndex,
372
373
  active: selection.value.active
373
374
  } : noInteraction),
374
- syncInteraction: computed(() => ({
375
- ...selection.value?.channel === "sync" ? interaction.value : noInteraction,
376
- label: label.value
377
- })),
375
+ receivingSynchronisation: computed(() => selection.value?.channel === "sync"),
378
376
  activeIndexFor,
379
377
  coordinateFor,
380
378
  activate,
@@ -139,7 +139,7 @@ var PieView = /* @__PURE__ */ defineComponent({
139
139
  });
140
140
  }
141
141
  function handleSectorLeave() {
142
- tooltip.clear("hover");
142
+ tooltip.clear("hover", "item");
143
143
  }
144
144
  let sectorList = items.value;
145
145
  const listeners = delegateItemEvents((position) => sectorList[position]?.value, {
@@ -217,7 +217,7 @@ var RadialBarView = /* @__PURE__ */ defineComponent({
217
217
  emit("mouseenter", sector, sector.index, event);
218
218
  };
219
219
  const onMouseleave = (event) => {
220
- tooltip.clear("hover");
220
+ tooltip.clear("hover", "item");
221
221
  emit("mouseleave", sector, sector.index, event);
222
222
  };
223
223
  if (slots.shape) return createVNode("g", mergeProps({ "key": items.value[i].key }, { [DATA_ITEM_INDEX_ATTRIBUTE_NAME]: sector.index }, {
@@ -228,18 +228,11 @@ export declare function useTooltipController(): {
228
228
  entries: import('./registry.js').Registry<TooltipPayloadConfiguration>;
229
229
  announcement: import('vue').ShallowRef<string, string>;
230
230
  keyboardInteraction: ComputedRef<TooltipInteraction>;
231
- syncInteraction: ComputedRef<{
232
- label: string | undefined;
233
- configuration?: TooltipPayloadConfiguration;
234
- active: boolean;
235
- index: TooltipActiveIndex;
236
- dataKey: import('../types/base.js').ChartDataKey | undefined;
237
- coordinate: Coordinate | undefined;
238
- }>;
231
+ receivingSynchronisation: ComputedRef<boolean>;
239
232
  activeIndexFor: (entry: Entry) => ComputedRef<number | null>;
240
233
  coordinateFor: (candidate: Target | undefined, fallback?: Coordinate) => Coordinate | undefined;
241
234
  activate: (channel: Channel, input: TooltipTargetRequest) => void;
242
- clear: (channel: Channel) => void;
235
+ clear: (channel: Channel, scope?: "item") => void;
243
236
  };
244
237
  export declare function useTooltipSource(): TooltipSource;
245
238
  export declare function provideTooltipEntry(entry: Entry): void;
@@ -435,17 +428,10 @@ export declare function createTooltip(inputs: TooltipInputs): {
435
428
  entries: import('./registry.js').Registry<TooltipPayloadConfiguration>;
436
429
  announcement: import('vue').ShallowRef<string, string>;
437
430
  keyboardInteraction: ComputedRef<TooltipInteraction>;
438
- syncInteraction: ComputedRef<{
439
- label: string | undefined;
440
- configuration?: TooltipPayloadConfiguration;
441
- active: boolean;
442
- index: TooltipActiveIndex;
443
- dataKey: import('../types/base.js').ChartDataKey | undefined;
444
- coordinate: Coordinate | undefined;
445
- }>;
431
+ receivingSynchronisation: ComputedRef<boolean>;
446
432
  activeIndexFor: (entry: Entry) => ComputedRef<number | null>;
447
433
  coordinateFor: (candidate: Target | undefined, fallback?: Coordinate) => Coordinate | undefined;
448
434
  activate: (channel: Channel, input: TooltipTargetRequest) => void;
449
- clear: (channel: Channel) => void;
435
+ clear: (channel: Channel, scope?: "item") => void;
450
436
  };
451
437
  export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vccs",
3
3
  "type": "module",
4
- "version": "1.0.0-beta.1",
4
+ "version": "1.0.0",
5
5
  "description": "Composable charting components for Vue 3 — an unofficial port of Recharts",
6
6
  "author": "rick-hup",
7
7
  "license": "MIT",
@@ -51,11 +51,15 @@
51
51
  ]
52
52
  }
53
53
  },
54
+ "bin": {
55
+ "vccs-usage": "./usage/vccs-usage.mjs"
56
+ },
54
57
  "files": [
55
58
  "./LICENSE",
56
59
  "./README.md",
57
60
  "./dist/**/*.d.ts",
58
- "./dist/**/*.mjs"
61
+ "./dist/**/*.mjs",
62
+ "./usage"
59
63
  ],
60
64
  "peerDependencies": {
61
65
  "@nuxt/kit": "^4.0.0",
@@ -0,0 +1,235 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "guides": [
4
+ {
5
+ "id": "installation",
6
+ "title": "Installation and registration",
7
+ "description": "Install vccs in Vite or Nuxt 4, use explicit imports, auto-imports, or the Nuxt module.",
8
+ "category": "core",
9
+ "path": "guides/core/installation.md",
10
+ "components": [],
11
+ "useCases": ["Set up a new Vue or Nuxt chart", "Configure VccsResolver or vccs/nuxt"],
12
+ "keywords": ["install", "setup", "vite", "nuxt", "resolver", "auto-import", "安装", "自动导入"]
13
+ },
14
+ {
15
+ "id": "vue-api",
16
+ "title": "Vue API rules",
17
+ "description": "Use slots, events, Vue models, responsive sizing, data keys, and CSS theme variables correctly.",
18
+ "category": "core",
19
+ "path": "guides/core/vue-api.md",
20
+ "components": ["Tooltip", "Brush", "Legend"],
21
+ "useCases": ["Customize chart content", "Control active state", "Fix a Recharts-to-Vue API mismatch"],
22
+ "keywords": ["slots", "events", "v-model", "active-index", "brush", "tooltip", "responsive", "theme", "插槽", "事件", "主题"]
23
+ },
24
+ {
25
+ "id": "typed-rows",
26
+ "title": "Typed chart rows",
27
+ "description": "Make data-key and tooltip payload types follow the application's row type.",
28
+ "category": "core",
29
+ "path": "guides/core/typed-rows.md",
30
+ "components": [],
31
+ "useCases": ["Reject wrong data keys at compile time", "Type tooltip payloads"],
32
+ "keywords": ["typescript", "types", "generic", "defineChartComponents", "row", "类型", "类型安全"]
33
+ },
34
+ {
35
+ "id": "ssr-testing",
36
+ "title": "SSR and deterministic testing",
37
+ "description": "Render charts on the server and disable motion for stable SSR and geometry assertions.",
38
+ "category": "core",
39
+ "path": "guides/core/ssr-testing.md",
40
+ "components": [],
41
+ "useCases": ["Render charts in Nuxt SSR", "Write deterministic chart tests"],
42
+ "keywords": ["ssr", "server", "hydration", "test", "animation", "服务端渲染", "测试"]
43
+ },
44
+ {
45
+ "id": "area-chart",
46
+ "title": "AreaChart",
47
+ "description": "Show a filled trend, accumulated magnitude, or stacked total over an ordered axis.",
48
+ "category": "chart",
49
+ "path": "guides/charts/area-chart.md",
50
+ "components": ["AreaChart", "Area"],
51
+ "useCases": ["Show a filled trend over time", "Show stacked totals over an ordered axis", "用面积展示趋势或堆积总量"],
52
+ "keywords": ["area", "filled line", "stacked area", "trend", "time series", "面积图", "趋势", "堆积"]
53
+ },
54
+ {
55
+ "id": "bar-chart",
56
+ "title": "BarChart",
57
+ "description": "Compare categories with vertical or horizontal bars, including stacked and negative values.",
58
+ "category": "chart",
59
+ "path": "guides/charts/bar-chart.md",
60
+ "components": ["BarChart", "Bar"],
61
+ "useCases": ["Compare values across categories", "Rank categories or show a stacked composition", "用柱状图或条形图比较分类"],
62
+ "keywords": ["bar", "column", "comparison", "ranking", "stacked", "柱状图", "条形图", "对比", "排名"]
63
+ },
64
+ {
65
+ "id": "line-chart",
66
+ "title": "LineChart",
67
+ "description": "Show one or more trends over an ordered numeric or categorical axis.",
68
+ "category": "chart",
69
+ "path": "guides/charts/line-chart.md",
70
+ "components": ["LineChart", "Line"],
71
+ "useCases": ["Show a time series", "Compare trends across series", "用折线展示趋势"],
72
+ "keywords": ["line", "trend", "time series", "spark alternative", "折线图", "趋势", "时间序列"]
73
+ },
74
+ {
75
+ "id": "scatter-chart",
76
+ "title": "ScatterChart",
77
+ "description": "Show relationships between numeric variables with points or bubbles.",
78
+ "category": "chart",
79
+ "path": "guides/charts/scatter-chart.md",
80
+ "components": ["ScatterChart", "Scatter", "ZAxis"],
81
+ "useCases": ["Show correlation or distribution", "Encode a third numeric value as bubble size", "用散点展示相关性或分布"],
82
+ "keywords": ["scatter", "correlation", "distribution", "bubble", "散点图", "气泡图", "相关性"]
83
+ },
84
+ {
85
+ "id": "composed-chart",
86
+ "title": "ComposedChart",
87
+ "description": "Combine Bar, Line, Area, and Scatter series on shared Cartesian axes.",
88
+ "category": "chart",
89
+ "path": "guides/charts/composed-chart.md",
90
+ "components": ["ComposedChart", "Bar", "Line", "Area", "Scatter"],
91
+ "useCases": ["Compare actual bars with a target line", "Mix series types on one chart", "在同一坐标系混合柱状和折线"],
92
+ "keywords": ["composed", "mixed", "combo", "bar and line", "组合图", "混合图"]
93
+ },
94
+ {
95
+ "id": "funnel-chart",
96
+ "title": "FunnelChart",
97
+ "description": "Show values and drop-off across ordered conversion stages.",
98
+ "category": "chart",
99
+ "path": "guides/charts/funnel-chart.md",
100
+ "components": ["FunnelChart", "Funnel"],
101
+ "useCases": ["Show conversion across stages", "Expose drop-off in a process", "展示阶段转化和流失"],
102
+ "keywords": ["funnel", "conversion", "drop-off", "stages", "漏斗图", "转化率"]
103
+ },
104
+ {
105
+ "id": "pie-chart",
106
+ "title": "PieChart",
107
+ "description": "Show a small number of part-to-whole proportions as a pie or donut.",
108
+ "category": "chart",
109
+ "path": "guides/charts/pie-chart.md",
110
+ "components": ["PieChart", "Pie", "Cell"],
111
+ "useCases": ["Show proportions of a total", "Build a donut chart", "展示少量分类的占比"],
112
+ "keywords": ["pie", "donut", "proportion", "part-to-whole", "饼图", "环形图", "占比"]
113
+ },
114
+ {
115
+ "id": "radar-chart",
116
+ "title": "RadarChart",
117
+ "description": "Compare multivariate profiles across a shared set of dimensions.",
118
+ "category": "chart",
119
+ "path": "guides/charts/radar-chart.md",
120
+ "components": ["RadarChart", "Radar", "PolarAngleAxis", "PolarGrid"],
121
+ "useCases": ["Compare scores across dimensions", "Show a multivariate profile", "比较多维评分"],
122
+ "keywords": ["radar", "spider", "multivariate", "profile", "雷达图", "多维"]
123
+ },
124
+ {
125
+ "id": "radial-bar-chart",
126
+ "title": "RadialBarChart",
127
+ "description": "Show progress or category values as concentric or stacked radial bars.",
128
+ "category": "chart",
129
+ "path": "guides/charts/radial-bar-chart.md",
130
+ "components": ["RadialBarChart", "RadialBar"],
131
+ "useCases": ["Show progress toward a goal", "Compare values on polar tracks", "展示环形进度或径向分类值"],
132
+ "keywords": ["radial bar", "progress", "gauge", "polar bar", "径向条形图", "进度", "仪表盘"]
133
+ },
134
+ {
135
+ "id": "bar-list",
136
+ "title": "BarList",
137
+ "description": "Render a lightweight plain-HTML ranked list with inline value bars.",
138
+ "category": "chart",
139
+ "path": "guides/charts/bar-list.md",
140
+ "components": ["BarList"],
141
+ "useCases": ["Build a compact ranking list", "Show labeled values without SVG axes", "用 HTML 列表展示排行"],
142
+ "keywords": ["bar list", "ranking", "html", "leaderboard", "条形列表", "排行榜"]
143
+ },
144
+ {
145
+ "id": "sparkline",
146
+ "title": "Sparkline",
147
+ "description": "Place a tiny inline trend in a table, KPI tile, or dense dashboard row.",
148
+ "category": "chart",
149
+ "path": "guides/charts/sparkline.md",
150
+ "components": ["Sparkline"],
151
+ "useCases": ["Show a compact trend without axes", "Add a trend to a KPI or table row", "在指标卡或表格中展示迷你趋势"],
152
+ "keywords": ["sparkline", "mini line", "kpi", "inline trend", "火花线", "迷你趋势"]
153
+ },
154
+ {
155
+ "id": "tracker",
156
+ "title": "Tracker",
157
+ "description": "Show a compact sequence of discrete activity or status cells.",
158
+ "category": "chart",
159
+ "path": "guides/charts/tracker.md",
160
+ "components": ["Tracker"],
161
+ "useCases": ["Show service or activity status over a sequence", "Render compact status cells", "展示一组状态或活动点"],
162
+ "keywords": ["tracker", "status", "activity", "cells", "状态", "活动点", "追踪"]
163
+ },
164
+ {
165
+ "id": "heatmap",
166
+ "title": "Heatmap",
167
+ "description": "Show magnitude or density across two categorical dimensions.",
168
+ "category": "chart",
169
+ "path": "guides/charts/heatmap.md",
170
+ "components": ["Heatmap"],
171
+ "useCases": ["Show a matrix of values", "Find concentration across two categories", "展示两个分类维度的密度或强度"],
172
+ "keywords": ["heatmap", "matrix", "density", "intensity", "热力图", "矩阵", "密度"]
173
+ },
174
+ {
175
+ "id": "calendar-heatmap",
176
+ "title": "CalendarHeatmap",
177
+ "description": "Show daily activity or intensity on a calendar.",
178
+ "category": "chart",
179
+ "path": "guides/charts/calendar-heatmap.md",
180
+ "components": ["CalendarHeatmap"],
181
+ "useCases": ["Show daily activity over weeks or months", "Build a contribution calendar", "展示每日活动或贡献日历"],
182
+ "keywords": ["calendar heatmap", "daily", "contribution", "activity calendar", "日历热力图", "贡献图"]
183
+ },
184
+ {
185
+ "id": "cohort-chart",
186
+ "title": "CohortChart",
187
+ "description": "Show retention or another metric by cohort and elapsed period.",
188
+ "category": "chart",
189
+ "path": "guides/charts/cohort-chart.md",
190
+ "components": ["CohortChart"],
191
+ "useCases": ["Analyze retention by signup cohort", "Compare behavior across elapsed periods", "分析分群留存"],
192
+ "keywords": ["cohort", "retention", "signup", "period", "队列图", "留存", "分群"]
193
+ },
194
+ {
195
+ "id": "journey-sankey",
196
+ "title": "JourneySankey",
197
+ "description": "Show how entities move through ordered journey steps and where they drop off.",
198
+ "category": "chart",
199
+ "path": "guides/charts/journey-sankey.md",
200
+ "components": ["JourneySankey"],
201
+ "useCases": ["Visualize a user journey", "Compare paths through ordered steps", "展示用户旅程和路径流失"],
202
+ "keywords": ["journey", "path", "conversion", "sankey", "用户旅程", "路径", "桑基图"]
203
+ },
204
+ {
205
+ "id": "sankey",
206
+ "title": "Sankey",
207
+ "description": "Show weighted flows between arbitrary source and target nodes.",
208
+ "category": "chart",
209
+ "path": "guides/charts/sankey.md",
210
+ "components": ["Sankey"],
211
+ "useCases": ["Show transfers or flows between nodes", "Visualize weighted relationships in a network", "展示节点之间的加权流动"],
212
+ "keywords": ["sankey", "flow", "nodes", "links", "桑基图", "流量", "流向"]
213
+ },
214
+ {
215
+ "id": "treemap",
216
+ "title": "Treemap",
217
+ "description": "Show hierarchical part-to-whole values as nested rectangles.",
218
+ "category": "chart",
219
+ "path": "guides/charts/treemap.md",
220
+ "components": ["Treemap"],
221
+ "useCases": ["Show hierarchy and magnitude together", "Compare branches of a whole", "用嵌套矩形展示层级和占比"],
222
+ "keywords": ["treemap", "hierarchy", "nested rectangles", "矩形树图", "层级", "占比"]
223
+ },
224
+ {
225
+ "id": "sunburst-chart",
226
+ "title": "SunburstChart",
227
+ "description": "Show hierarchical part-to-whole values as concentric radial rings.",
228
+ "category": "chart",
229
+ "path": "guides/charts/sunburst-chart.md",
230
+ "components": ["SunburstChart"],
231
+ "useCases": ["Show hierarchy as radial rings", "Explore nested proportions", "用同心环展示层级和占比"],
232
+ "keywords": ["sunburst", "hierarchy", "radial rings", "旭日图", "层级", "环形"]
233
+ }
234
+ ]
235
+ }
@@ -0,0 +1,56 @@
1
+ # AreaChart
2
+
3
+ Use `AreaChart` for a filled trend or a stacked total over an ordered axis. Use `LineChart` when the fill would overemphasize magnitude; use `BarChart` for discrete category comparisons.
4
+
5
+ ## Data shape
6
+
7
+ - `data` is an array of flat rows on the chart root.
8
+ - `XAxis data-key` names the ordered category or time value.
9
+ - Each `Area data-key` selects one numeric series from the same row.
10
+ - Use one `Area` per series and set `name` when the legend or tooltip needs a different label.
11
+
12
+ ## Minimal example
13
+
14
+ ```vue
15
+ <script setup lang="ts">
16
+ import { Area, AreaChart, CartesianGrid, Tooltip, XAxis, YAxis } from 'vccs'
17
+
18
+ const data = [
19
+ { month: 'Jan', visitors: 186 },
20
+ { month: 'Feb', visitors: 305 },
21
+ { month: 'Mar', visitors: 237 },
22
+ ]
23
+ </script>
24
+
25
+ <template>
26
+ <AreaChart
27
+ :data="data"
28
+ :height="300"
29
+ >
30
+ <CartesianGrid />
31
+ <XAxis data-key="month" />
32
+ <YAxis />
33
+ <Tooltip :cursor="false" />
34
+ <Area
35
+ type="monotone"
36
+ data-key="visitors"
37
+ stroke="#f97316"
38
+ fill="#f97316"
39
+ :fill-opacity="0.3"
40
+ />
41
+ </AreaChart>
42
+ </template>
43
+ ```
44
+
45
+ Animation is enabled by default; retrieve `ssr-testing` before taking screenshots or asserting exact geometry.
46
+
47
+ ## Chart-specific rules
48
+
49
+ - Keep all series on the shared root `data` array; do not put a separate array on each `Area`.
50
+ - Give areas the same `stack-id` to stack them; omit it when the areas should remain independent.
51
+ - Use `connect-nulls` only when a gap should be bridged; otherwise missing values stay visible as gaps.
52
+ - Retrieve `composed-chart` when bars, lines, or scatter points must share the same axes.
53
+
54
+ ## Related guides
55
+
56
+ `installation`, `vue-api`, `typed-rows`, `ssr-testing`, `line-chart`, `bar-chart`, `composed-chart`.
@@ -0,0 +1,60 @@
1
+ # BarChart
2
+
3
+ Use `BarChart` to compare or rank values across categories. Use `BarList` for a compact HTML ranking without axes; use `LineChart` when the main point is a continuous trend.
4
+
5
+ ## Data shape
6
+
7
+ - `data` is an array of flat rows on the chart root.
8
+ - `XAxis data-key` names the category for vertical bars.
9
+ - Each `Bar data-key` selects one numeric series from the same row.
10
+ - Use one `Bar` per series and set `name` when the legend or tooltip needs a different label.
11
+
12
+ ## Minimal example
13
+
14
+ ```vue
15
+ <script setup lang="ts">
16
+ import { Bar, BarChart, CartesianGrid, Legend, Tooltip, XAxis, YAxis } from 'vccs'
17
+
18
+ const data = [
19
+ { month: 'Jan', desktop: 186, mobile: 80 },
20
+ { month: 'Feb', desktop: 305, mobile: 120 },
21
+ { month: 'Mar', desktop: 237, mobile: 160 },
22
+ ]
23
+ </script>
24
+
25
+ <template>
26
+ <BarChart
27
+ :data="data"
28
+ :height="300"
29
+ >
30
+ <CartesianGrid />
31
+ <XAxis data-key="month" />
32
+ <YAxis />
33
+ <Tooltip :cursor="false" />
34
+ <Legend />
35
+ <Bar
36
+ data-key="desktop"
37
+ name="Desktop"
38
+ fill="#f97316"
39
+ />
40
+ <Bar
41
+ data-key="mobile"
42
+ name="Mobile"
43
+ fill="#14b8a6"
44
+ />
45
+ </BarChart>
46
+ </template>
47
+ ```
48
+
49
+ Animation is enabled by default; retrieve `ssr-testing` before taking screenshots or asserting exact geometry.
50
+
51
+ ## Chart-specific rules
52
+
53
+ - Keep grouped or stacked series on the shared root `data` array, with one `Bar` per numeric field.
54
+ - Give bars the same `stack-id` to stack them; for stacks containing negative values, set `stack-offset="sign"` on `BarChart`.
55
+ - Use `layout="vertical"` for horizontal bars, with a numeric `XAxis` and a categorical `YAxis` carrying the category `data-key`.
56
+ - Use `bar-size` for a fixed bar width or `max-bar-size` for an upper limit.
57
+
58
+ ## Related guides
59
+
60
+ `installation`, `vue-api`, `typed-rows`, `ssr-testing`, `bar-list`, `line-chart`, `composed-chart`.