@katlux/block-charts 0.1.0-beta.0 → 0.1.0-beta.2

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 (55) hide show
  1. package/dist/module.d.mts +6 -0
  2. package/dist/module.json +12 -0
  3. package/dist/module.mjs +23 -0
  4. package/dist/runtime/components/KAreaChart/KAreaChart.d.vue.ts +53 -0
  5. package/dist/runtime/components/KAreaChart/KAreaChart.vue +382 -0
  6. package/dist/runtime/components/KAreaChart/KAreaChart.vue.d.ts +53 -0
  7. package/dist/runtime/components/KBarChart/KBarChart.d.vue.ts +55 -0
  8. package/dist/runtime/components/KBarChart/KBarChart.vue +398 -0
  9. package/dist/runtime/components/KBarChart/KBarChart.vue.d.ts +55 -0
  10. package/dist/runtime/components/KHeatMap/KHeatMap.d.vue.ts +36 -0
  11. package/dist/runtime/components/KHeatMap/KHeatMap.vue +263 -0
  12. package/dist/runtime/components/KHeatMap/KHeatMap.vue.d.ts +36 -0
  13. package/dist/runtime/components/KLineChart/KLineChart.d.vue.ts +51 -0
  14. package/dist/runtime/components/KLineChart/KLineChart.vue +407 -0
  15. package/dist/runtime/components/KLineChart/KLineChart.vue.d.ts +51 -0
  16. package/dist/runtime/components/KPieChart/KPieChart.d.vue.ts +32 -0
  17. package/dist/runtime/components/KPieChart/KPieChart.vue +273 -0
  18. package/dist/runtime/components/KPieChart/KPieChart.vue.d.ts +32 -0
  19. package/dist/runtime/components/KScatterChart/KScatterChart.d.vue.ts +55 -0
  20. package/dist/runtime/components/KScatterChart/KScatterChart.vue +356 -0
  21. package/dist/runtime/components/KScatterChart/KScatterChart.vue.d.ts +55 -0
  22. package/dist/runtime/composables/useChartAnimation.d.ts +9 -0
  23. package/dist/runtime/composables/useChartAnimation.js +32 -0
  24. package/dist/runtime/composables/useChartAxes.d.ts +40 -0
  25. package/dist/runtime/composables/useChartAxes.js +58 -0
  26. package/dist/runtime/composables/useChartCanvas.d.ts +11 -0
  27. package/dist/runtime/composables/useChartCanvas.js +47 -0
  28. package/dist/runtime/composables/useChartData.d.ts +14 -0
  29. package/dist/runtime/composables/useChartData.js +45 -0
  30. package/dist/runtime/composables/useChartExport.d.ts +5 -0
  31. package/dist/runtime/composables/useChartExport.js +32 -0
  32. package/dist/runtime/composables/useChartHitTest.d.ts +11 -0
  33. package/dist/runtime/composables/useChartHitTest.js +39 -0
  34. package/dist/runtime/composables/useChartSvg.d.ts +17 -0
  35. package/dist/runtime/composables/useChartSvg.js +22 -0
  36. package/dist/runtime/composables/useChartViewport.d.ts +25 -0
  37. package/dist/runtime/composables/useChartViewport.js +92 -0
  38. package/dist/types.d.mts +3 -0
  39. package/package.json +7 -3
  40. package/build.config.ts +0 -4
  41. package/src/module.ts +0 -25
  42. package/src/runtime/components/KAreaChart/KAreaChart.vue +0 -410
  43. package/src/runtime/components/KBarChart/KBarChart.vue +0 -427
  44. package/src/runtime/components/KHeatMap/KHeatMap.vue +0 -301
  45. package/src/runtime/components/KLineChart/KLineChart.vue +0 -493
  46. package/src/runtime/components/KPieChart/KPieChart.vue +0 -307
  47. package/src/runtime/components/KScatterChart/KScatterChart.vue +0 -375
  48. package/src/runtime/composables/useChartAnimation.ts +0 -45
  49. package/src/runtime/composables/useChartAxes.ts +0 -105
  50. package/src/runtime/composables/useChartCanvas.ts +0 -67
  51. package/src/runtime/composables/useChartData.ts +0 -79
  52. package/src/runtime/composables/useChartExport.ts +0 -40
  53. package/src/runtime/composables/useChartHitTest.ts +0 -71
  54. package/src/runtime/composables/useChartSvg.ts +0 -45
  55. package/src/runtime/composables/useChartViewport.ts +0 -140
@@ -0,0 +1,263 @@
1
+ <template lang="pug">
2
+ .k-chart-wrapper
3
+ .k-chart-inner(
4
+ ref="containerRef"
5
+ @mousemove="onMouseMove"
6
+ @mouseleave="onMouseLeave"
7
+ )
8
+ KLoader(:loading="isLoading" overlay)
9
+ canvas(ref="canvasRef" v-show="!isLoading")
10
+ svg.k-chart-svg(
11
+ v-show="!isLoading"
12
+ ref="svgRef"
13
+ :width="width"
14
+ :height="height"
15
+ )
16
+ // Dynamic Interaction Layer
17
+ g.k-chart-interaction(v-if="hoveredIndex !== -1 && hoveredCell")
18
+ rect.k-chart-hit-proxy(
19
+ :x="hoveredCell.x"
20
+ :y="hoveredCell.y"
21
+ :width="hoveredCell.w"
22
+ :height="hoveredCell.h"
23
+ fill="transparent"
24
+ style="cursor: pointer"
25
+ @click="emit('click-point', hoveredCell.item, hoveredIndex)"
26
+ )
27
+ rect(
28
+ :x="hoveredCell.x - 1"
29
+ :y="hoveredCell.y - 1"
30
+ :width="hoveredCell.w + 2"
31
+ :height="hoveredCell.h + 2"
32
+ fill="transparent"
33
+ stroke="#fff"
34
+ stroke-width="1.5"
35
+ rx="2"
36
+ pointer-events="none"
37
+ )
38
+
39
+ // Labels remain in SVG for sharpness and easy styling
40
+ .k-chart-tooltip(v-if="tooltipState.visible && !isLoading" :style="{ left: tooltipState.x + 'px', top: tooltipState.y + 'px' }")
41
+ slot(name="tooltip" :item="tooltipState.item" :index="tooltipState.index")
42
+ span {{ tooltipState.content }}
43
+ </template>
44
+
45
+ <script setup>
46
+ import { ref, watch, computed, onMounted, nextTick } from "vue";
47
+ import { useChartSvg } from "../../composables/useChartSvg";
48
+ import { useChartData } from "../../composables/useChartData";
49
+ import { useChartCanvas } from "../../composables/useChartCanvas";
50
+ import { useChartExport } from "../../composables/useChartExport";
51
+ const props = defineProps({
52
+ dataProvider: { type: null, required: false },
53
+ rowField: { type: String, required: false, default: "row" },
54
+ columnField: { type: String, required: false, default: "column" },
55
+ valueField: { type: String, required: false, default: "value" },
56
+ colorLow: { type: String, required: false, default: "#1e1e2e" },
57
+ colorHigh: { type: String, required: false, default: "#6366f1" },
58
+ colors: { type: Array, required: false, default: () => ["#1e1e2e", "#313244", "#45475a", "#585b70", "#cba6f7", "#f5c2e7", "#eba0ac", "#f38ba8"] },
59
+ backgroundColor: { type: String, required: false, default: "#ffffff" },
60
+ showLegend: { type: Boolean, required: false, default: true },
61
+ xAxisTitle: { type: String, required: false, default: "" },
62
+ yAxisTitle: { type: String, required: false, default: "" }
63
+ });
64
+ const emit = defineEmits(["click-point", "hover-point"]);
65
+ const containerRef = ref(null);
66
+ const canvasRef = ref(null);
67
+ const svgRef = ref(null);
68
+ const { ctx, width, height, clear, setupCanvas } = useChartCanvas();
69
+ const { hoveredIndex, tooltipState, showTooltip, hideTooltip, setHovered } = useChartSvg();
70
+ const dataRef = computed(() => props.dataProvider);
71
+ const { items } = useChartData(dataRef);
72
+ const { exportPng, exportSvg: exportSvgFile } = useChartExport();
73
+ const isLoading = computed(() => {
74
+ return props.dataProvider?.loading?.value || props.dataProvider?.initialLoad?.value || false;
75
+ });
76
+ const padLeft = 80;
77
+ const padTop = 30;
78
+ const padRight = 20;
79
+ const padBottom = 20;
80
+ const rows = computed(() => [...new Set(items.value.map((r) => String(r[props.rowField])))]);
81
+ const cols = computed(() => [...new Set(items.value.map((r) => String(r[props.columnField])))]);
82
+ const cellW = computed(() => Math.max(8, (width.value - padLeft - padRight) / (cols.value.length || 1)));
83
+ const cellH = computed(() => Math.max(8, (height.value - padTop - padBottom) / (rows.value.length || 1)));
84
+ const maxVal = computed(() => Math.max(...items.value.map((r) => Number(r[props.valueField])), 1));
85
+ const hoveredCell = computed(() => {
86
+ if (hoveredIndex.value === -1) return null;
87
+ return renderedCells.value[hoveredIndex.value];
88
+ });
89
+ const minVal = computed(() => Math.min(...items.value.map((r) => Number(r[props.valueField])), 0));
90
+ const lerp = (t) => {
91
+ const low = parseInt((props.colorLow || "#1e1e2e").replace("#", ""), 16);
92
+ const high = parseInt((props.colorHigh || "#6366f1").replace("#", ""), 16);
93
+ const lr = low >> 16 & 255;
94
+ const lg = low >> 8 & 255;
95
+ const lb = low & 255;
96
+ const hr = high >> 16 & 255;
97
+ const hg = high >> 8 & 255;
98
+ const hb = high & 255;
99
+ const r = Math.round(lr + (hr - lr) * t);
100
+ const g = Math.round(lg + (hg - lg) * t);
101
+ const b = Math.round(lb + (hb - lb) * t);
102
+ return `rgb(${r},${g},${b})`;
103
+ };
104
+ const renderedCells = computed(() => {
105
+ const range = maxVal.value - minVal.value || 1;
106
+ return items.value.map((item) => {
107
+ const ri = rows.value.indexOf(String(item[props.rowField]));
108
+ const ci = cols.value.indexOf(String(item[props.columnField]));
109
+ const t = (Number(item[props.valueField]) - minVal.value) / range;
110
+ return {
111
+ x: padLeft + ci * cellW.value,
112
+ y: padTop + ri * cellH.value,
113
+ w: cellW.value - 1,
114
+ h: cellH.value - 1,
115
+ color: lerp(t),
116
+ item,
117
+ label: `${item[props.rowField]} \xD7 ${item[props.columnField]}: ${item[props.valueField]}`
118
+ };
119
+ });
120
+ });
121
+ const draw = () => {
122
+ if (!ctx.value) return;
123
+ clear(props.backgroundColor);
124
+ const c = ctx.value;
125
+ drawAxes(c);
126
+ renderedCells.value.forEach((cell, i) => {
127
+ const isHovered = hoveredIndex.value === i;
128
+ c.fillStyle = cell.color;
129
+ c.beginPath();
130
+ c.rect(cell.x, cell.y, cell.w, cell.h);
131
+ c.fill();
132
+ if (isHovered) {
133
+ c.strokeStyle = "#fff";
134
+ c.lineWidth = 2;
135
+ c.strokeRect(cell.x + 1, cell.y + 1, cell.w - 2, cell.h - 2);
136
+ }
137
+ });
138
+ if (props.showLegend) drawLegend(c);
139
+ };
140
+ const drawAxes = (c) => {
141
+ c.save();
142
+ c.fillStyle = "rgba(120,120,140,0.8)";
143
+ c.font = "11px system-ui, sans-serif";
144
+ const plotW = width.value - padLeft - padRight;
145
+ const plotH = height.value - padTop - padBottom;
146
+ c.textAlign = "right";
147
+ rows.value.forEach((label, i) => {
148
+ const y = padTop + i * cellH.value + cellH.value / 2 + 4;
149
+ c.fillText(label, padLeft - 8, y);
150
+ });
151
+ c.textAlign = "center";
152
+ cols.value.forEach((label, i) => {
153
+ const x = padLeft + i * cellW.value + cellW.value / 2;
154
+ c.fillText(label, x, padTop + plotH + 16);
155
+ });
156
+ if (props.xAxisTitle) {
157
+ c.fillText(props.xAxisTitle, padLeft + plotW / 2, padTop + plotH + 34);
158
+ }
159
+ if (props.yAxisTitle) {
160
+ c.save();
161
+ c.translate(padLeft - 55, padTop + plotH / 2);
162
+ c.rotate(-Math.PI / 2);
163
+ c.textAlign = "center";
164
+ c.fillText(props.yAxisTitle, 0, 0);
165
+ c.restore();
166
+ }
167
+ c.restore();
168
+ };
169
+ const drawLegend = (c) => {
170
+ c.save();
171
+ const gradW = 120;
172
+ const gradH = 10;
173
+ const x = width.value - padRight - gradW;
174
+ const y = height.value - padBottom + 10;
175
+ const grad = c.createLinearGradient(x, 0, x + gradW, 0);
176
+ grad.addColorStop(0, props.colorLow || "#1e1e2e");
177
+ grad.addColorStop(1, props.colorHigh || "#6366f1");
178
+ c.fillStyle = grad;
179
+ c.fillRect(x, y, gradW, gradH);
180
+ c.fillStyle = "rgba(150,150,170,0.9)";
181
+ c.font = "10px system-ui, sans-serif";
182
+ c.textAlign = "left";
183
+ c.fillText(minVal.value.toString(), x, y + gradH + 10);
184
+ c.textAlign = "right";
185
+ c.fillText(maxVal.value.toString(), x + gradW, y + gradH + 10);
186
+ c.restore();
187
+ };
188
+ watch([renderedCells, hoveredIndex, width, height], draw);
189
+ onMounted(async () => {
190
+ await nextTick();
191
+ if (containerRef.value && canvasRef.value) {
192
+ setupCanvas(canvasRef.value);
193
+ draw();
194
+ }
195
+ });
196
+ const onMouseMove = (e) => {
197
+ const rect = containerRef.value?.getBoundingClientRect();
198
+ if (!rect) return;
199
+ const mx = e.clientX - rect.left;
200
+ const my = e.clientY - rect.top;
201
+ const idx = renderedCells.value.findIndex(
202
+ (c) => mx >= c.x && mx <= c.x + c.w && my >= c.y && my <= c.y + c.h
203
+ );
204
+ if (idx !== -1) {
205
+ const cell = renderedCells.value[idx];
206
+ setHovered(idx);
207
+ showTooltip(mx + 10, my - 10, cell.label, cell.item, idx);
208
+ emit("hover-point", cell.item, idx);
209
+ } else {
210
+ setHovered(-1);
211
+ hideTooltip();
212
+ }
213
+ };
214
+ const onMouseLeave = () => {
215
+ setHovered(-1);
216
+ hideTooltip();
217
+ };
218
+ defineExpose({
219
+ exportPng: () => canvasRef.value && exportPng(canvasRef.value, "heatmap.png"),
220
+ exportSvg: () => svgRef.value && exportSvgFile(svgRef.value, "heatmap.svg")
221
+ });
222
+ </script>
223
+
224
+ <style scoped>
225
+ .k-chart-wrapper {
226
+ position: relative;
227
+ user-select: none;
228
+ }
229
+ .k-chart-wrapper .k-chart-inner {
230
+ width: 100%;
231
+ height: 400px;
232
+ position: relative;
233
+ }
234
+ .k-chart-wrapper .k-chart-inner canvas, .k-chart-wrapper .k-chart-inner .k-chart-svg {
235
+ position: absolute;
236
+ top: 0;
237
+ left: 0;
238
+ width: 100%;
239
+ height: 100%;
240
+ display: block;
241
+ }
242
+ .k-chart-wrapper .k-chart-inner .k-chart-svg {
243
+ overflow: visible;
244
+ pointer-events: none;
245
+ }
246
+ .k-chart-wrapper .k-chart-inner .k-chart-svg .k-chart-hit-proxy {
247
+ pointer-events: all;
248
+ cursor: pointer;
249
+ }
250
+ .k-chart-wrapper .k-chart-tooltip {
251
+ position: absolute;
252
+ background: var(--bg-color-elevated, #1e1e2e);
253
+ color: var(--text-color-primary, #cdd6f4);
254
+ border: 1px solid var(--border-color-medium, #45475a);
255
+ border-radius: 8px;
256
+ padding: 6px 10px;
257
+ font-size: 12px;
258
+ pointer-events: none;
259
+ z-index: 20;
260
+ white-space: nowrap;
261
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
262
+ }
263
+ </style>
@@ -0,0 +1,36 @@
1
+ type __VLS_Props = {
2
+ dataProvider?: any;
3
+ rowField?: string;
4
+ columnField?: string;
5
+ valueField?: string;
6
+ colorLow?: string;
7
+ colorHigh?: string;
8
+ colors?: string[];
9
+ backgroundColor?: string;
10
+ showLegend?: boolean;
11
+ xAxisTitle?: string;
12
+ yAxisTitle?: string;
13
+ };
14
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
15
+ exportPng: () => void | null;
16
+ exportSvg: () => void | null;
17
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
18
+ "click-point": (item: any, index: number) => any;
19
+ "hover-point": (item: any, index: number) => any;
20
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
21
+ "onClick-point"?: ((item: any, index: number) => any) | undefined;
22
+ "onHover-point"?: ((item: any, index: number) => any) | undefined;
23
+ }>, {
24
+ colors: string[];
25
+ backgroundColor: string;
26
+ showLegend: boolean;
27
+ xAxisTitle: string;
28
+ yAxisTitle: string;
29
+ valueField: string;
30
+ rowField: string;
31
+ columnField: string;
32
+ colorLow: string;
33
+ colorHigh: string;
34
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
35
+ declare const _default: typeof __VLS_export;
36
+ export default _default;
@@ -0,0 +1,51 @@
1
+ type __VLS_Props = {
2
+ dataProvider?: any;
3
+ xField?: string;
4
+ yField?: string;
5
+ seriesField?: string;
6
+ animated?: boolean;
7
+ zoomable?: boolean;
8
+ maxZoom?: number;
9
+ colors?: string[];
10
+ backgroundColor?: string;
11
+ showLegend?: boolean;
12
+ xAxisTitle?: string;
13
+ yAxisTitle?: string;
14
+ };
15
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
16
+ zoomIn: () => void;
17
+ zoomOut: () => void;
18
+ resetZoom: () => void;
19
+ exportPng: () => void | null;
20
+ exportSvg: () => void | null;
21
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
22
+ "click-point": (item: any, index: number) => any;
23
+ "hover-point": (item: any, index: number) => any;
24
+ "zoom-change": (scale: number) => any;
25
+ "pan-change": (offset: {
26
+ x: number;
27
+ y: number;
28
+ }) => any;
29
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
30
+ "onClick-point"?: ((item: any, index: number) => any) | undefined;
31
+ "onHover-point"?: ((item: any, index: number) => any) | undefined;
32
+ "onZoom-change"?: ((scale: number) => any) | undefined;
33
+ "onPan-change"?: ((offset: {
34
+ x: number;
35
+ y: number;
36
+ }) => any) | undefined;
37
+ }>, {
38
+ xField: string;
39
+ yField: string;
40
+ seriesField: string;
41
+ animated: boolean;
42
+ zoomable: boolean;
43
+ maxZoom: number;
44
+ colors: string[];
45
+ backgroundColor: string;
46
+ showLegend: boolean;
47
+ xAxisTitle: string;
48
+ yAxisTitle: string;
49
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
50
+ declare const _default: typeof __VLS_export;
51
+ export default _default;