@katlux/block-charts 0.1.1-alpha.86 → 0.1.1-alpha.87

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/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": "^3.0.0"
6
6
  },
7
- "version": "0.1.1-alpha.86",
7
+ "version": "0.1.1-alpha.87",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
@@ -65,7 +65,7 @@ const emit = defineEmits(["click-point", "hover-point"]);
65
65
  const containerRef = ref(null);
66
66
  const canvasRef = ref(null);
67
67
  const svgRef = ref(null);
68
- const { ctx, width, height, clear, setupCanvas } = useChartCanvas();
68
+ const { ctx, width, height, resizeTick, clear, setupCanvas } = useChartCanvas();
69
69
  const { hoveredIndex, tooltipState, showTooltip, hideTooltip, setHovered } = useChartSvg();
70
70
  const dataRef = computed(() => props.dataProvider);
71
71
  const { items } = useChartData(dataRef);
@@ -137,6 +137,7 @@ const draw = () => {
137
137
  });
138
138
  if (props.showLegend) drawLegend(c);
139
139
  };
140
+ watch([items, renderedCells, hoveredIndex, width, height, resizeTick], draw, { deep: true });
140
141
  const drawAxes = (c) => {
141
142
  c.save();
142
143
  c.fillStyle = "rgba(120,120,140,0.8)";
@@ -185,7 +186,16 @@ const drawLegend = (c) => {
185
186
  c.fillText(maxVal.value.toString(), x + gradW, y + gradH + 10);
186
187
  c.restore();
187
188
  };
188
- watch([renderedCells, hoveredIndex, width, height], draw);
189
+ watch(isLoading, (val) => {
190
+ if (!val) {
191
+ nextTick(() => {
192
+ if (canvasRef.value) {
193
+ setupCanvas(canvasRef.value);
194
+ draw();
195
+ }
196
+ });
197
+ }
198
+ });
189
199
  onMounted(async () => {
190
200
  await nextTick();
191
201
  if (containerRef.value && canvasRef.value) {
@@ -34,7 +34,7 @@ export interface IChartAxesContext {
34
34
  }
35
35
  /**
36
36
  * Robustly parses a value into a number.
37
- * Handles: numbers, numeric strings, and ISO date strings.
37
+ * Handles: numbers, numeric strings, ISO date strings, and falls back to array index for unparseable category strings.
38
38
  */
39
- export declare function parseValue(val: any): number;
39
+ export declare function parseValue(val: any, fallbackIndex?: number): number;
40
40
  export declare function useChartAxes(opts: IChartAxesOptions): IChartAxesContext;
@@ -1,13 +1,14 @@
1
1
  import { computed } from "vue";
2
- export function parseValue(val) {
3
- if (val === null || val === void 0) return 0;
2
+ export function parseValue(val, fallbackIndex) {
3
+ if (val === null || val === void 0) return fallbackIndex ?? 0;
4
+ if (typeof val === "number") return isNaN(val) ? fallbackIndex ?? 0 : val;
4
5
  const n = Number(val);
5
6
  if (!isNaN(n)) return n;
6
7
  if (typeof val === "string") {
7
8
  const d = Date.parse(val);
8
9
  if (!isNaN(d)) return d;
9
10
  }
10
- return 0;
11
+ return fallbackIndex ?? 0;
11
12
  }
12
13
  export function useChartAxes(opts) {
13
14
  const pad = opts.padding ?? { top: 20, right: 20, bottom: 40, left: 55 };
@@ -16,19 +17,19 @@ export function useChartAxes(opts) {
16
17
  const plotWidth = computed(() => Math.max(0, opts.width.value - pad.left - pad.right));
17
18
  const plotHeight = computed(() => Math.max(0, opts.height.value - pad.top - pad.bottom));
18
19
  const rawXMin = computed(() => {
19
- const vals = opts.items.value.map((r) => parseValue(r[opts.xField.value]));
20
+ const vals = opts.items.value.map((r, i) => parseValue(r[opts.xField.value], i));
20
21
  return vals.length ? Math.min(...vals) : 0;
21
22
  });
22
23
  const rawXMax = computed(() => {
23
- const vals = opts.items.value.map((r) => parseValue(r[opts.xField.value]));
24
+ const vals = opts.items.value.map((r, i) => parseValue(r[opts.xField.value], i));
24
25
  return vals.length ? Math.max(...vals) : 1;
25
26
  });
26
27
  const yMin = computed(() => {
27
- const vals = opts.items.value.map((r) => parseValue(r[opts.yField.value]));
28
+ const vals = opts.items.value.map((r, i) => parseValue(r[opts.yField.value], i));
28
29
  return vals.length ? Math.min(...vals) : 0;
29
30
  });
30
31
  const yMax = computed(() => {
31
- const vals = opts.items.value.map((r) => parseValue(r[opts.yField.value]));
32
+ const vals = opts.items.value.map((r, i) => parseValue(r[opts.yField.value], i));
32
33
  return vals.length ? Math.max(...vals) : 1;
33
34
  });
34
35
  const xRange = computed(() => rawXMax.value - rawXMin.value);
@@ -5,6 +5,7 @@ export interface IChartCanvasContext {
5
5
  width: Ref<number>;
6
6
  height: Ref<number>;
7
7
  dpr: Ref<number>;
8
+ resizeTick: Ref<number>;
8
9
  clear: (color?: string) => void;
9
10
  setupCanvas: (el: HTMLCanvasElement) => void;
10
11
  }
@@ -4,32 +4,47 @@ export function useChartCanvas() {
4
4
  const ctx = ref(null);
5
5
  const width = ref(0);
6
6
  const height = ref(0);
7
- const dpr = ref(window?.devicePixelRatio ?? 1);
7
+ const dpr = ref(typeof window !== "undefined" ? window.devicePixelRatio ?? 1 : 1);
8
+ const resizeTick = ref(0);
8
9
  let resizeObserver = null;
9
10
  const applySize = (el, w, h) => {
10
- const ratio = window.devicePixelRatio ?? 1;
11
+ const ratio = typeof window !== "undefined" ? window.devicePixelRatio ?? 1 : 1;
12
+ const newPixelW = Math.round(w * ratio);
13
+ const newPixelH = Math.round(h * ratio);
11
14
  dpr.value = ratio;
12
- el.width = w * ratio;
13
- el.height = h * ratio;
14
- ctx.value?.scale(ratio, ratio);
15
- width.value = w;
16
- height.value = h;
15
+ let sizeChanged = false;
16
+ if (el.width !== newPixelW || el.height !== newPixelH) {
17
+ el.width = newPixelW;
18
+ el.height = newPixelH;
19
+ ctx.value = el.getContext("2d");
20
+ ctx.value?.scale(ratio, ratio);
21
+ sizeChanged = true;
22
+ }
23
+ if (width.value !== w || height.value !== h || sizeChanged) {
24
+ width.value = w;
25
+ height.value = h;
26
+ resizeTick.value++;
27
+ }
17
28
  };
18
29
  const setupCanvas = (el) => {
19
30
  canvas.value = el;
20
31
  ctx.value = el.getContext("2d");
21
32
  const rect = el.getBoundingClientRect();
22
- applySize(el, rect.width, rect.height);
23
- resizeObserver = new ResizeObserver((entries) => {
24
- for (const entry of entries) {
25
- const { width: w, height: h } = entry.contentRect;
26
- if (canvas.value && ctx.value) {
27
- ctx.value = canvas.value.getContext("2d");
28
- applySize(canvas.value, w, h);
33
+ const initialW = rect.width || el.clientWidth || 400;
34
+ const initialH = rect.height || el.clientHeight || 400;
35
+ applySize(el, initialW, initialH);
36
+ if (typeof ResizeObserver !== "undefined") {
37
+ resizeObserver?.disconnect();
38
+ resizeObserver = new ResizeObserver((entries) => {
39
+ for (const entry of entries) {
40
+ const { width: w, height: h } = entry.contentRect;
41
+ if (canvas.value && w > 0 && h > 0) {
42
+ applySize(canvas.value, w, h);
43
+ }
29
44
  }
30
- }
31
- });
32
- resizeObserver.observe(el);
45
+ });
46
+ resizeObserver.observe(el);
47
+ }
33
48
  };
34
49
  const clear = (color) => {
35
50
  if (ctx.value && canvas.value) {
@@ -43,5 +58,5 @@ export function useChartCanvas() {
43
58
  onUnmounted(() => {
44
59
  resizeObserver?.disconnect();
45
60
  });
46
- return { canvas, ctx, width, height, dpr, clear, setupCanvas };
61
+ return { canvas, ctx, width, height, dpr, resizeTick, clear, setupCanvas };
47
62
  }
@@ -1,4 +1,5 @@
1
1
  import { type Ref } from 'vue';
2
+ import type { ADataProvider } from '@katlux/providers';
2
3
  export interface IChartDataContext<T = any> {
3
4
  items: Ref<T[]>;
4
5
  loading: Ref<boolean>;
@@ -6,9 +7,12 @@ export interface IChartDataContext<T = any> {
6
7
  reload: () => Promise<void>;
7
8
  }
8
9
  /**
9
- * Robust data fetcher for charts. Supports:
10
- * 1. Plain arrays
11
- * 2. Katlux DataProviders (with .pageData ref)
12
- * 3. Legacy fetchable providers (with .fetch() method)
10
+ * Extracts item rows strictly from a Katlux DataProvider instance's `pageData` property.
13
11
  */
14
- export declare function useChartData<T = any>(providerOrData: Ref<any>): IChartDataContext<T>;
12
+ export declare function extractProviderItems<T = any>(provider?: ADataProvider | any): T[];
13
+ /**
14
+ * Provider-agnostic data watcher for Katlux chart components.
15
+ * Consumes data exclusively through Katlux DataProvider instances (Server-side or Client-side),
16
+ * using strictly the `pageData` property.
17
+ */
18
+ export declare function useChartData<T = any>(dataProvider: Ref<ADataProvider | null | undefined> | ADataProvider | null | undefined): IChartDataContext<T>;
@@ -1,45 +1,73 @@
1
1
  import { ref, watch, onMounted, isRef } from "vue";
2
- export function useChartData(providerOrData) {
2
+ export function extractProviderItems(provider) {
3
+ if (!provider) return [];
4
+ const instance = isRef(provider) ? provider.value : provider;
5
+ if (!instance) return [];
6
+ let raw = instance.pageData;
7
+ if (isRef(raw)) {
8
+ raw = raw.value;
9
+ }
10
+ if (!raw) return [];
11
+ if (Array.isArray(raw)) {
12
+ return raw;
13
+ }
14
+ if (Array.isArray(raw.rows)) {
15
+ return raw.rows;
16
+ }
17
+ return [];
18
+ }
19
+ export function useChartData(dataProvider) {
3
20
  const items = ref([]);
4
21
  const loading = ref(false);
5
22
  const error = ref(null);
6
- const load = async () => {
7
- const source = providerOrData.value;
8
- if (!source) {
23
+ const ensureUnpaginated = (provider) => {
24
+ if (provider && provider.pageSize && isRef(provider.pageSize) && provider.pageSize.value !== -1) {
25
+ provider.pageSize.value = -1;
26
+ }
27
+ };
28
+ const updateItems = () => {
29
+ const provider = isRef(dataProvider) ? dataProvider.value : dataProvider;
30
+ if (provider) {
31
+ ensureUnpaginated(provider);
32
+ items.value = extractProviderItems(provider);
33
+ } else {
9
34
  items.value = [];
10
- return;
11
35
  }
36
+ };
37
+ watch(
38
+ () => {
39
+ const provider = isRef(dataProvider) ? dataProvider.value : dataProvider;
40
+ if (!provider) return null;
41
+ const pd = isRef(provider.pageData) ? provider.pageData.value : provider.pageData;
42
+ return { provider, pd };
43
+ },
44
+ (newVal) => {
45
+ if (newVal?.provider) {
46
+ ensureUnpaginated(newVal.provider);
47
+ }
48
+ updateItems();
49
+ },
50
+ { immediate: true, deep: true }
51
+ );
52
+ const reload = async () => {
53
+ const provider = isRef(dataProvider) ? dataProvider.value : dataProvider;
54
+ if (!provider) return;
12
55
  loading.value = true;
13
56
  error.value = null;
14
57
  try {
15
- if (Array.isArray(source)) {
16
- items.value = source;
17
- return;
18
- }
19
- if (source.pageData) {
20
- items.value = isRef(source.pageData) ? source.pageData.value : source.pageData;
21
- return;
22
- }
23
- if (typeof source.fetch === "function") {
24
- const result = await source.fetch();
25
- items.value = result?.rows ?? result ?? [];
26
- return;
27
- }
28
- if (source.rows) {
29
- items.value = source.rows;
30
- return;
58
+ ensureUnpaginated(provider);
59
+ if (provider.refresh) {
60
+ await provider.refresh();
61
+ } else if (provider.loadPageData) {
62
+ await provider.loadPageData();
31
63
  }
64
+ updateItems();
32
65
  } catch (e) {
33
- error.value = e?.message ?? "Failed to load chart data";
66
+ error.value = e?.message ?? "Failed to reload chart provider";
34
67
  } finally {
35
68
  loading.value = false;
36
69
  }
37
70
  };
38
- const reload = load;
39
- onMounted(load);
40
- watch(providerOrData, load);
41
- watch(() => providerOrData.value?.pageData?.value, (newRows) => {
42
- if (newRows) items.value = newRows;
43
- }, { deep: true });
71
+ onMounted(updateItems);
44
72
  return { items, loading, error, reload };
45
73
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@katlux/block-charts",
3
- "version": "0.1.1-alpha.86",
3
+ "version": "0.1.1-alpha.87",
4
4
  "description": "Pre-built charting block components for Katlux toolkit",
5
5
  "author": "Katlux",
6
6
  "license": "Apache-2.0",