hazo_ui 2.16.0 → 2.17.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.
package/dist/index.cjs CHANGED
@@ -9301,6 +9301,808 @@ function SortIndicator({
9301
9301
  sort.length > 1 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium", children: idx + 1 })
9302
9302
  ] });
9303
9303
  }
9304
+ var VBOX_W = 200;
9305
+ var PAD = 2;
9306
+ function Sparkline({
9307
+ data,
9308
+ color: color2,
9309
+ className,
9310
+ height = 40
9311
+ }) {
9312
+ const vbox_h = height;
9313
+ const non_null_values = data.filter((v) => v !== null && !Number.isNaN(v));
9314
+ if (non_null_values.length === 0) {
9315
+ return /* @__PURE__ */ jsxRuntime.jsx(
9316
+ "svg",
9317
+ {
9318
+ viewBox: `0 0 ${VBOX_W} ${vbox_h}`,
9319
+ preserveAspectRatio: "none",
9320
+ className,
9321
+ style: { width: "100%", height: `${height}px`, display: "block" }
9322
+ }
9323
+ );
9324
+ }
9325
+ const mn = Math.min(...non_null_values);
9326
+ const mx = Math.max(...non_null_values);
9327
+ const rng = mx - mn || 1;
9328
+ const cx = (i) => data.length <= 1 ? VBOX_W / 2 : PAD + i * (VBOX_W - 2 * PAD) / (data.length - 1);
9329
+ const cy = (v) => vbox_h - PAD - (v - mn) * (vbox_h - 2 * PAD) / rng;
9330
+ let line_d = "";
9331
+ data.forEach((v, i) => {
9332
+ if (v === null || Number.isNaN(v)) return;
9333
+ line_d += `${line_d ? " L" : "M"}${cx(i).toFixed(1)},${cy(v).toFixed(1)}`;
9334
+ });
9335
+ const first_non_null_idx = data.findIndex((v) => v !== null && !Number.isNaN(v));
9336
+ const last_idx = data.length - 1;
9337
+ const last_v = data[last_idx];
9338
+ const area_d = line_d && `${line_d} L${cx(last_idx).toFixed(1)},${vbox_h - PAD} L${cx(first_non_null_idx).toFixed(1)},${vbox_h - PAD} Z`;
9339
+ const has_last = last_v !== null && last_v !== void 0 && !Number.isNaN(last_v);
9340
+ return /* @__PURE__ */ jsxRuntime.jsxs(
9341
+ "svg",
9342
+ {
9343
+ viewBox: `0 0 ${VBOX_W} ${vbox_h}`,
9344
+ preserveAspectRatio: "none",
9345
+ className,
9346
+ style: { width: "100%", height: `${height}px`, display: "block" },
9347
+ children: [
9348
+ area_d && /* @__PURE__ */ jsxRuntime.jsx("path", { d: area_d, fill: color2, fillOpacity: 0.1, stroke: "none" }),
9349
+ line_d && /* @__PURE__ */ jsxRuntime.jsx("path", { d: line_d, stroke: color2, strokeWidth: 1.5, fill: "none" }),
9350
+ has_last && /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: cx(last_idx), cy: cy(last_v), r: 2, fill: color2 })
9351
+ ]
9352
+ }
9353
+ );
9354
+ }
9355
+ var PAD2 = 2;
9356
+ function InverseSparkline({
9357
+ data,
9358
+ color: color2,
9359
+ className,
9360
+ width = 62,
9361
+ height = 18
9362
+ }) {
9363
+ const non_null_values = data.filter((v) => v !== null && !Number.isNaN(v));
9364
+ if (non_null_values.length === 0) {
9365
+ return /* @__PURE__ */ jsxRuntime.jsx(
9366
+ "svg",
9367
+ {
9368
+ viewBox: `0 0 ${width} ${height}`,
9369
+ preserveAspectRatio: "none",
9370
+ className,
9371
+ style: { width: `${width}px`, height: `${height}px`, display: "block" }
9372
+ }
9373
+ );
9374
+ }
9375
+ const mn = Math.min(...non_null_values);
9376
+ const mx = Math.max(...non_null_values);
9377
+ const rng = mx - mn || 1;
9378
+ const cx = (i) => data.length <= 1 ? width / 2 : PAD2 + i * (width - 2 * PAD2) / (data.length - 1);
9379
+ const cy = (v) => PAD2 + (v - mn) * (height - 2 * PAD2) / rng;
9380
+ let line_d = "";
9381
+ data.forEach((v, i) => {
9382
+ if (v === null || Number.isNaN(v)) return;
9383
+ line_d += `${line_d ? " L" : "M"}${cx(i).toFixed(1)},${cy(v).toFixed(1)}`;
9384
+ });
9385
+ return /* @__PURE__ */ jsxRuntime.jsx(
9386
+ "svg",
9387
+ {
9388
+ viewBox: `0 0 ${width} ${height}`,
9389
+ preserveAspectRatio: "none",
9390
+ className,
9391
+ style: { width: `${width}px`, height: `${height}px`, display: "block" },
9392
+ children: line_d && /* @__PURE__ */ jsxRuntime.jsx("path", { d: line_d, stroke: color2, strokeWidth: 1.4, fill: "none" })
9393
+ }
9394
+ );
9395
+ }
9396
+
9397
+ // src/components/hazo_ui_charts/format.ts
9398
+ function format_num(n) {
9399
+ if (n === null || n === void 0) return "";
9400
+ if (typeof n === "string") return n;
9401
+ if (Number.isNaN(n)) return "";
9402
+ const abs = Math.abs(n);
9403
+ if (abs >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
9404
+ if (abs >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
9405
+ if (Number.isInteger(n)) return n.toString();
9406
+ return n.toFixed(1);
9407
+ }
9408
+ function pick_x_label_indices(length) {
9409
+ if (length <= 0) return [0, 0, 0];
9410
+ if (length === 1) return [0, 0, 0];
9411
+ if (length === 2) return [0, 0, 1];
9412
+ return [0, Math.floor(length / 2), length - 1];
9413
+ }
9414
+ var PAD_LEFT = 38;
9415
+ var PAD_RIGHT = 14;
9416
+ var PAD_TOP = 12;
9417
+ var PAD_BOTTOM = 22;
9418
+ var AXIS_LABEL_COLOR = "#8b949e";
9419
+ var GRIDLINE_COLOR = "#2a3441";
9420
+ function compute_geometry(data, width, height) {
9421
+ const vals = data.filter((v) => v !== null && !Number.isNaN(v));
9422
+ if (vals.length === 0) return null;
9423
+ const mn = Math.min(...vals);
9424
+ const mx = Math.max(...vals);
9425
+ const rng = mx - mn || mx * 0.1 + 1;
9426
+ const y_min = Math.max(0, mn - rng * 0.1);
9427
+ const y_max = mx + rng * 0.1;
9428
+ const y_rng = y_max - y_min || 1;
9429
+ const plot_w = width - PAD_LEFT - PAD_RIGHT;
9430
+ const plot_h = height - PAD_TOP - PAD_BOTTOM;
9431
+ return {
9432
+ vbox_w: width,
9433
+ vbox_h: height,
9434
+ y_min,
9435
+ y_max,
9436
+ cx: (i) => data.length <= 1 ? PAD_LEFT + plot_w / 2 : PAD_LEFT + i * plot_w / (data.length - 1),
9437
+ cy: (v) => PAD_TOP + (1 - (v - y_min) / y_rng) * plot_h
9438
+ };
9439
+ }
9440
+ function build_paths(data, g) {
9441
+ let line_d = "";
9442
+ let first_idx = -1;
9443
+ data.forEach((v, i) => {
9444
+ if (v === null || Number.isNaN(v)) return;
9445
+ if (first_idx === -1) first_idx = i;
9446
+ line_d += `${line_d ? " L" : "M"}${g.cx(i).toFixed(1)},${g.cy(v).toFixed(1)}`;
9447
+ });
9448
+ const last_idx = data.length - 1;
9449
+ const last_v = data[last_idx];
9450
+ const area_d = line_d && last_v !== null && last_v !== void 0 && !Number.isNaN(last_v) ? `${line_d} L${g.cx(last_idx).toFixed(1)},${g.vbox_h - PAD_BOTTOM} L${g.cx(first_idx).toFixed(1)},${g.vbox_h - PAD_BOTTOM} Z` : "";
9451
+ return { line_d, area_d };
9452
+ }
9453
+ function LineChart({
9454
+ data,
9455
+ dates,
9456
+ color: color2,
9457
+ width = 360,
9458
+ height = 130,
9459
+ unit = "",
9460
+ showTooltip = true,
9461
+ className
9462
+ }) {
9463
+ const geo = compute_geometry(data, width, height);
9464
+ const svg_ref = React25__namespace.useRef(null);
9465
+ const [hover_idx, set_hover_idx] = React25__namespace.useState(null);
9466
+ const handle_mouse_move = React25__namespace.useCallback(
9467
+ (e) => {
9468
+ if (!geo) return;
9469
+ const rect = e.currentTarget.getBoundingClientRect();
9470
+ if (rect.width === 0) return;
9471
+ const vbox_x = (e.clientX - rect.left) / rect.width * geo.vbox_w;
9472
+ const plot_w = geo.vbox_w - PAD_LEFT - PAD_RIGHT;
9473
+ if (vbox_x < PAD_LEFT || vbox_x > geo.vbox_w - PAD_RIGHT) {
9474
+ set_hover_idx(null);
9475
+ return;
9476
+ }
9477
+ const ratio = (vbox_x - PAD_LEFT) / plot_w;
9478
+ const idx = Math.round(ratio * (data.length - 1));
9479
+ const clamped = Math.max(0, Math.min(data.length - 1, idx));
9480
+ let resolved = clamped;
9481
+ if (data[resolved] === null || Number.isNaN(data[resolved])) {
9482
+ for (let step = 1; step < data.length; step += 1) {
9483
+ const left_i = clamped - step;
9484
+ const right_i = clamped + step;
9485
+ if (left_i >= 0 && data[left_i] !== null && !Number.isNaN(data[left_i])) {
9486
+ resolved = left_i;
9487
+ break;
9488
+ }
9489
+ if (right_i < data.length && data[right_i] !== null && !Number.isNaN(data[right_i])) {
9490
+ resolved = right_i;
9491
+ break;
9492
+ }
9493
+ }
9494
+ }
9495
+ set_hover_idx(resolved);
9496
+ },
9497
+ [geo, data]
9498
+ );
9499
+ const handle_mouse_leave = React25__namespace.useCallback(() => set_hover_idx(null), []);
9500
+ if (!geo) {
9501
+ return /* @__PURE__ */ jsxRuntime.jsx(
9502
+ "svg",
9503
+ {
9504
+ viewBox: `0 0 ${width} ${height}`,
9505
+ className: cn("cls_hazo_chart cls_hazo_chart_empty", className),
9506
+ style: { width: "100%", height: "auto", display: "block", maxHeight: `${height + 10}px` }
9507
+ }
9508
+ );
9509
+ }
9510
+ const { line_d, area_d } = build_paths(data, geo);
9511
+ const x_label_idx = pick_x_label_indices(data.length);
9512
+ const last_idx = data.length - 1;
9513
+ const last_v = data[last_idx];
9514
+ const has_last = last_v !== null && last_v !== void 0 && !Number.isNaN(last_v);
9515
+ const hover_v = hover_idx !== null ? data[hover_idx] : null;
9516
+ const hover_has_v = hover_v !== null && hover_v !== void 0 && !Number.isNaN(hover_v);
9517
+ return /* @__PURE__ */ jsxRuntime.jsxs(
9518
+ "svg",
9519
+ {
9520
+ ref: svg_ref,
9521
+ viewBox: `0 0 ${width} ${height}`,
9522
+ onMouseMove: showTooltip ? handle_mouse_move : void 0,
9523
+ onMouseLeave: showTooltip ? handle_mouse_leave : void 0,
9524
+ className: cn("cls_hazo_chart", className),
9525
+ style: {
9526
+ width: "100%",
9527
+ height: "auto",
9528
+ display: "block",
9529
+ maxHeight: `${height + 10}px`,
9530
+ cursor: showTooltip ? "crosshair" : "default"
9531
+ },
9532
+ children: [
9533
+ [0, 1, 2].map((i) => {
9534
+ const y = PAD_TOP + i / 2 * (geo.vbox_h - PAD_TOP - PAD_BOTTOM);
9535
+ return /* @__PURE__ */ jsxRuntime.jsx(
9536
+ "line",
9537
+ {
9538
+ x1: PAD_LEFT,
9539
+ x2: geo.vbox_w - PAD_RIGHT,
9540
+ y1: y,
9541
+ y2: y,
9542
+ stroke: GRIDLINE_COLOR,
9543
+ strokeWidth: 0.5,
9544
+ strokeDasharray: "2,3"
9545
+ },
9546
+ i
9547
+ );
9548
+ }),
9549
+ area_d && /* @__PURE__ */ jsxRuntime.jsx("path", { d: area_d, fill: color2, fillOpacity: 0.12, stroke: "none" }),
9550
+ line_d && /* @__PURE__ */ jsxRuntime.jsx("path", { d: line_d, stroke: color2, strokeWidth: 1.8, fill: "none" }),
9551
+ /* @__PURE__ */ jsxRuntime.jsx("text", { x: PAD_LEFT - 4, y: PAD_TOP + 3, textAnchor: "end", fill: AXIS_LABEL_COLOR, fontSize: 9, children: format_num(geo.y_max) }),
9552
+ /* @__PURE__ */ jsxRuntime.jsx(
9553
+ "text",
9554
+ {
9555
+ x: PAD_LEFT - 4,
9556
+ y: PAD_TOP + (geo.vbox_h - PAD_TOP - PAD_BOTTOM) / 2 + 3,
9557
+ textAnchor: "end",
9558
+ fill: AXIS_LABEL_COLOR,
9559
+ fontSize: 9,
9560
+ children: format_num((geo.y_max + geo.y_min) / 2)
9561
+ }
9562
+ ),
9563
+ /* @__PURE__ */ jsxRuntime.jsx(
9564
+ "text",
9565
+ {
9566
+ x: PAD_LEFT - 4,
9567
+ y: geo.vbox_h - PAD_BOTTOM + 3,
9568
+ textAnchor: "end",
9569
+ fill: AXIS_LABEL_COLOR,
9570
+ fontSize: 9,
9571
+ children: format_num(geo.y_min)
9572
+ }
9573
+ ),
9574
+ x_label_idx.map((idx, k) => {
9575
+ const anchor = k === 0 ? "start" : k === 1 ? "middle" : "end";
9576
+ const x_pos = geo.cx(idx);
9577
+ return /* @__PURE__ */ jsxRuntime.jsx(
9578
+ "text",
9579
+ {
9580
+ x: x_pos,
9581
+ y: geo.vbox_h - 6,
9582
+ textAnchor: anchor,
9583
+ fill: AXIS_LABEL_COLOR,
9584
+ fontSize: 9,
9585
+ children: dates[idx] ?? ""
9586
+ },
9587
+ `x_${k}`
9588
+ );
9589
+ }),
9590
+ has_last && hover_idx === null && (() => {
9591
+ const x = geo.cx(last_idx);
9592
+ const y = geo.cy(last_v);
9593
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
9594
+ /* @__PURE__ */ jsxRuntime.jsx(
9595
+ "line",
9596
+ {
9597
+ x1: x,
9598
+ x2: PAD_LEFT,
9599
+ y1: y,
9600
+ y2: y,
9601
+ stroke: color2,
9602
+ strokeWidth: 0.5,
9603
+ strokeDasharray: "2,2",
9604
+ opacity: 0.4
9605
+ }
9606
+ ),
9607
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: x, cy: y, r: 3.5, fill: color2, stroke: color2, strokeWidth: 2, fillOpacity: 0.3 }),
9608
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: x, cy: y, r: 2, fill: color2 }),
9609
+ /* @__PURE__ */ jsxRuntime.jsxs("text", { x: x - 6, y: y - 6, textAnchor: "end", fill: color2, fontSize: 10, fontWeight: 700, children: [
9610
+ format_num(last_v),
9611
+ unit
9612
+ ] })
9613
+ ] });
9614
+ })(),
9615
+ showTooltip && hover_idx !== null && hover_has_v && (() => {
9616
+ const x = geo.cx(hover_idx);
9617
+ const y = geo.cy(hover_v);
9618
+ const label = `${format_num(hover_v)}${unit}`;
9619
+ const date = dates[hover_idx] ?? "";
9620
+ const bubble_text_w = Math.max(label.length, date.length) * 5.5 + 12;
9621
+ const bubble_w = Math.max(bubble_text_w, 40);
9622
+ const bubble_h = 26;
9623
+ const flip = x + bubble_w + 6 > geo.vbox_w - PAD_RIGHT;
9624
+ const bubble_x = flip ? x - bubble_w - 6 : x + 6;
9625
+ const bubble_y = Math.max(PAD_TOP, Math.min(y - bubble_h / 2, geo.vbox_h - PAD_BOTTOM - bubble_h));
9626
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
9627
+ /* @__PURE__ */ jsxRuntime.jsx(
9628
+ "line",
9629
+ {
9630
+ x1: x,
9631
+ x2: x,
9632
+ y1: PAD_TOP,
9633
+ y2: geo.vbox_h - PAD_BOTTOM,
9634
+ stroke: color2,
9635
+ strokeWidth: 0.5,
9636
+ strokeDasharray: "2,2",
9637
+ opacity: 0.6
9638
+ }
9639
+ ),
9640
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: x, cy: y, r: 3, fill: color2 }),
9641
+ /* @__PURE__ */ jsxRuntime.jsx(
9642
+ "rect",
9643
+ {
9644
+ x: bubble_x,
9645
+ y: bubble_y,
9646
+ width: bubble_w,
9647
+ height: bubble_h,
9648
+ rx: 3,
9649
+ fill: "#0d1117",
9650
+ stroke: color2,
9651
+ strokeWidth: 0.5,
9652
+ fillOpacity: 0.92
9653
+ }
9654
+ ),
9655
+ /* @__PURE__ */ jsxRuntime.jsx(
9656
+ "text",
9657
+ {
9658
+ x: bubble_x + bubble_w / 2,
9659
+ y: bubble_y + 11,
9660
+ textAnchor: "middle",
9661
+ fill: color2,
9662
+ fontSize: 10,
9663
+ fontWeight: 700,
9664
+ children: label
9665
+ }
9666
+ ),
9667
+ /* @__PURE__ */ jsxRuntime.jsx(
9668
+ "text",
9669
+ {
9670
+ x: bubble_x + bubble_w / 2,
9671
+ y: bubble_y + 22,
9672
+ textAnchor: "middle",
9673
+ fill: AXIS_LABEL_COLOR,
9674
+ fontSize: 8,
9675
+ children: date
9676
+ }
9677
+ )
9678
+ ] });
9679
+ })()
9680
+ ]
9681
+ }
9682
+ );
9683
+ }
9684
+ var PAD_LEFT2 = 38;
9685
+ var PAD_RIGHT2 = 14;
9686
+ var PAD_TOP2 = 12;
9687
+ var PAD_BOTTOM2 = 22;
9688
+ var AXIS_LABEL_COLOR2 = "#8b949e";
9689
+ var GRIDLINE_COLOR2 = "#2a3441";
9690
+ function compute_geometry2(series, width, height) {
9691
+ const all_vals = [];
9692
+ let n = 0;
9693
+ for (const s of series) {
9694
+ n = Math.max(n, s.data.length);
9695
+ for (const v of s.data) if (v !== null && !Number.isNaN(v)) all_vals.push(v);
9696
+ }
9697
+ if (all_vals.length === 0 || n === 0) return null;
9698
+ const mn = Math.min(...all_vals);
9699
+ const mx = Math.max(...all_vals);
9700
+ const rng = mx - mn || mx * 0.1 + 1;
9701
+ const y_min = Math.max(0, mn - rng * 0.05);
9702
+ const y_max = mx + rng * 0.05;
9703
+ const y_rng = y_max - y_min || 1;
9704
+ const plot_w = width - PAD_LEFT2 - PAD_RIGHT2;
9705
+ const plot_h = height - PAD_TOP2 - PAD_BOTTOM2;
9706
+ return {
9707
+ vbox_w: width,
9708
+ vbox_h: height,
9709
+ y_min,
9710
+ y_max,
9711
+ point_count: n,
9712
+ cx: (i) => n <= 1 ? PAD_LEFT2 + plot_w / 2 : PAD_LEFT2 + i * plot_w / (n - 1),
9713
+ cy: (v) => PAD_TOP2 + (1 - (v - y_min) / y_rng) * plot_h
9714
+ };
9715
+ }
9716
+ function MultiLineChart({
9717
+ series,
9718
+ dates,
9719
+ width = 360,
9720
+ height = 140,
9721
+ showTooltip = true,
9722
+ showLegend = true,
9723
+ className
9724
+ }) {
9725
+ const geo = compute_geometry2(series, width, height);
9726
+ const [hover_idx, set_hover_idx] = React25__namespace.useState(null);
9727
+ const handle_mouse_move = React25__namespace.useCallback(
9728
+ (e) => {
9729
+ if (!geo) return;
9730
+ const rect = e.currentTarget.getBoundingClientRect();
9731
+ if (rect.width === 0) return;
9732
+ const vbox_x = (e.clientX - rect.left) / rect.width * geo.vbox_w;
9733
+ const plot_w = geo.vbox_w - PAD_LEFT2 - PAD_RIGHT2;
9734
+ if (vbox_x < PAD_LEFT2 || vbox_x > geo.vbox_w - PAD_RIGHT2) {
9735
+ set_hover_idx(null);
9736
+ return;
9737
+ }
9738
+ const ratio = (vbox_x - PAD_LEFT2) / plot_w;
9739
+ const idx = Math.round(ratio * (geo.point_count - 1));
9740
+ set_hover_idx(Math.max(0, Math.min(geo.point_count - 1, idx)));
9741
+ },
9742
+ [geo]
9743
+ );
9744
+ const handle_mouse_leave = React25__namespace.useCallback(() => set_hover_idx(null), []);
9745
+ if (!geo) {
9746
+ return /* @__PURE__ */ jsxRuntime.jsx(
9747
+ "svg",
9748
+ {
9749
+ viewBox: `0 0 ${width} ${height}`,
9750
+ className: cn("cls_hazo_chart cls_hazo_chart_empty", className),
9751
+ style: { width: "100%", height: "auto", display: "block", maxHeight: `${height + 10}px` }
9752
+ }
9753
+ );
9754
+ }
9755
+ const x_label_idx = pick_x_label_indices(geo.point_count);
9756
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("cls_hazo_chart_wrapper", className), children: [
9757
+ /* @__PURE__ */ jsxRuntime.jsxs(
9758
+ "svg",
9759
+ {
9760
+ viewBox: `0 0 ${width} ${height}`,
9761
+ onMouseMove: showTooltip ? handle_mouse_move : void 0,
9762
+ onMouseLeave: showTooltip ? handle_mouse_leave : void 0,
9763
+ className: "cls_hazo_chart cls_hazo_chart_multi",
9764
+ style: {
9765
+ width: "100%",
9766
+ height: "auto",
9767
+ display: "block",
9768
+ maxHeight: `${height + 10}px`,
9769
+ cursor: showTooltip ? "crosshair" : "default"
9770
+ },
9771
+ children: [
9772
+ [0, 1, 2].map((i) => {
9773
+ const y = PAD_TOP2 + i / 2 * (geo.vbox_h - PAD_TOP2 - PAD_BOTTOM2);
9774
+ return /* @__PURE__ */ jsxRuntime.jsx(
9775
+ "line",
9776
+ {
9777
+ x1: PAD_LEFT2,
9778
+ x2: geo.vbox_w - PAD_RIGHT2,
9779
+ y1: y,
9780
+ y2: y,
9781
+ stroke: GRIDLINE_COLOR2,
9782
+ strokeWidth: 0.5,
9783
+ strokeDasharray: "2,3"
9784
+ },
9785
+ i
9786
+ );
9787
+ }),
9788
+ /* @__PURE__ */ jsxRuntime.jsx("text", { x: PAD_LEFT2 - 4, y: PAD_TOP2 + 3, textAnchor: "end", fill: AXIS_LABEL_COLOR2, fontSize: 9, children: format_num(geo.y_max) }),
9789
+ /* @__PURE__ */ jsxRuntime.jsx(
9790
+ "text",
9791
+ {
9792
+ x: PAD_LEFT2 - 4,
9793
+ y: PAD_TOP2 + (geo.vbox_h - PAD_TOP2 - PAD_BOTTOM2) / 2 + 3,
9794
+ textAnchor: "end",
9795
+ fill: AXIS_LABEL_COLOR2,
9796
+ fontSize: 9,
9797
+ children: format_num((geo.y_max + geo.y_min) / 2)
9798
+ }
9799
+ ),
9800
+ /* @__PURE__ */ jsxRuntime.jsx("text", { x: PAD_LEFT2 - 4, y: geo.vbox_h - PAD_BOTTOM2 + 3, textAnchor: "end", fill: AXIS_LABEL_COLOR2, fontSize: 9, children: format_num(geo.y_min) }),
9801
+ x_label_idx.map((idx, k) => {
9802
+ const anchor = k === 0 ? "start" : k === 1 ? "middle" : "end";
9803
+ return /* @__PURE__ */ jsxRuntime.jsx(
9804
+ "text",
9805
+ {
9806
+ x: geo.cx(idx),
9807
+ y: geo.vbox_h - 6,
9808
+ textAnchor: anchor,
9809
+ fill: AXIS_LABEL_COLOR2,
9810
+ fontSize: 9,
9811
+ children: dates[idx] ?? ""
9812
+ },
9813
+ `x_${k}`
9814
+ );
9815
+ }),
9816
+ series.map((s, s_idx) => {
9817
+ let d = "";
9818
+ s.data.forEach((v, i) => {
9819
+ if (v === null || Number.isNaN(v)) return;
9820
+ d += `${d ? " L" : "M"}${geo.cx(i).toFixed(1)},${geo.cy(v).toFixed(1)}`;
9821
+ });
9822
+ const last_idx = s.data.length - 1;
9823
+ const last_v = s.data[last_idx];
9824
+ const has_last = last_v !== null && last_v !== void 0 && !Number.isNaN(last_v);
9825
+ return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
9826
+ d && /* @__PURE__ */ jsxRuntime.jsx("path", { d, stroke: s.color, strokeWidth: 1.7, fill: "none" }),
9827
+ has_last && hover_idx === null && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
9828
+ /* @__PURE__ */ jsxRuntime.jsx(
9829
+ "circle",
9830
+ {
9831
+ cx: geo.cx(last_idx),
9832
+ cy: geo.cy(last_v),
9833
+ r: 3,
9834
+ fill: s.color,
9835
+ fillOpacity: 0.3,
9836
+ stroke: s.color,
9837
+ strokeWidth: 1.5
9838
+ }
9839
+ ),
9840
+ /* @__PURE__ */ jsxRuntime.jsx(
9841
+ "text",
9842
+ {
9843
+ x: geo.cx(last_idx) - 5,
9844
+ y: geo.cy(last_v) - 5,
9845
+ textAnchor: "end",
9846
+ fill: s.color,
9847
+ fontSize: 9,
9848
+ fontWeight: 700,
9849
+ children: format_num(last_v)
9850
+ }
9851
+ )
9852
+ ] })
9853
+ ] }, `series_${s_idx}`);
9854
+ }),
9855
+ showTooltip && hover_idx !== null && (() => {
9856
+ const x = geo.cx(hover_idx);
9857
+ const items = series.map((s) => ({
9858
+ label: s.label,
9859
+ color: s.color,
9860
+ value: s.data[hover_idx] ?? null
9861
+ })).filter((it) => it.value !== null && !Number.isNaN(it.value));
9862
+ if (items.length === 0) return null;
9863
+ const bubble_w = 70;
9864
+ const row_h = 12;
9865
+ const bubble_h = items.length * row_h + 18;
9866
+ const flip = x + bubble_w + 6 > geo.vbox_w - PAD_RIGHT2;
9867
+ const bubble_x = flip ? x - bubble_w - 6 : x + 6;
9868
+ const bubble_y = Math.max(PAD_TOP2, Math.min(PAD_TOP2 + 5, geo.vbox_h - PAD_BOTTOM2 - bubble_h));
9869
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
9870
+ /* @__PURE__ */ jsxRuntime.jsx(
9871
+ "line",
9872
+ {
9873
+ x1: x,
9874
+ x2: x,
9875
+ y1: PAD_TOP2,
9876
+ y2: geo.vbox_h - PAD_BOTTOM2,
9877
+ stroke: AXIS_LABEL_COLOR2,
9878
+ strokeWidth: 0.5,
9879
+ strokeDasharray: "2,2",
9880
+ opacity: 0.6
9881
+ }
9882
+ ),
9883
+ items.map((it) => /* @__PURE__ */ jsxRuntime.jsx(
9884
+ "circle",
9885
+ {
9886
+ cx: x,
9887
+ cy: geo.cy(it.value),
9888
+ r: 3,
9889
+ fill: it.color
9890
+ },
9891
+ `hd_${it.label}`
9892
+ )),
9893
+ /* @__PURE__ */ jsxRuntime.jsx(
9894
+ "rect",
9895
+ {
9896
+ x: bubble_x,
9897
+ y: bubble_y,
9898
+ width: bubble_w,
9899
+ height: bubble_h,
9900
+ rx: 3,
9901
+ fill: "#0d1117",
9902
+ stroke: AXIS_LABEL_COLOR2,
9903
+ strokeWidth: 0.5,
9904
+ fillOpacity: 0.92
9905
+ }
9906
+ ),
9907
+ /* @__PURE__ */ jsxRuntime.jsx("text", { x: bubble_x + 6, y: bubble_y + 10, fill: AXIS_LABEL_COLOR2, fontSize: 8, children: dates[hover_idx] ?? "" }),
9908
+ items.map((it, i) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
9909
+ /* @__PURE__ */ jsxRuntime.jsx(
9910
+ "rect",
9911
+ {
9912
+ x: bubble_x + 6,
9913
+ y: bubble_y + 14 + i * row_h,
9914
+ width: 6,
9915
+ height: 6,
9916
+ fill: it.color
9917
+ }
9918
+ ),
9919
+ /* @__PURE__ */ jsxRuntime.jsx(
9920
+ "text",
9921
+ {
9922
+ x: bubble_x + 16,
9923
+ y: bubble_y + 20 + i * row_h,
9924
+ fill: it.color,
9925
+ fontSize: 9,
9926
+ fontWeight: 600,
9927
+ children: format_num(it.value)
9928
+ }
9929
+ )
9930
+ ] }, `row_${i}`))
9931
+ ] });
9932
+ })()
9933
+ ]
9934
+ }
9935
+ ),
9936
+ showLegend && /* @__PURE__ */ jsxRuntime.jsx(
9937
+ "div",
9938
+ {
9939
+ className: "cls_hazo_chart_legend",
9940
+ style: {
9941
+ display: "flex",
9942
+ gap: "12px",
9943
+ justifyContent: "center",
9944
+ marginTop: "4px",
9945
+ fontSize: "10px",
9946
+ color: AXIS_LABEL_COLOR2,
9947
+ flexWrap: "wrap"
9948
+ },
9949
+ children: series.map((s) => /* @__PURE__ */ jsxRuntime.jsxs(
9950
+ "span",
9951
+ {
9952
+ style: { display: "inline-flex", alignItems: "center", gap: "4px" },
9953
+ children: [
9954
+ /* @__PURE__ */ jsxRuntime.jsx(
9955
+ "i",
9956
+ {
9957
+ style: {
9958
+ display: "inline-block",
9959
+ width: "8px",
9960
+ height: "8px",
9961
+ background: s.color,
9962
+ borderRadius: "1px"
9963
+ }
9964
+ }
9965
+ ),
9966
+ s.label
9967
+ ]
9968
+ },
9969
+ s.label
9970
+ ))
9971
+ }
9972
+ )
9973
+ ] });
9974
+ }
9975
+ var PAD_LEFT3 = 32;
9976
+ var PAD_RIGHT3 = 8;
9977
+ var PAD_TOP3 = 10;
9978
+ var PAD_BOTTOM3 = 22;
9979
+ var BAR_GAP_RATIO = 0.25;
9980
+ var AXIS_LABEL_COLOR3 = "#8b949e";
9981
+ function StackedBars({
9982
+ bars,
9983
+ width = 360,
9984
+ height = 140,
9985
+ showYAxis = true,
9986
+ className
9987
+ }) {
9988
+ const totals = bars.map((b) => b.segments.reduce((sum, s) => sum + s.value, 0));
9989
+ const y_max = Math.max(0, ...totals) || 1;
9990
+ const plot_w = width - PAD_LEFT3 - PAD_RIGHT3;
9991
+ const plot_h = height - PAD_TOP3 - PAD_BOTTOM3;
9992
+ const slot_w = bars.length > 0 ? plot_w / bars.length : 0;
9993
+ const bar_w = slot_w * (1 - BAR_GAP_RATIO);
9994
+ const x_for = (i) => PAD_LEFT3 + i * slot_w + (slot_w - bar_w) / 2;
9995
+ const x_label_idx = pick_x_label_indices(bars.length);
9996
+ return /* @__PURE__ */ jsxRuntime.jsxs(
9997
+ "svg",
9998
+ {
9999
+ viewBox: `0 0 ${width} ${height}`,
10000
+ className: cn("cls_hazo_chart cls_hazo_chart_stacked_bars", className),
10001
+ style: { width: "100%", height: "auto", display: "block", maxHeight: `${height + 10}px` },
10002
+ children: [
10003
+ showYAxis && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
10004
+ /* @__PURE__ */ jsxRuntime.jsx("text", { x: PAD_LEFT3 - 4, y: PAD_TOP3 + 3, textAnchor: "end", fill: AXIS_LABEL_COLOR3, fontSize: 9, children: format_num(y_max) }),
10005
+ /* @__PURE__ */ jsxRuntime.jsx(
10006
+ "text",
10007
+ {
10008
+ x: PAD_LEFT3 - 4,
10009
+ y: PAD_TOP3 + plot_h / 2 + 3,
10010
+ textAnchor: "end",
10011
+ fill: AXIS_LABEL_COLOR3,
10012
+ fontSize: 9,
10013
+ children: format_num(y_max / 2)
10014
+ }
10015
+ ),
10016
+ /* @__PURE__ */ jsxRuntime.jsx(
10017
+ "text",
10018
+ {
10019
+ x: PAD_LEFT3 - 4,
10020
+ y: height - PAD_BOTTOM3 + 3,
10021
+ textAnchor: "end",
10022
+ fill: AXIS_LABEL_COLOR3,
10023
+ fontSize: 9,
10024
+ children: "0"
10025
+ }
10026
+ )
10027
+ ] }),
10028
+ bars.map((bar, i) => {
10029
+ let cursor_y = height - PAD_BOTTOM3;
10030
+ return /* @__PURE__ */ jsxRuntime.jsx("g", { children: bar.segments.map((seg, s_idx) => {
10031
+ if (seg.value <= 0) return null;
10032
+ const seg_h = seg.value / y_max * plot_h;
10033
+ const y = cursor_y - seg_h;
10034
+ cursor_y = y;
10035
+ return /* @__PURE__ */ jsxRuntime.jsx(
10036
+ "rect",
10037
+ {
10038
+ x: x_for(i),
10039
+ y,
10040
+ width: bar_w,
10041
+ height: seg_h,
10042
+ fill: seg.color
10043
+ },
10044
+ `seg_${s_idx}`
10045
+ );
10046
+ }) }, `bar_${i}`);
10047
+ }),
10048
+ x_label_idx.map((idx, k) => {
10049
+ const anchor = k === 0 ? "start" : k === 1 ? "middle" : "end";
10050
+ const x_pos = x_for(idx) + bar_w / 2;
10051
+ return /* @__PURE__ */ jsxRuntime.jsx(
10052
+ "text",
10053
+ {
10054
+ x: x_pos,
10055
+ y: height - 6,
10056
+ textAnchor: anchor,
10057
+ fill: AXIS_LABEL_COLOR3,
10058
+ fontSize: 9,
10059
+ children: bars[idx]?.label ?? ""
10060
+ },
10061
+ `x_${k}`
10062
+ );
10063
+ })
10064
+ ]
10065
+ }
10066
+ );
10067
+ }
10068
+ function DateRangeSelector({
10069
+ value,
10070
+ onChange,
10071
+ options,
10072
+ className,
10073
+ ariaLabel = "Date range"
10074
+ }) {
10075
+ return /* @__PURE__ */ jsxRuntime.jsx(
10076
+ "div",
10077
+ {
10078
+ role: "group",
10079
+ "aria-label": ariaLabel,
10080
+ className: cn(
10081
+ "cls_hazo_date_range_selector inline-flex items-center gap-0 rounded-md border bg-background p-0.5",
10082
+ className
10083
+ ),
10084
+ children: options.map((opt) => {
10085
+ const is_active = opt.value === value;
10086
+ return /* @__PURE__ */ jsxRuntime.jsx(
10087
+ "button",
10088
+ {
10089
+ type: "button",
10090
+ "aria-pressed": is_active,
10091
+ onClick: () => {
10092
+ if (!is_active) onChange(opt.value);
10093
+ },
10094
+ className: cn(
10095
+ "cls_hazo_date_range_option px-2.5 py-1 text-xs font-medium rounded-sm transition-colors",
10096
+ is_active ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground hover:bg-muted"
10097
+ ),
10098
+ children: opt.label
10099
+ },
10100
+ opt.value
10101
+ );
10102
+ })
10103
+ }
10104
+ );
10105
+ }
9304
10106
 
9305
10107
  Object.defineProperty(exports, "rawToast", {
9306
10108
  enumerable: true,
@@ -9340,6 +10142,7 @@ exports.CollapsibleTrigger = CollapsibleTrigger2;
9340
10142
  exports.CommandNodeExtension = CommandNodeExtension;
9341
10143
  exports.CommandPill = CommandPill;
9342
10144
  exports.CommandPopover = CommandPopover;
10145
+ exports.DateRangeSelector = DateRangeSelector;
9343
10146
  exports.Drawer = Drawer;
9344
10147
  exports.DrawerClose = DrawerClose;
9345
10148
  exports.DrawerContent = DrawerContent;
@@ -9396,8 +10199,11 @@ exports.HoverCard = HoverCard;
9396
10199
  exports.HoverCardContent = HoverCardContent;
9397
10200
  exports.HoverCardTrigger = HoverCardTrigger;
9398
10201
  exports.Input = Input;
10202
+ exports.InverseSparkline = InverseSparkline;
9399
10203
  exports.Label = Label3;
10204
+ exports.LineChart = LineChart;
9400
10205
  exports.LoadingTimeout = LoadingTimeout;
10206
+ exports.MultiLineChart = MultiLineChart;
9401
10207
  exports.Popover = Popover;
9402
10208
  exports.PopoverContent = PopoverContent;
9403
10209
  exports.PopoverTrigger = PopoverTrigger;
@@ -9428,7 +10234,9 @@ exports.SkeletonBar = SkeletonBar;
9428
10234
  exports.SkeletonCircle = SkeletonCircle;
9429
10235
  exports.SkeletonGroup = SkeletonGroup;
9430
10236
  exports.SkeletonRect = SkeletonRect;
10237
+ exports.Sparkline = Sparkline;
9431
10238
  exports.Spinner = Spinner;
10239
+ exports.StackedBars = StackedBars;
9432
10240
  exports.Switch = Switch;
9433
10241
  exports.Table = Table2;
9434
10242
  exports.TableBody = TableBody;
@@ -9454,8 +10262,10 @@ exports.applyKanbanFilter = applyKanbanFilter;
9454
10262
  exports.buttonGroupVariants = buttonGroupVariants;
9455
10263
  exports.create_command_suggestion_extension = create_command_suggestion_extension;
9456
10264
  exports.errorToast = errorToast;
10265
+ exports.format_num = format_num;
9457
10266
  exports.get_hazo_ui_config = get_hazo_ui_config;
9458
10267
  exports.parse_commands_from_text = parse_commands_from_text;
10268
+ exports.pick_x_label_indices = pick_x_label_indices;
9459
10269
  exports.reset_hazo_ui_config = reset_hazo_ui_config;
9460
10270
  exports.resolve_animation_classes = resolve_animation_classes;
9461
10271
  exports.set_hazo_ui_config = set_hazo_ui_config;