@epoch32/visage 1.1.2 → 1.1.4

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 (52) hide show
  1. package/dist/api/index.js +55 -4085
  2. package/dist/charts/index.js +48 -2806
  3. package/dist/devtools/index.js +18 -4766
  4. package/dist/dom/index.js +32 -1639
  5. package/dist/form/index.js +13 -1498
  6. package/dist/index-061jf06a.js +848 -0
  7. package/dist/index-0madxn60.js +442 -0
  8. package/dist/index-1xtrtz1t.js +256 -0
  9. package/dist/index-2zz0g2n2.js +212 -0
  10. package/dist/index-5s255b3w.js +303 -0
  11. package/dist/index-6kfe1nr4.js +1179 -0
  12. package/dist/index-8czhpea8.js +338 -0
  13. package/dist/index-8x8xz80v.js +727 -0
  14. package/dist/index-921pn75b.js +0 -0
  15. package/dist/index-965ryh36.js +537 -0
  16. package/dist/index-9fvarkrx.js +98 -0
  17. package/dist/index-a4xns6cc.js +219 -0
  18. package/dist/index-b5wc752a.js +1696 -0
  19. package/dist/index-df5e614s.js +334 -0
  20. package/dist/index-dyvgtp43.js +1094 -0
  21. package/dist/index-grhdtcb5.js +234 -0
  22. package/dist/index-h6tjqk6e.js +158 -0
  23. package/dist/index-js199er4.js +101 -0
  24. package/dist/index-x2rs3vab.js +124 -0
  25. package/dist/index-y9bq7h9g.js +1162 -0
  26. package/dist/index-zr6cvset.js +2092 -0
  27. package/dist/index.d.ts +1 -1
  28. package/dist/index.js +458 -11915
  29. package/dist/jsx-runtime/index.js +13 -1719
  30. package/dist/query/index.js +47 -2505
  31. package/dist/router/index.js +25 -1945
  32. package/dist/rpc/index.js +39 -3567
  33. package/dist/server/index.js +11 -1256
  34. package/dist/signals/index.js +52 -1160
  35. package/dist/style/index.js +77 -2429
  36. package/dist/template/index.js +7 -1232
  37. package/dist/ui/index.d.ts +1 -1
  38. package/dist/ui/index.js +150 -4244
  39. package/dist/ui/tabs.d.ts +23 -1
  40. package/dist/virtual/index.js +12 -2044
  41. package/dist/vite/index.js +5 -744
  42. package/package.json +2 -2
  43. package/src/dom/render.ts +20 -7
  44. package/src/index.ts +2 -0
  45. package/src/signals/computed.ts +19 -12
  46. package/src/signals/context.ts +1 -2
  47. package/src/signals/effect.ts +7 -7
  48. package/src/signals/signal.ts +16 -12
  49. package/src/signals/system.ts +50 -21
  50. package/src/ui/index.ts +2 -0
  51. package/src/ui/tabs.tsx +211 -5
  52. package/src/ui/utils.ts +1 -3
@@ -0,0 +1,1094 @@
1
+ import {
2
+ untrack2,
3
+ computed2,
4
+ resolveValue2,
5
+ onCleanup2,
6
+ createContext2,
7
+ useContext2,
8
+ signal2
9
+ } from "./index-6kfe1nr4.js";
10
+ import {
11
+ jsx2,
12
+ jsxs2
13
+ } from "./index-9fvarkrx.js";
14
+
15
+ // src/charts/context.ts
16
+ var ChartContext2 = createContext2(undefined);
17
+ function useChartContext2() {
18
+ const ctx = useContext2(ChartContext2);
19
+ if (!ctx) {
20
+ throw new Error("Visage Chart child component must be rendered within a <Chart> container.");
21
+ }
22
+ return ctx;
23
+ }
24
+
25
+ // src/charts/path.ts
26
+ function pathLine2(points, options = {}) {
27
+ if (points.length === 0)
28
+ return "";
29
+ if (points.length === 1) {
30
+ const p = points[0];
31
+ return `M ${p.x.toFixed(2)},${p.y.toFixed(2)}`;
32
+ }
33
+ const curve = options.curve ?? "linear";
34
+ if (curve === "smooth" && points.length >= 2) {
35
+ if (points.length === 2) {
36
+ const p0 = points[0];
37
+ const p1 = points[1];
38
+ return `M ${p0.x.toFixed(2)},${p0.y.toFixed(2)} L ${p1.x.toFixed(2)},${p1.y.toFixed(2)}`;
39
+ }
40
+ const n = points.length;
41
+ const dx = [];
42
+ const dy = [];
43
+ const slopes = [];
44
+ for (let i = 0;i < n - 1; i++) {
45
+ const pA = points[i];
46
+ const pB = points[i + 1];
47
+ const deltaX = pB.x - pA.x;
48
+ const deltaY = pB.y - pA.y;
49
+ dx.push(deltaX);
50
+ dy.push(deltaY);
51
+ slopes.push(deltaX === 0 ? 0 : deltaY / deltaX);
52
+ }
53
+ const m = new Array(n).fill(0);
54
+ m[0] = slopes[0];
55
+ m[n - 1] = slopes[n - 2];
56
+ for (let i = 1;i < n - 1; i++) {
57
+ const sPrev = slopes[i - 1];
58
+ const sNext = slopes[i];
59
+ if (sPrev * sNext <= 0) {
60
+ m[i] = 0;
61
+ } else {
62
+ const dxPrev = dx[i - 1];
63
+ const dxNext = dx[i];
64
+ const common = dxPrev + dxNext;
65
+ m[i] = common === 0 ? 0 : 3 * common / ((common + dxNext) / sPrev + (common + dxPrev) / sNext);
66
+ }
67
+ }
68
+ let d = `M ${points[0].x.toFixed(2)},${points[0].y.toFixed(2)}`;
69
+ for (let i = 0;i < n - 1; i++) {
70
+ const p1 = points[i];
71
+ const p2 = points[i + 1];
72
+ const deltaX = dx[i];
73
+ const cp1x = p1.x + deltaX / 3;
74
+ const cp1y = p1.y + m[i] * deltaX / 3;
75
+ const cp2x = p2.x - deltaX / 3;
76
+ const cp2y = p2.y - m[i + 1] * deltaX / 3;
77
+ d += ` C ${cp1x.toFixed(2)},${cp1y.toFixed(2)} ${cp2x.toFixed(2)},${cp2y.toFixed(2)} ${p2.x.toFixed(2)},${p2.y.toFixed(2)}`;
78
+ }
79
+ return d;
80
+ }
81
+ if (curve === "step") {
82
+ let d = `M ${points[0].x.toFixed(2)},${points[0].y.toFixed(2)}`;
83
+ for (let i = 0;i < points.length - 1; i++) {
84
+ const p1 = points[i];
85
+ const p2 = points[i + 1];
86
+ const midX = (p1.x + p2.x) / 2;
87
+ d += ` L ${midX.toFixed(2)},${p1.y.toFixed(2)} L ${midX.toFixed(2)},${p2.y.toFixed(2)} L ${p2.x.toFixed(2)},${p2.y.toFixed(2)}`;
88
+ }
89
+ return d;
90
+ }
91
+ let d = `M ${points[0].x.toFixed(2)},${points[0].y.toFixed(2)}`;
92
+ for (let i = 1;i < points.length; i++) {
93
+ d += ` L ${points[i].x.toFixed(2)},${points[i].y.toFixed(2)}`;
94
+ }
95
+ return d;
96
+ }
97
+ function pathArea2(points, baselineY, options = {}) {
98
+ if (points.length === 0)
99
+ return "";
100
+ const lineD = pathLine2(points, options);
101
+ if (!lineD)
102
+ return "";
103
+ const first = points[0];
104
+ const last = points[points.length - 1];
105
+ return `${lineD} L ${last.x.toFixed(2)},${baselineY.toFixed(2)} L ${first.x.toFixed(2)},${baselineY.toFixed(2)} Z`;
106
+ }
107
+ function pathArc2(options) {
108
+ const { cx, cy, innerRadius, outerRadius, startAngle, endAngle } = options;
109
+ const cosStart = Math.cos(startAngle);
110
+ const sinStart = Math.sin(startAngle);
111
+ const cosEnd = Math.cos(endAngle);
112
+ const sinEnd = Math.sin(endAngle);
113
+ const x0 = cx + outerRadius * cosStart;
114
+ const y0 = cy + outerRadius * sinStart;
115
+ const x1 = cx + outerRadius * cosEnd;
116
+ const y1 = cy + outerRadius * sinEnd;
117
+ const largeArcFlag = endAngle - startAngle <= Math.PI ? 0 : 1;
118
+ if (innerRadius <= 0) {
119
+ return `M ${cx.toFixed(2)},${cy.toFixed(2)} L ${x0.toFixed(2)},${y0.toFixed(2)} A ${outerRadius.toFixed(2)},${outerRadius.toFixed(2)} 0 ${largeArcFlag} 1 ${x1.toFixed(2)},${y1.toFixed(2)} Z`;
120
+ }
121
+ const x2 = cx + innerRadius * cosEnd;
122
+ const y2 = cy + innerRadius * sinEnd;
123
+ const x3 = cx + innerRadius * cosStart;
124
+ const y3 = cy + innerRadius * sinStart;
125
+ return `M ${x0.toFixed(2)},${y0.toFixed(2)} A ${outerRadius.toFixed(2)},${outerRadius.toFixed(2)} 0 ${largeArcFlag} 1 ${x1.toFixed(2)},${y1.toFixed(2)} L ${x2.toFixed(2)},${y2.toFixed(2)} A ${innerRadius.toFixed(2)},${innerRadius.toFixed(2)} 0 ${largeArcFlag} 0 ${x3.toFixed(2)},${y3.toFixed(2)} Z`;
126
+ }
127
+
128
+ // src/charts/Area.tsx
129
+ function Area2(props) {
130
+ return () => {
131
+ const ctx = useChartContext2();
132
+ const stroke = props.stroke ?? "#a855f7";
133
+ const strokeWidth = props.strokeWidth ?? 2;
134
+ const fill = props.fill ?? "rgba(168, 85, 247, 0.15)";
135
+ const fillOpacity = props.fillOpacity ?? 1;
136
+ const curve = props.curve ?? "smooth";
137
+ const id = `area-${props.dataKey}-${Math.random().toString(36).slice(2, 7)}`;
138
+ ctx.registerSeries({
139
+ id,
140
+ dataKey: props.dataKey,
141
+ type: "area",
142
+ color: stroke,
143
+ name: props.name ?? props.dataKey
144
+ });
145
+ onCleanup2(() => {
146
+ ctx.unregisterSeries(id);
147
+ });
148
+ return /* @__PURE__ */ jsx2("g", {
149
+ class: `visage-area-${props.dataKey}`,
150
+ opacity: props.opacity ?? 1,
151
+ children: () => {
152
+ const dataArr = ctx.data();
153
+ if (dataArr.length === 0)
154
+ return null;
155
+ const xScale = ctx.xScale();
156
+ const yScale = ctx.yScale();
157
+ const baselineY = ctx.innerHeight();
158
+ const points = [];
159
+ for (let i = 0;i < dataArr.length; i++) {
160
+ const item = dataArr[i];
161
+ const val = item[props.dataKey];
162
+ if (typeof val !== "number" || Number.isNaN(val))
163
+ continue;
164
+ let x = 0;
165
+ if (xScale.type === "band") {
166
+ x = xScale(i) + xScale.bandwidth() / 2;
167
+ } else {
168
+ x = xScale(i);
169
+ }
170
+ const y = yScale(val);
171
+ points.push({ x, y });
172
+ }
173
+ const areaD = pathArea2(points, baselineY, { curve });
174
+ const lineD = pathLine2(points, { curve });
175
+ const dots = [];
176
+ if (props.dot && points.length > 0) {
177
+ const dotCfg = typeof props.dot === "object" ? props.dot : {};
178
+ const r = dotCfg.r ?? 3.5;
179
+ const dotFill = dotCfg.fill ?? "#ffffff";
180
+ const dotStroke = dotCfg.stroke ?? stroke;
181
+ for (let i = 0;i < points.length; i++) {
182
+ const p = points[i];
183
+ dots.push(/* @__PURE__ */ jsx2("circle", {
184
+ cx: p.x,
185
+ cy: p.y,
186
+ r,
187
+ fill: dotFill,
188
+ stroke: dotStroke,
189
+ "stroke-width": 2
190
+ }));
191
+ }
192
+ }
193
+ return [
194
+ /* @__PURE__ */ jsx2("path", {
195
+ d: areaD,
196
+ fill,
197
+ "fill-opacity": fillOpacity
198
+ }),
199
+ /* @__PURE__ */ jsx2("path", {
200
+ d: lineD,
201
+ fill: "none",
202
+ stroke,
203
+ "stroke-width": strokeWidth,
204
+ "stroke-linejoin": "round",
205
+ "stroke-linecap": "round"
206
+ }),
207
+ ...dots
208
+ ];
209
+ }
210
+ });
211
+ };
212
+ }
213
+
214
+ // src/charts/Axis.tsx
215
+ function XAxis2(props) {
216
+ return () => {
217
+ const ctx = useChartContext2();
218
+ if (props.dataKey) {
219
+ ctx.setXDataKey(props.dataKey);
220
+ onCleanup2(() => {
221
+ if (ctx.xDataKey() === props.dataKey) {
222
+ ctx.setXDataKey("");
223
+ }
224
+ });
225
+ }
226
+ const stroke = props.stroke ?? "#94a3b8";
227
+ const fontSize = props.fontSize ?? 11;
228
+ const hideLine = props.hideLine ?? false;
229
+ const w = ctx.innerWidth();
230
+ const h = ctx.innerHeight();
231
+ const xScale = ctx.xScale();
232
+ const dataArr = ctx.data();
233
+ const elements = [];
234
+ if (!hideLine) {
235
+ elements.push(/* @__PURE__ */ jsx2("line", {
236
+ x1: 0,
237
+ y1: h,
238
+ x2: w,
239
+ y2: h,
240
+ stroke,
241
+ "stroke-width": 1
242
+ }));
243
+ }
244
+ if (xScale.type === "band") {
245
+ const ticks = xScale.ticks();
246
+ const bw = xScale.bandwidth();
247
+ for (let i = 0;i < ticks.length; i++) {
248
+ const val = ticks[i];
249
+ const x = xScale(i) + bw / 2;
250
+ const text = props.tickFormat ? props.tickFormat(val, i) : String(val);
251
+ elements.push(/* @__PURE__ */ jsx2("text", {
252
+ x,
253
+ y: h + 16,
254
+ "text-anchor": "middle",
255
+ "font-size": fontSize,
256
+ fill: stroke,
257
+ children: text
258
+ }));
259
+ }
260
+ } else {
261
+ const count = props.tickCount ?? 5;
262
+ const ticks = xScale.ticks(count);
263
+ for (let i = 0;i < ticks.length; i++) {
264
+ const tickIdx = ticks[i];
265
+ const x = xScale(tickIdx);
266
+ const idx = Math.max(0, Math.min(dataArr.length - 1, Math.round(tickIdx)));
267
+ const rawItem = dataArr[idx];
268
+ const rawVal = props.dataKey && rawItem ? rawItem[props.dataKey] : idx;
269
+ const text = props.tickFormat ? props.tickFormat(rawVal, i) : String(rawVal ?? idx);
270
+ elements.push(/* @__PURE__ */ jsx2("text", {
271
+ x,
272
+ y: h + 16,
273
+ "text-anchor": "middle",
274
+ "font-size": fontSize,
275
+ fill: stroke,
276
+ children: text
277
+ }));
278
+ }
279
+ }
280
+ return /* @__PURE__ */ jsx2("g", {
281
+ class: "visage-x-axis",
282
+ children: elements
283
+ });
284
+ };
285
+ }
286
+ function YAxis2(props) {
287
+ return () => {
288
+ const ctx = useChartContext2();
289
+ const stroke = props.stroke ?? "#94a3b8";
290
+ const fontSize = props.fontSize ?? 11;
291
+ const hideLine = props.hideLine ?? true;
292
+ const unit = props.unit ?? "";
293
+ const h = ctx.innerHeight();
294
+ const yScale = ctx.yScale();
295
+ const ticks = yScale.ticks(props.tickCount ?? 5);
296
+ const elements = [];
297
+ if (!hideLine) {
298
+ elements.push(/* @__PURE__ */ jsx2("line", {
299
+ x1: 0,
300
+ y1: 0,
301
+ x2: 0,
302
+ y2: h,
303
+ stroke,
304
+ "stroke-width": 1
305
+ }));
306
+ }
307
+ for (let i = 0;i < ticks.length; i++) {
308
+ const val = ticks[i];
309
+ const y = yScale(val);
310
+ const formatted = props.tickFormat ? props.tickFormat(val, i) : unit ? `${val}${unit}` : String(val);
311
+ elements.push(/* @__PURE__ */ jsx2("text", {
312
+ x: -8,
313
+ y: y + 4,
314
+ "text-anchor": "end",
315
+ "font-size": fontSize,
316
+ fill: stroke,
317
+ children: formatted
318
+ }));
319
+ }
320
+ return /* @__PURE__ */ jsx2("g", {
321
+ class: "visage-y-axis",
322
+ children: elements
323
+ });
324
+ };
325
+ }
326
+
327
+ // src/charts/Bar.tsx
328
+ function Bar2(props) {
329
+ return () => {
330
+ const ctx = useChartContext2();
331
+ const fill = props.fill ?? "#6366f1";
332
+ const radius = props.radius ?? 4;
333
+ const id = `bar-${props.dataKey}-${Math.random().toString(36).slice(2, 7)}`;
334
+ ctx.registerSeries({
335
+ id,
336
+ dataKey: props.dataKey,
337
+ type: "bar",
338
+ color: fill,
339
+ name: props.name ?? props.dataKey
340
+ });
341
+ onCleanup2(() => {
342
+ ctx.unregisterSeries(id);
343
+ });
344
+ return /* @__PURE__ */ jsx2("g", {
345
+ class: `visage-bar-${props.dataKey}`,
346
+ opacity: props.opacity ?? 1,
347
+ children: () => {
348
+ const dataArr = ctx.data();
349
+ if (dataArr.length === 0)
350
+ return null;
351
+ const xScale = ctx.xScale();
352
+ const yScale = ctx.yScale();
353
+ const baselineY = ctx.innerHeight();
354
+ const allBars = ctx.series().filter((s) => s.type === "bar");
355
+ const barIndex = allBars.findIndex((s) => s.id === id);
356
+ const totalBars = Math.max(1, allBars.length);
357
+ const bars = [];
358
+ for (let i = 0;i < dataArr.length; i++) {
359
+ const item = dataArr[i];
360
+ const val = item[props.dataKey];
361
+ if (typeof val !== "number" || Number.isNaN(val))
362
+ continue;
363
+ let slotX = 0;
364
+ let slotW = 20;
365
+ if (xScale.type === "band") {
366
+ slotX = xScale(i);
367
+ slotW = xScale.bandwidth();
368
+ } else {
369
+ const stepW = ctx.innerWidth() / Math.max(1, dataArr.length);
370
+ slotX = xScale(i) - stepW / 2;
371
+ slotW = stepW * 0.8;
372
+ }
373
+ const subBarW = slotW / totalBars;
374
+ const barW = props.maxWidth ? Math.min(props.maxWidth, subBarW) : subBarW;
375
+ const barX = slotX + (barIndex >= 0 ? barIndex * subBarW : 0) + (subBarW - barW) / 2;
376
+ const y = yScale(val);
377
+ const h = Math.max(0, baselineY - y);
378
+ bars.push(/* @__PURE__ */ jsx2("rect", {
379
+ x: barX,
380
+ y,
381
+ width: barW,
382
+ height: h,
383
+ rx: radius,
384
+ ry: radius,
385
+ fill
386
+ }));
387
+ }
388
+ return bars;
389
+ }
390
+ });
391
+ };
392
+ }
393
+
394
+ // src/charts/CartesianGrid.tsx
395
+ function CartesianGrid2(props) {
396
+ return () => {
397
+ const ctx = useChartContext2();
398
+ const horizontal = props.horizontal ?? true;
399
+ const vertical = props.vertical ?? false;
400
+ const stroke = props.stroke ?? "#e2e8f0";
401
+ const strokeWidth = props.strokeWidth ?? 1;
402
+ const strokeDasharray = props.strokeDasharray;
403
+ const opacity = props.opacity ?? 0.8;
404
+ const lines = [];
405
+ const w = ctx.innerWidth();
406
+ const h = ctx.innerHeight();
407
+ const yScale = ctx.yScale();
408
+ const xScale = ctx.xScale();
409
+ if (horizontal) {
410
+ const ticks = yScale.ticks(5);
411
+ for (const t of ticks) {
412
+ const y = yScale(t);
413
+ lines.push(/* @__PURE__ */ jsx2("line", {
414
+ x1: 0,
415
+ y1: y,
416
+ x2: w,
417
+ y2: y,
418
+ stroke,
419
+ "stroke-width": strokeWidth,
420
+ "stroke-dasharray": strokeDasharray
421
+ }));
422
+ }
423
+ }
424
+ if (vertical) {
425
+ const ticks = xScale.ticks(5);
426
+ for (const t of ticks) {
427
+ const x = xScale(t);
428
+ lines.push(/* @__PURE__ */ jsx2("line", {
429
+ x1: x,
430
+ y1: 0,
431
+ x2: x,
432
+ y2: h,
433
+ stroke,
434
+ "stroke-width": strokeWidth,
435
+ "stroke-dasharray": strokeDasharray
436
+ }));
437
+ }
438
+ }
439
+ return /* @__PURE__ */ jsx2("g", {
440
+ class: "visage-cartesian-grid",
441
+ opacity,
442
+ children: lines
443
+ });
444
+ };
445
+ }
446
+
447
+ // src/charts/scale.ts
448
+ function generateNiceTicks2(min, max, count = 5) {
449
+ if (min === max)
450
+ return [min];
451
+ if (count <= 0)
452
+ return [];
453
+ const span = max - min;
454
+ const rawStep = span / count;
455
+ const magnitude = 10 ** Math.floor(Math.log10(rawStep));
456
+ const err = rawStep / magnitude;
457
+ let step;
458
+ if (err >= 7.5) {
459
+ step = 10 * magnitude;
460
+ } else if (err >= 3.5) {
461
+ step = 5 * magnitude;
462
+ } else if (err >= 1.5) {
463
+ step = 2 * magnitude;
464
+ } else {
465
+ step = 1 * magnitude;
466
+ }
467
+ const start = Math.floor(min / step) * step;
468
+ const end = Math.ceil(max / step) * step;
469
+ const ticks = [];
470
+ for (let current = start;current <= end + step * 0.5; current += step) {
471
+ const rounded = Number(current.toPrecision(12));
472
+ ticks.push(rounded);
473
+ }
474
+ return ticks;
475
+ }
476
+ function createLinearScale2(domain, range, options = {}) {
477
+ let [d0, d1] = domain;
478
+ const [r0, r1] = range;
479
+ if (options.nice && d0 !== d1) {
480
+ const ticks = generateNiceTicks2(d0, d1, 5);
481
+ if (ticks.length > 0) {
482
+ d0 = Math.min(d0, ticks[0]);
483
+ d1 = Math.max(d1, ticks[ticks.length - 1]);
484
+ }
485
+ }
486
+ const fn = (val) => {
487
+ let v = val;
488
+ if (options.clamp) {
489
+ const minD = Math.min(d0, d1);
490
+ const maxD = Math.max(d0, d1);
491
+ v = Math.max(minD, Math.min(maxD, v));
492
+ }
493
+ const ratio = d1 === d0 ? 0 : (v - d0) / (d1 - d0);
494
+ return r0 + ratio * (r1 - r0);
495
+ };
496
+ fn.type = "linear";
497
+ fn.domain = [d0, d1];
498
+ fn.range = [r0, r1];
499
+ fn.invert = (pixel) => {
500
+ const ratio = r1 === r0 ? 0 : (pixel - r0) / (r1 - r0);
501
+ return d0 + ratio * (d1 - d0);
502
+ };
503
+ fn.ticks = (count = 5) => {
504
+ return generateNiceTicks2(d0, d1, count);
505
+ };
506
+ return fn;
507
+ }
508
+ function createBandScale2(domain, range, options = {}) {
509
+ const paddingInner = options.paddingInner ?? 0.2;
510
+ const paddingOuter = options.paddingOuter ?? 0.1;
511
+ const n = domain.length;
512
+ const totalSteps = Math.max(1, n - paddingInner + 2 * paddingOuter);
513
+ const step = (range[1] - range[0]) / totalSteps;
514
+ const bandwidth = step * (1 - paddingInner);
515
+ const fn = (val) => {
516
+ let idx = -1;
517
+ if (typeof val === "number" && Number.isInteger(val) && val >= 0 && val < domain.length) {
518
+ idx = val;
519
+ } else {
520
+ idx = domain.indexOf(val);
521
+ }
522
+ if (idx === -1)
523
+ return range[0];
524
+ return range[0] + (idx + paddingOuter) * step;
525
+ };
526
+ fn.type = "band";
527
+ fn.domain = [...domain];
528
+ fn.range = [range[0], range[1]];
529
+ fn.bandwidth = () => bandwidth;
530
+ fn.step = () => step;
531
+ fn.ticks = () => [...domain];
532
+ return fn;
533
+ }
534
+
535
+ // src/charts/Chart.tsx
536
+ var DEFAULT_MARGIN = {
537
+ top: 16,
538
+ right: 16,
539
+ bottom: 24,
540
+ left: 36
541
+ };
542
+ function Chart2(props) {
543
+ const margin = {
544
+ ...DEFAULT_MARGIN,
545
+ ...props.margin
546
+ };
547
+ const initialW = typeof props.width === "number" ? props.width : 500;
548
+ const initialH = typeof props.height === "number" ? props.height : 300;
549
+ const measuredWidth = signal2(initialW);
550
+ const measuredHeight = signal2(initialH);
551
+ let containerEl = null;
552
+ const handleRef = (el) => {
553
+ containerEl = el;
554
+ if (typeof ResizeObserver !== "undefined") {
555
+ const ro = new ResizeObserver((entries) => {
556
+ for (const entry of entries) {
557
+ const cr = entry.contentRect;
558
+ if (cr.width > 0)
559
+ measuredWidth.set(cr.width);
560
+ if (cr.height > 0)
561
+ measuredHeight.set(cr.height);
562
+ }
563
+ });
564
+ ro.observe(el);
565
+ onCleanup2(() => ro.disconnect());
566
+ } else {
567
+ if (el.clientWidth > 0)
568
+ measuredWidth.set(el.clientWidth);
569
+ if (el.clientHeight > 0)
570
+ measuredHeight.set(el.clientHeight);
571
+ }
572
+ };
573
+ const innerWidth = computed2(() => Math.max(10, measuredWidth() - margin.left - margin.right));
574
+ const innerHeight = computed2(() => Math.max(10, measuredHeight() - margin.top - margin.bottom));
575
+ const rawData = () => resolveValue2(props.data) || [];
576
+ const seriesList = signal2([]);
577
+ const xDataKey = signal2(undefined);
578
+ const registerSeries = (meta) => {
579
+ untrack2(() => {
580
+ seriesList.update((prev) => {
581
+ if (prev.some((s) => s.id === meta.id))
582
+ return prev;
583
+ return [...prev, meta];
584
+ });
585
+ });
586
+ };
587
+ const unregisterSeries = (id) => {
588
+ untrack2(() => {
589
+ seriesList.update((prev) => prev.filter((s) => s.id !== id));
590
+ });
591
+ };
592
+ const hoverState = signal2({
593
+ active: false,
594
+ x: 0,
595
+ y: 0,
596
+ index: -1
597
+ });
598
+ const xScale = computed2(() => {
599
+ const dataArr = rawData();
600
+ const w = innerWidth();
601
+ const key = xDataKey();
602
+ const hasBarSeries = untrack2(() => seriesList().some((s) => s.type === "bar"));
603
+ if (hasBarSeries && key && dataArr.length > 0) {
604
+ const categories = dataArr.map((d) => String(d[key]));
605
+ return createBandScale2(categories, [0, w], {
606
+ paddingInner: 0.1,
607
+ paddingOuter: 0.05
608
+ });
609
+ }
610
+ const maxIdx = Math.max(1, dataArr.length - 1);
611
+ return createLinearScale2([0, maxIdx], [0, w]);
612
+ });
613
+ const yScale = computed2(() => {
614
+ const h = innerHeight();
615
+ const dataArr = rawData();
616
+ const currentSeries = seriesList();
617
+ let minY = 0;
618
+ let maxY = 10;
619
+ if (props.yDomain) {
620
+ minY = props.yDomain[0];
621
+ maxY = props.yDomain[1];
622
+ } else if (dataArr.length > 0 && currentSeries.length > 0) {
623
+ let foundMin = Infinity;
624
+ let foundMax = -Infinity;
625
+ for (const item of dataArr) {
626
+ for (const s of currentSeries) {
627
+ const val = item[s.dataKey];
628
+ if (typeof val === "number" && !Number.isNaN(val)) {
629
+ if (val < foundMin)
630
+ foundMin = val;
631
+ if (val > foundMax)
632
+ foundMax = val;
633
+ }
634
+ }
635
+ }
636
+ if (foundMax > -Infinity) {
637
+ minY = Math.min(0, foundMin);
638
+ maxY = foundMax === 0 ? 1 : foundMax * 1.1;
639
+ }
640
+ }
641
+ return createLinearScale2([minY, maxY], [h, 0], { nice: true });
642
+ });
643
+ const handlePointer = (clientX, clientY) => {
644
+ if (!containerEl)
645
+ return;
646
+ const rect = containerEl.getBoundingClientRect();
647
+ const relX = clientX - rect.left - margin.left;
648
+ const relY = clientY - rect.top - margin.top;
649
+ const w = innerWidth();
650
+ const h = innerHeight();
651
+ if (relX < 0 || relX > w || relY < 0 || relY > h) {
652
+ hoverState.set({ active: false, x: 0, y: 0, index: -1 });
653
+ return;
654
+ }
655
+ const dataArr = rawData();
656
+ if (dataArr.length === 0)
657
+ return;
658
+ const count = dataArr.length;
659
+ const stepW = w / Math.max(1, count - 1);
660
+ const rawIdx = Math.round(relX / stepW);
661
+ const clampedIdx = Math.max(0, Math.min(count - 1, rawIdx));
662
+ hoverState.set({
663
+ active: true,
664
+ x: relX + margin.left,
665
+ y: relY + margin.top,
666
+ index: clampedIdx,
667
+ datum: dataArr[clampedIdx]
668
+ });
669
+ };
670
+ const handleMouseMove = (e) => {
671
+ handlePointer(e.clientX, e.clientY);
672
+ };
673
+ const handleMouseLeave = () => {
674
+ hoverState.set({ active: false, x: 0, y: 0, index: -1 });
675
+ };
676
+ const handleTouchMove = (e) => {
677
+ const touch = e.touches[0];
678
+ if (touch)
679
+ handlePointer(touch.clientX, touch.clientY);
680
+ };
681
+ const handleTouchEnd = () => {
682
+ hoverState.set({ active: false, x: 0, y: 0, index: -1 });
683
+ };
684
+ const ctxValue = {
685
+ data: rawData,
686
+ width: () => measuredWidth(),
687
+ height: () => measuredHeight(),
688
+ margin,
689
+ innerWidth: () => innerWidth(),
690
+ innerHeight: () => innerHeight(),
691
+ xScale: () => xScale(),
692
+ yScale: () => yScale(),
693
+ hoverState: () => hoverState(),
694
+ setHoverState: (s) => hoverState.set(s),
695
+ xDataKey: () => xDataKey(),
696
+ setXDataKey: (k) => xDataKey.set(k),
697
+ series: () => seriesList(),
698
+ registerSeries,
699
+ unregisterSeries
700
+ };
701
+ const containerStyle = {
702
+ position: "relative",
703
+ width: typeof props.width === "number" ? `${props.width}px` : props.width ?? "100%",
704
+ height: typeof props.height === "number" ? `${props.height}px` : props.height ?? "300px",
705
+ touchAction: "none",
706
+ userSelect: "none",
707
+ ...props.style
708
+ };
709
+ const rawChildren = Array.isArray(props.children) ? props.children : [props.children];
710
+ const childrenList = [];
711
+ const stack = [...rawChildren];
712
+ while (stack.length > 0) {
713
+ const item = stack.shift();
714
+ if (Array.isArray(item)) {
715
+ stack.unshift(...item);
716
+ } else if (item !== null && item !== undefined && item !== false) {
717
+ childrenList.push(item);
718
+ }
719
+ }
720
+ const svgChildren = [];
721
+ const overlayChildren = [];
722
+ for (const child of childrenList) {
723
+ if (typeof child === "function" && child.__visage_overlay) {
724
+ overlayChildren.push(child);
725
+ } else {
726
+ svgChildren.push(child);
727
+ }
728
+ }
729
+ return ChartContext2.Provider({
730
+ value: ctxValue,
731
+ children: () => /* @__PURE__ */ jsxs2("div", {
732
+ ref: handleRef,
733
+ class: props.class || props.className,
734
+ style: containerStyle,
735
+ onMouseMove: handleMouseMove,
736
+ onMouseLeave: handleMouseLeave,
737
+ onTouchMove: handleTouchMove,
738
+ onTouchEnd: handleTouchEnd,
739
+ children: [
740
+ /* @__PURE__ */ jsxs2("svg", {
741
+ role: "img",
742
+ "aria-label": props["aria-label"] ?? "Data visualization chart",
743
+ viewBox: () => `0 0 ${measuredWidth()} ${measuredHeight()}`,
744
+ width: "100%",
745
+ height: "100%",
746
+ style: { overflow: "visible", display: "block" },
747
+ children: [
748
+ /* @__PURE__ */ jsx2("title", {
749
+ children: props.title ?? "Data Visualization Chart"
750
+ }),
751
+ /* @__PURE__ */ jsx2("g", {
752
+ transform: `translate(${margin.left}, ${margin.top})`,
753
+ children: svgChildren
754
+ })
755
+ ]
756
+ }),
757
+ overlayChildren
758
+ ]
759
+ })
760
+ });
761
+ }
762
+
763
+ // src/charts/Cursor.tsx
764
+ function ChartCursor2(props) {
765
+ return () => {
766
+ const ctx = useChartContext2();
767
+ const stroke = props.stroke ?? "#94a3b8";
768
+ const strokeWidth = props.strokeWidth ?? 1;
769
+ const strokeDasharray = props.strokeDasharray ?? "4 4";
770
+ const opacity = props.opacity ?? 0.8;
771
+ const hover = ctx.hoverState();
772
+ if (!hover.active)
773
+ return null;
774
+ const h = ctx.innerHeight();
775
+ const x = hover.x - ctx.margin.left;
776
+ return /* @__PURE__ */ jsx2("g", {
777
+ class: "visage-chart-cursor",
778
+ children: /* @__PURE__ */ jsx2("line", {
779
+ x1: x,
780
+ y1: 0,
781
+ x2: x,
782
+ y2: h,
783
+ stroke,
784
+ "stroke-width": strokeWidth,
785
+ "stroke-dasharray": strokeDasharray,
786
+ opacity
787
+ })
788
+ });
789
+ };
790
+ }
791
+
792
+ // src/charts/Legend.tsx
793
+ function ChartLegend2(props) {
794
+ const render = () => {
795
+ const ctx = useChartContext2();
796
+ const align = props.align ?? "center";
797
+ const justifyMap = {
798
+ left: "flex-start",
799
+ center: "center",
800
+ right: "flex-end"
801
+ };
802
+ const series = ctx.series();
803
+ return /* @__PURE__ */ jsx2("div", {
804
+ class: props.class || props.className,
805
+ style: {
806
+ display: "flex",
807
+ alignItems: "center",
808
+ justifyContent: justifyMap[align],
809
+ gap: "16px",
810
+ paddingTop: "8px",
811
+ fontSize: "12px",
812
+ color: "#475569"
813
+ },
814
+ children: series.map((s) => /* @__PURE__ */ jsxs2("div", {
815
+ style: { display: "flex", alignItems: "center", gap: "6px" },
816
+ children: [
817
+ /* @__PURE__ */ jsx2("span", {
818
+ style: {
819
+ width: "10px",
820
+ height: "10px",
821
+ borderRadius: "3px",
822
+ background: s.color ?? "#8b5cf6"
823
+ }
824
+ }),
825
+ /* @__PURE__ */ jsx2("span", {
826
+ children: s.name ?? s.dataKey
827
+ })
828
+ ]
829
+ }))
830
+ });
831
+ };
832
+ render.__visage_overlay = true;
833
+ return render;
834
+ }
835
+ var Legend2 = ChartLegend2;
836
+
837
+ // src/charts/Line.tsx
838
+ function Line2(props) {
839
+ return () => {
840
+ const ctx = useChartContext2();
841
+ const stroke = props.stroke ?? "#8b5cf6";
842
+ const strokeWidth = props.strokeWidth ?? 2;
843
+ const curve = props.curve ?? "linear";
844
+ const id = `line-${props.dataKey}-${Math.random().toString(36).slice(2, 7)}`;
845
+ ctx.registerSeries({
846
+ id,
847
+ dataKey: props.dataKey,
848
+ type: "line",
849
+ color: stroke,
850
+ name: props.name ?? props.dataKey
851
+ });
852
+ onCleanup2(() => {
853
+ ctx.unregisterSeries(id);
854
+ });
855
+ return /* @__PURE__ */ jsx2("g", {
856
+ class: `visage-line-${props.dataKey}`,
857
+ opacity: props.opacity ?? 1,
858
+ children: () => {
859
+ const dataArr = ctx.data();
860
+ if (dataArr.length === 0)
861
+ return null;
862
+ const xScale = ctx.xScale();
863
+ const yScale = ctx.yScale();
864
+ const points = [];
865
+ for (let i = 0;i < dataArr.length; i++) {
866
+ const item = dataArr[i];
867
+ const val = item[props.dataKey];
868
+ if (typeof val !== "number" || Number.isNaN(val))
869
+ continue;
870
+ let x = 0;
871
+ if (xScale.type === "band") {
872
+ x = xScale(i) + xScale.bandwidth() / 2;
873
+ } else {
874
+ x = xScale(i);
875
+ }
876
+ const y = yScale(val);
877
+ points.push({ x, y });
878
+ }
879
+ const d = pathLine2(points, { curve });
880
+ const dots = [];
881
+ if (props.dot && points.length > 0) {
882
+ const dotCfg = typeof props.dot === "object" ? props.dot : {};
883
+ const r = dotCfg.r ?? 3.5;
884
+ const fill = dotCfg.fill ?? "#ffffff";
885
+ const dotStroke = dotCfg.stroke ?? stroke;
886
+ const dotSw = dotCfg.strokeWidth ?? 2;
887
+ for (let i = 0;i < points.length; i++) {
888
+ const p = points[i];
889
+ dots.push(/* @__PURE__ */ jsx2("circle", {
890
+ cx: p.x,
891
+ cy: p.y,
892
+ r,
893
+ fill,
894
+ stroke: dotStroke,
895
+ "stroke-width": dotSw
896
+ }));
897
+ }
898
+ }
899
+ return [
900
+ /* @__PURE__ */ jsx2("path", {
901
+ d,
902
+ fill: "none",
903
+ stroke,
904
+ "stroke-width": strokeWidth,
905
+ "stroke-dasharray": props.strokeDasharray,
906
+ "stroke-linejoin": "round",
907
+ "stroke-linecap": "round"
908
+ }),
909
+ ...dots
910
+ ];
911
+ }
912
+ });
913
+ };
914
+ }
915
+
916
+ // src/charts/Pie.tsx
917
+ var DEFAULT_PALETTE = [
918
+ "#8b5cf6",
919
+ "#ec4899",
920
+ "#3b82f6",
921
+ "#10b981",
922
+ "#f59e0b",
923
+ "#6366f1",
924
+ "#14b8a6",
925
+ "#ef4444"
926
+ ];
927
+ function Pie2(props) {
928
+ return () => {
929
+ const ctx = useChartContext2();
930
+ const innerRadius = props.innerRadius ?? 0;
931
+ const outerRadius = props.outerRadius ?? 80;
932
+ const colors = props.colors ?? DEFAULT_PALETTE;
933
+ const padAngle = props.padAngle ?? 0.02;
934
+ return /* @__PURE__ */ jsx2("g", {
935
+ class: "visage-pie",
936
+ children: () => {
937
+ const dataArr = ctx.data();
938
+ if (dataArr.length === 0)
939
+ return null;
940
+ const w = ctx.innerWidth();
941
+ const h = ctx.innerHeight();
942
+ const centerX = props.cx ?? w / 2;
943
+ const centerY = props.cy ?? h / 2;
944
+ let total = 0;
945
+ for (const item of dataArr) {
946
+ const val = Number(item[props.dataKey]) || 0;
947
+ total += val;
948
+ }
949
+ if (total === 0)
950
+ return null;
951
+ const slices = [];
952
+ let currentAngle = -Math.PI / 2;
953
+ for (let i = 0;i < dataArr.length; i++) {
954
+ const item = dataArr[i];
955
+ const val = Number(item[props.dataKey]) || 0;
956
+ const sliceAngle = val / total * (2 * Math.PI);
957
+ const startAngle = currentAngle + (val > 0 ? padAngle / 2 : 0);
958
+ const endAngle = currentAngle + sliceAngle - (val > 0 ? padAngle / 2 : 0);
959
+ currentAngle += sliceAngle;
960
+ if (val <= 0 || endAngle <= startAngle)
961
+ continue;
962
+ const d = pathArc2({
963
+ cx: centerX,
964
+ cy: centerY,
965
+ innerRadius,
966
+ outerRadius,
967
+ startAngle,
968
+ endAngle
969
+ });
970
+ const fill = colors[i % colors.length];
971
+ slices.push(/* @__PURE__ */ jsx2("path", {
972
+ d,
973
+ fill,
974
+ stroke: "#ffffff",
975
+ "stroke-width": 1.5
976
+ }));
977
+ }
978
+ return slices;
979
+ }
980
+ });
981
+ };
982
+ }
983
+
984
+ // src/charts/Tooltip.tsx
985
+ function ChartTooltip2(props) {
986
+ const render = () => {
987
+ const ctx = useChartContext2();
988
+ const hover = ctx.hoverState();
989
+ if (!hover.active || !hover.datum)
990
+ return null;
991
+ const w = ctx.width();
992
+ const datum = hover.datum;
993
+ const series = ctx.series();
994
+ const xKey = ctx.xDataKey();
995
+ const cardWidth = 160;
996
+ let left = hover.x + 14;
997
+ if (left + cardWidth > w) {
998
+ left = Math.max(10, hover.x - cardWidth - 14);
999
+ }
1000
+ const top = Math.max(10, hover.y - 30);
1001
+ if (props.content) {
1002
+ return /* @__PURE__ */ jsx2("div", {
1003
+ style: {
1004
+ position: "absolute",
1005
+ left: `${left}px`,
1006
+ top: `${top}px`,
1007
+ pointerEvents: "none",
1008
+ zIndex: 50
1009
+ },
1010
+ children: props.content(datum, series)
1011
+ });
1012
+ }
1013
+ const label = xKey && datum[xKey] !== undefined ? datum[xKey] : `#${hover.index + 1}`;
1014
+ const formattedLabel = props.labelFormat ? props.labelFormat(label) : String(label);
1015
+ return /* @__PURE__ */ jsxs2("div", {
1016
+ class: props.class || props.className,
1017
+ style: {
1018
+ position: "absolute",
1019
+ left: `${left}px`,
1020
+ top: `${top}px`,
1021
+ pointerEvents: "none",
1022
+ zIndex: 50,
1023
+ minWidth: `${cardWidth}px`,
1024
+ padding: "8px 12px",
1025
+ borderRadius: "10px",
1026
+ background: "rgba(255, 255, 255, 0.92)",
1027
+ backdropFilter: "blur(12px)",
1028
+ WebkitBackdropFilter: "blur(12px)",
1029
+ border: "1px solid rgba(226, 232, 240, 0.8)",
1030
+ boxShadow: "0 10px 15px -3px rgba(0, 0, 0, 0.08), 0 4px 6px -4px rgba(0, 0, 0, 0.05)",
1031
+ fontSize: "12px",
1032
+ color: "#1e293b",
1033
+ lineHeight: "1.4"
1034
+ },
1035
+ children: [
1036
+ /* @__PURE__ */ jsx2("div", {
1037
+ style: {
1038
+ fontWeight: "600",
1039
+ color: "#475569",
1040
+ marginBottom: "6px",
1041
+ borderBottom: "1px solid #f1f5f9",
1042
+ paddingBottom: "4px"
1043
+ },
1044
+ children: formattedLabel
1045
+ }),
1046
+ /* @__PURE__ */ jsx2("div", {
1047
+ style: { display: "flex", flexDirection: "column", gap: "4px" },
1048
+ children: series.map((s) => {
1049
+ const rawVal = datum[s.dataKey];
1050
+ if (rawVal === undefined || rawVal === null)
1051
+ return null;
1052
+ const valStr = props.format ? props.format(rawVal, s.name ?? s.dataKey) : String(rawVal);
1053
+ return /* @__PURE__ */ jsxs2("div", {
1054
+ style: {
1055
+ display: "flex",
1056
+ alignItems: "center",
1057
+ justifyContent: "space-between",
1058
+ gap: "8px"
1059
+ },
1060
+ children: [
1061
+ /* @__PURE__ */ jsxs2("div", {
1062
+ style: { display: "flex", alignItems: "center", gap: "6px" },
1063
+ children: [
1064
+ /* @__PURE__ */ jsx2("span", {
1065
+ style: {
1066
+ width: "8px",
1067
+ height: "8px",
1068
+ borderRadius: "50%",
1069
+ background: s.color ?? "#8b5cf6"
1070
+ }
1071
+ }),
1072
+ /* @__PURE__ */ jsx2("span", {
1073
+ style: { color: "#64748b" },
1074
+ children: s.name ?? s.dataKey
1075
+ })
1076
+ ]
1077
+ }),
1078
+ /* @__PURE__ */ jsx2("span", {
1079
+ style: { fontWeight: "600", color: "#0f172a" },
1080
+ children: valStr
1081
+ })
1082
+ ]
1083
+ });
1084
+ })
1085
+ })
1086
+ ]
1087
+ });
1088
+ };
1089
+ render.__visage_overlay = true;
1090
+ return render;
1091
+ }
1092
+ var Tooltip2 = ChartTooltip2;
1093
+
1094
+ export { ChartContext2, useChartContext2, pathLine2, pathArea2, pathArc2, Area2, XAxis2, YAxis2, Bar2, CartesianGrid2, generateNiceTicks2, createLinearScale2, createBandScale2, Chart2, ChartCursor2, ChartLegend2, Legend2, Line2, Pie2, ChartTooltip2, Tooltip2 };