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.js CHANGED
@@ -9233,7 +9233,809 @@ function SortIndicator({
9233
9233
  sort.length > 1 && /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium", children: idx + 1 })
9234
9234
  ] });
9235
9235
  }
9236
+ var VBOX_W = 200;
9237
+ var PAD = 2;
9238
+ function Sparkline({
9239
+ data,
9240
+ color: color2,
9241
+ className,
9242
+ height = 40
9243
+ }) {
9244
+ const vbox_h = height;
9245
+ const non_null_values = data.filter((v) => v !== null && !Number.isNaN(v));
9246
+ if (non_null_values.length === 0) {
9247
+ return /* @__PURE__ */ jsx(
9248
+ "svg",
9249
+ {
9250
+ viewBox: `0 0 ${VBOX_W} ${vbox_h}`,
9251
+ preserveAspectRatio: "none",
9252
+ className,
9253
+ style: { width: "100%", height: `${height}px`, display: "block" }
9254
+ }
9255
+ );
9256
+ }
9257
+ const mn = Math.min(...non_null_values);
9258
+ const mx = Math.max(...non_null_values);
9259
+ const rng = mx - mn || 1;
9260
+ const cx = (i) => data.length <= 1 ? VBOX_W / 2 : PAD + i * (VBOX_W - 2 * PAD) / (data.length - 1);
9261
+ const cy = (v) => vbox_h - PAD - (v - mn) * (vbox_h - 2 * PAD) / rng;
9262
+ let line_d = "";
9263
+ data.forEach((v, i) => {
9264
+ if (v === null || Number.isNaN(v)) return;
9265
+ line_d += `${line_d ? " L" : "M"}${cx(i).toFixed(1)},${cy(v).toFixed(1)}`;
9266
+ });
9267
+ const first_non_null_idx = data.findIndex((v) => v !== null && !Number.isNaN(v));
9268
+ const last_idx = data.length - 1;
9269
+ const last_v = data[last_idx];
9270
+ 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`;
9271
+ const has_last = last_v !== null && last_v !== void 0 && !Number.isNaN(last_v);
9272
+ return /* @__PURE__ */ jsxs(
9273
+ "svg",
9274
+ {
9275
+ viewBox: `0 0 ${VBOX_W} ${vbox_h}`,
9276
+ preserveAspectRatio: "none",
9277
+ className,
9278
+ style: { width: "100%", height: `${height}px`, display: "block" },
9279
+ children: [
9280
+ area_d && /* @__PURE__ */ jsx("path", { d: area_d, fill: color2, fillOpacity: 0.1, stroke: "none" }),
9281
+ line_d && /* @__PURE__ */ jsx("path", { d: line_d, stroke: color2, strokeWidth: 1.5, fill: "none" }),
9282
+ has_last && /* @__PURE__ */ jsx("circle", { cx: cx(last_idx), cy: cy(last_v), r: 2, fill: color2 })
9283
+ ]
9284
+ }
9285
+ );
9286
+ }
9287
+ var PAD2 = 2;
9288
+ function InverseSparkline({
9289
+ data,
9290
+ color: color2,
9291
+ className,
9292
+ width = 62,
9293
+ height = 18
9294
+ }) {
9295
+ const non_null_values = data.filter((v) => v !== null && !Number.isNaN(v));
9296
+ if (non_null_values.length === 0) {
9297
+ return /* @__PURE__ */ jsx(
9298
+ "svg",
9299
+ {
9300
+ viewBox: `0 0 ${width} ${height}`,
9301
+ preserveAspectRatio: "none",
9302
+ className,
9303
+ style: { width: `${width}px`, height: `${height}px`, display: "block" }
9304
+ }
9305
+ );
9306
+ }
9307
+ const mn = Math.min(...non_null_values);
9308
+ const mx = Math.max(...non_null_values);
9309
+ const rng = mx - mn || 1;
9310
+ const cx = (i) => data.length <= 1 ? width / 2 : PAD2 + i * (width - 2 * PAD2) / (data.length - 1);
9311
+ const cy = (v) => PAD2 + (v - mn) * (height - 2 * PAD2) / rng;
9312
+ let line_d = "";
9313
+ data.forEach((v, i) => {
9314
+ if (v === null || Number.isNaN(v)) return;
9315
+ line_d += `${line_d ? " L" : "M"}${cx(i).toFixed(1)},${cy(v).toFixed(1)}`;
9316
+ });
9317
+ return /* @__PURE__ */ jsx(
9318
+ "svg",
9319
+ {
9320
+ viewBox: `0 0 ${width} ${height}`,
9321
+ preserveAspectRatio: "none",
9322
+ className,
9323
+ style: { width: `${width}px`, height: `${height}px`, display: "block" },
9324
+ children: line_d && /* @__PURE__ */ jsx("path", { d: line_d, stroke: color2, strokeWidth: 1.4, fill: "none" })
9325
+ }
9326
+ );
9327
+ }
9328
+
9329
+ // src/components/hazo_ui_charts/format.ts
9330
+ function format_num(n) {
9331
+ if (n === null || n === void 0) return "";
9332
+ if (typeof n === "string") return n;
9333
+ if (Number.isNaN(n)) return "";
9334
+ const abs = Math.abs(n);
9335
+ if (abs >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
9336
+ if (abs >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
9337
+ if (Number.isInteger(n)) return n.toString();
9338
+ return n.toFixed(1);
9339
+ }
9340
+ function pick_x_label_indices(length) {
9341
+ if (length <= 0) return [0, 0, 0];
9342
+ if (length === 1) return [0, 0, 0];
9343
+ if (length === 2) return [0, 0, 1];
9344
+ return [0, Math.floor(length / 2), length - 1];
9345
+ }
9346
+ var PAD_LEFT = 38;
9347
+ var PAD_RIGHT = 14;
9348
+ var PAD_TOP = 12;
9349
+ var PAD_BOTTOM = 22;
9350
+ var AXIS_LABEL_COLOR = "#8b949e";
9351
+ var GRIDLINE_COLOR = "#2a3441";
9352
+ function compute_geometry(data, width, height) {
9353
+ const vals = data.filter((v) => v !== null && !Number.isNaN(v));
9354
+ if (vals.length === 0) return null;
9355
+ const mn = Math.min(...vals);
9356
+ const mx = Math.max(...vals);
9357
+ const rng = mx - mn || mx * 0.1 + 1;
9358
+ const y_min = Math.max(0, mn - rng * 0.1);
9359
+ const y_max = mx + rng * 0.1;
9360
+ const y_rng = y_max - y_min || 1;
9361
+ const plot_w = width - PAD_LEFT - PAD_RIGHT;
9362
+ const plot_h = height - PAD_TOP - PAD_BOTTOM;
9363
+ return {
9364
+ vbox_w: width,
9365
+ vbox_h: height,
9366
+ y_min,
9367
+ y_max,
9368
+ cx: (i) => data.length <= 1 ? PAD_LEFT + plot_w / 2 : PAD_LEFT + i * plot_w / (data.length - 1),
9369
+ cy: (v) => PAD_TOP + (1 - (v - y_min) / y_rng) * plot_h
9370
+ };
9371
+ }
9372
+ function build_paths(data, g) {
9373
+ let line_d = "";
9374
+ let first_idx = -1;
9375
+ data.forEach((v, i) => {
9376
+ if (v === null || Number.isNaN(v)) return;
9377
+ if (first_idx === -1) first_idx = i;
9378
+ line_d += `${line_d ? " L" : "M"}${g.cx(i).toFixed(1)},${g.cy(v).toFixed(1)}`;
9379
+ });
9380
+ const last_idx = data.length - 1;
9381
+ const last_v = data[last_idx];
9382
+ 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` : "";
9383
+ return { line_d, area_d };
9384
+ }
9385
+ function LineChart({
9386
+ data,
9387
+ dates,
9388
+ color: color2,
9389
+ width = 360,
9390
+ height = 130,
9391
+ unit = "",
9392
+ showTooltip = true,
9393
+ className
9394
+ }) {
9395
+ const geo = compute_geometry(data, width, height);
9396
+ const svg_ref = React25.useRef(null);
9397
+ const [hover_idx, set_hover_idx] = React25.useState(null);
9398
+ const handle_mouse_move = React25.useCallback(
9399
+ (e) => {
9400
+ if (!geo) return;
9401
+ const rect = e.currentTarget.getBoundingClientRect();
9402
+ if (rect.width === 0) return;
9403
+ const vbox_x = (e.clientX - rect.left) / rect.width * geo.vbox_w;
9404
+ const plot_w = geo.vbox_w - PAD_LEFT - PAD_RIGHT;
9405
+ if (vbox_x < PAD_LEFT || vbox_x > geo.vbox_w - PAD_RIGHT) {
9406
+ set_hover_idx(null);
9407
+ return;
9408
+ }
9409
+ const ratio = (vbox_x - PAD_LEFT) / plot_w;
9410
+ const idx = Math.round(ratio * (data.length - 1));
9411
+ const clamped = Math.max(0, Math.min(data.length - 1, idx));
9412
+ let resolved = clamped;
9413
+ if (data[resolved] === null || Number.isNaN(data[resolved])) {
9414
+ for (let step = 1; step < data.length; step += 1) {
9415
+ const left_i = clamped - step;
9416
+ const right_i = clamped + step;
9417
+ if (left_i >= 0 && data[left_i] !== null && !Number.isNaN(data[left_i])) {
9418
+ resolved = left_i;
9419
+ break;
9420
+ }
9421
+ if (right_i < data.length && data[right_i] !== null && !Number.isNaN(data[right_i])) {
9422
+ resolved = right_i;
9423
+ break;
9424
+ }
9425
+ }
9426
+ }
9427
+ set_hover_idx(resolved);
9428
+ },
9429
+ [geo, data]
9430
+ );
9431
+ const handle_mouse_leave = React25.useCallback(() => set_hover_idx(null), []);
9432
+ if (!geo) {
9433
+ return /* @__PURE__ */ jsx(
9434
+ "svg",
9435
+ {
9436
+ viewBox: `0 0 ${width} ${height}`,
9437
+ className: cn("cls_hazo_chart cls_hazo_chart_empty", className),
9438
+ style: { width: "100%", height: "auto", display: "block", maxHeight: `${height + 10}px` }
9439
+ }
9440
+ );
9441
+ }
9442
+ const { line_d, area_d } = build_paths(data, geo);
9443
+ const x_label_idx = pick_x_label_indices(data.length);
9444
+ const last_idx = data.length - 1;
9445
+ const last_v = data[last_idx];
9446
+ const has_last = last_v !== null && last_v !== void 0 && !Number.isNaN(last_v);
9447
+ const hover_v = hover_idx !== null ? data[hover_idx] : null;
9448
+ const hover_has_v = hover_v !== null && hover_v !== void 0 && !Number.isNaN(hover_v);
9449
+ return /* @__PURE__ */ jsxs(
9450
+ "svg",
9451
+ {
9452
+ ref: svg_ref,
9453
+ viewBox: `0 0 ${width} ${height}`,
9454
+ onMouseMove: showTooltip ? handle_mouse_move : void 0,
9455
+ onMouseLeave: showTooltip ? handle_mouse_leave : void 0,
9456
+ className: cn("cls_hazo_chart", className),
9457
+ style: {
9458
+ width: "100%",
9459
+ height: "auto",
9460
+ display: "block",
9461
+ maxHeight: `${height + 10}px`,
9462
+ cursor: showTooltip ? "crosshair" : "default"
9463
+ },
9464
+ children: [
9465
+ [0, 1, 2].map((i) => {
9466
+ const y = PAD_TOP + i / 2 * (geo.vbox_h - PAD_TOP - PAD_BOTTOM);
9467
+ return /* @__PURE__ */ jsx(
9468
+ "line",
9469
+ {
9470
+ x1: PAD_LEFT,
9471
+ x2: geo.vbox_w - PAD_RIGHT,
9472
+ y1: y,
9473
+ y2: y,
9474
+ stroke: GRIDLINE_COLOR,
9475
+ strokeWidth: 0.5,
9476
+ strokeDasharray: "2,3"
9477
+ },
9478
+ i
9479
+ );
9480
+ }),
9481
+ area_d && /* @__PURE__ */ jsx("path", { d: area_d, fill: color2, fillOpacity: 0.12, stroke: "none" }),
9482
+ line_d && /* @__PURE__ */ jsx("path", { d: line_d, stroke: color2, strokeWidth: 1.8, fill: "none" }),
9483
+ /* @__PURE__ */ jsx("text", { x: PAD_LEFT - 4, y: PAD_TOP + 3, textAnchor: "end", fill: AXIS_LABEL_COLOR, fontSize: 9, children: format_num(geo.y_max) }),
9484
+ /* @__PURE__ */ jsx(
9485
+ "text",
9486
+ {
9487
+ x: PAD_LEFT - 4,
9488
+ y: PAD_TOP + (geo.vbox_h - PAD_TOP - PAD_BOTTOM) / 2 + 3,
9489
+ textAnchor: "end",
9490
+ fill: AXIS_LABEL_COLOR,
9491
+ fontSize: 9,
9492
+ children: format_num((geo.y_max + geo.y_min) / 2)
9493
+ }
9494
+ ),
9495
+ /* @__PURE__ */ jsx(
9496
+ "text",
9497
+ {
9498
+ x: PAD_LEFT - 4,
9499
+ y: geo.vbox_h - PAD_BOTTOM + 3,
9500
+ textAnchor: "end",
9501
+ fill: AXIS_LABEL_COLOR,
9502
+ fontSize: 9,
9503
+ children: format_num(geo.y_min)
9504
+ }
9505
+ ),
9506
+ x_label_idx.map((idx, k) => {
9507
+ const anchor = k === 0 ? "start" : k === 1 ? "middle" : "end";
9508
+ const x_pos = geo.cx(idx);
9509
+ return /* @__PURE__ */ jsx(
9510
+ "text",
9511
+ {
9512
+ x: x_pos,
9513
+ y: geo.vbox_h - 6,
9514
+ textAnchor: anchor,
9515
+ fill: AXIS_LABEL_COLOR,
9516
+ fontSize: 9,
9517
+ children: dates[idx] ?? ""
9518
+ },
9519
+ `x_${k}`
9520
+ );
9521
+ }),
9522
+ has_last && hover_idx === null && (() => {
9523
+ const x = geo.cx(last_idx);
9524
+ const y = geo.cy(last_v);
9525
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
9526
+ /* @__PURE__ */ jsx(
9527
+ "line",
9528
+ {
9529
+ x1: x,
9530
+ x2: PAD_LEFT,
9531
+ y1: y,
9532
+ y2: y,
9533
+ stroke: color2,
9534
+ strokeWidth: 0.5,
9535
+ strokeDasharray: "2,2",
9536
+ opacity: 0.4
9537
+ }
9538
+ ),
9539
+ /* @__PURE__ */ jsx("circle", { cx: x, cy: y, r: 3.5, fill: color2, stroke: color2, strokeWidth: 2, fillOpacity: 0.3 }),
9540
+ /* @__PURE__ */ jsx("circle", { cx: x, cy: y, r: 2, fill: color2 }),
9541
+ /* @__PURE__ */ jsxs("text", { x: x - 6, y: y - 6, textAnchor: "end", fill: color2, fontSize: 10, fontWeight: 700, children: [
9542
+ format_num(last_v),
9543
+ unit
9544
+ ] })
9545
+ ] });
9546
+ })(),
9547
+ showTooltip && hover_idx !== null && hover_has_v && (() => {
9548
+ const x = geo.cx(hover_idx);
9549
+ const y = geo.cy(hover_v);
9550
+ const label = `${format_num(hover_v)}${unit}`;
9551
+ const date = dates[hover_idx] ?? "";
9552
+ const bubble_text_w = Math.max(label.length, date.length) * 5.5 + 12;
9553
+ const bubble_w = Math.max(bubble_text_w, 40);
9554
+ const bubble_h = 26;
9555
+ const flip = x + bubble_w + 6 > geo.vbox_w - PAD_RIGHT;
9556
+ const bubble_x = flip ? x - bubble_w - 6 : x + 6;
9557
+ const bubble_y = Math.max(PAD_TOP, Math.min(y - bubble_h / 2, geo.vbox_h - PAD_BOTTOM - bubble_h));
9558
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
9559
+ /* @__PURE__ */ jsx(
9560
+ "line",
9561
+ {
9562
+ x1: x,
9563
+ x2: x,
9564
+ y1: PAD_TOP,
9565
+ y2: geo.vbox_h - PAD_BOTTOM,
9566
+ stroke: color2,
9567
+ strokeWidth: 0.5,
9568
+ strokeDasharray: "2,2",
9569
+ opacity: 0.6
9570
+ }
9571
+ ),
9572
+ /* @__PURE__ */ jsx("circle", { cx: x, cy: y, r: 3, fill: color2 }),
9573
+ /* @__PURE__ */ jsx(
9574
+ "rect",
9575
+ {
9576
+ x: bubble_x,
9577
+ y: bubble_y,
9578
+ width: bubble_w,
9579
+ height: bubble_h,
9580
+ rx: 3,
9581
+ fill: "#0d1117",
9582
+ stroke: color2,
9583
+ strokeWidth: 0.5,
9584
+ fillOpacity: 0.92
9585
+ }
9586
+ ),
9587
+ /* @__PURE__ */ jsx(
9588
+ "text",
9589
+ {
9590
+ x: bubble_x + bubble_w / 2,
9591
+ y: bubble_y + 11,
9592
+ textAnchor: "middle",
9593
+ fill: color2,
9594
+ fontSize: 10,
9595
+ fontWeight: 700,
9596
+ children: label
9597
+ }
9598
+ ),
9599
+ /* @__PURE__ */ jsx(
9600
+ "text",
9601
+ {
9602
+ x: bubble_x + bubble_w / 2,
9603
+ y: bubble_y + 22,
9604
+ textAnchor: "middle",
9605
+ fill: AXIS_LABEL_COLOR,
9606
+ fontSize: 8,
9607
+ children: date
9608
+ }
9609
+ )
9610
+ ] });
9611
+ })()
9612
+ ]
9613
+ }
9614
+ );
9615
+ }
9616
+ var PAD_LEFT2 = 38;
9617
+ var PAD_RIGHT2 = 14;
9618
+ var PAD_TOP2 = 12;
9619
+ var PAD_BOTTOM2 = 22;
9620
+ var AXIS_LABEL_COLOR2 = "#8b949e";
9621
+ var GRIDLINE_COLOR2 = "#2a3441";
9622
+ function compute_geometry2(series, width, height) {
9623
+ const all_vals = [];
9624
+ let n = 0;
9625
+ for (const s of series) {
9626
+ n = Math.max(n, s.data.length);
9627
+ for (const v of s.data) if (v !== null && !Number.isNaN(v)) all_vals.push(v);
9628
+ }
9629
+ if (all_vals.length === 0 || n === 0) return null;
9630
+ const mn = Math.min(...all_vals);
9631
+ const mx = Math.max(...all_vals);
9632
+ const rng = mx - mn || mx * 0.1 + 1;
9633
+ const y_min = Math.max(0, mn - rng * 0.05);
9634
+ const y_max = mx + rng * 0.05;
9635
+ const y_rng = y_max - y_min || 1;
9636
+ const plot_w = width - PAD_LEFT2 - PAD_RIGHT2;
9637
+ const plot_h = height - PAD_TOP2 - PAD_BOTTOM2;
9638
+ return {
9639
+ vbox_w: width,
9640
+ vbox_h: height,
9641
+ y_min,
9642
+ y_max,
9643
+ point_count: n,
9644
+ cx: (i) => n <= 1 ? PAD_LEFT2 + plot_w / 2 : PAD_LEFT2 + i * plot_w / (n - 1),
9645
+ cy: (v) => PAD_TOP2 + (1 - (v - y_min) / y_rng) * plot_h
9646
+ };
9647
+ }
9648
+ function MultiLineChart({
9649
+ series,
9650
+ dates,
9651
+ width = 360,
9652
+ height = 140,
9653
+ showTooltip = true,
9654
+ showLegend = true,
9655
+ className
9656
+ }) {
9657
+ const geo = compute_geometry2(series, width, height);
9658
+ const [hover_idx, set_hover_idx] = React25.useState(null);
9659
+ const handle_mouse_move = React25.useCallback(
9660
+ (e) => {
9661
+ if (!geo) return;
9662
+ const rect = e.currentTarget.getBoundingClientRect();
9663
+ if (rect.width === 0) return;
9664
+ const vbox_x = (e.clientX - rect.left) / rect.width * geo.vbox_w;
9665
+ const plot_w = geo.vbox_w - PAD_LEFT2 - PAD_RIGHT2;
9666
+ if (vbox_x < PAD_LEFT2 || vbox_x > geo.vbox_w - PAD_RIGHT2) {
9667
+ set_hover_idx(null);
9668
+ return;
9669
+ }
9670
+ const ratio = (vbox_x - PAD_LEFT2) / plot_w;
9671
+ const idx = Math.round(ratio * (geo.point_count - 1));
9672
+ set_hover_idx(Math.max(0, Math.min(geo.point_count - 1, idx)));
9673
+ },
9674
+ [geo]
9675
+ );
9676
+ const handle_mouse_leave = React25.useCallback(() => set_hover_idx(null), []);
9677
+ if (!geo) {
9678
+ return /* @__PURE__ */ jsx(
9679
+ "svg",
9680
+ {
9681
+ viewBox: `0 0 ${width} ${height}`,
9682
+ className: cn("cls_hazo_chart cls_hazo_chart_empty", className),
9683
+ style: { width: "100%", height: "auto", display: "block", maxHeight: `${height + 10}px` }
9684
+ }
9685
+ );
9686
+ }
9687
+ const x_label_idx = pick_x_label_indices(geo.point_count);
9688
+ return /* @__PURE__ */ jsxs("div", { className: cn("cls_hazo_chart_wrapper", className), children: [
9689
+ /* @__PURE__ */ jsxs(
9690
+ "svg",
9691
+ {
9692
+ viewBox: `0 0 ${width} ${height}`,
9693
+ onMouseMove: showTooltip ? handle_mouse_move : void 0,
9694
+ onMouseLeave: showTooltip ? handle_mouse_leave : void 0,
9695
+ className: "cls_hazo_chart cls_hazo_chart_multi",
9696
+ style: {
9697
+ width: "100%",
9698
+ height: "auto",
9699
+ display: "block",
9700
+ maxHeight: `${height + 10}px`,
9701
+ cursor: showTooltip ? "crosshair" : "default"
9702
+ },
9703
+ children: [
9704
+ [0, 1, 2].map((i) => {
9705
+ const y = PAD_TOP2 + i / 2 * (geo.vbox_h - PAD_TOP2 - PAD_BOTTOM2);
9706
+ return /* @__PURE__ */ jsx(
9707
+ "line",
9708
+ {
9709
+ x1: PAD_LEFT2,
9710
+ x2: geo.vbox_w - PAD_RIGHT2,
9711
+ y1: y,
9712
+ y2: y,
9713
+ stroke: GRIDLINE_COLOR2,
9714
+ strokeWidth: 0.5,
9715
+ strokeDasharray: "2,3"
9716
+ },
9717
+ i
9718
+ );
9719
+ }),
9720
+ /* @__PURE__ */ jsx("text", { x: PAD_LEFT2 - 4, y: PAD_TOP2 + 3, textAnchor: "end", fill: AXIS_LABEL_COLOR2, fontSize: 9, children: format_num(geo.y_max) }),
9721
+ /* @__PURE__ */ jsx(
9722
+ "text",
9723
+ {
9724
+ x: PAD_LEFT2 - 4,
9725
+ y: PAD_TOP2 + (geo.vbox_h - PAD_TOP2 - PAD_BOTTOM2) / 2 + 3,
9726
+ textAnchor: "end",
9727
+ fill: AXIS_LABEL_COLOR2,
9728
+ fontSize: 9,
9729
+ children: format_num((geo.y_max + geo.y_min) / 2)
9730
+ }
9731
+ ),
9732
+ /* @__PURE__ */ 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) }),
9733
+ x_label_idx.map((idx, k) => {
9734
+ const anchor = k === 0 ? "start" : k === 1 ? "middle" : "end";
9735
+ return /* @__PURE__ */ jsx(
9736
+ "text",
9737
+ {
9738
+ x: geo.cx(idx),
9739
+ y: geo.vbox_h - 6,
9740
+ textAnchor: anchor,
9741
+ fill: AXIS_LABEL_COLOR2,
9742
+ fontSize: 9,
9743
+ children: dates[idx] ?? ""
9744
+ },
9745
+ `x_${k}`
9746
+ );
9747
+ }),
9748
+ series.map((s, s_idx) => {
9749
+ let d = "";
9750
+ s.data.forEach((v, i) => {
9751
+ if (v === null || Number.isNaN(v)) return;
9752
+ d += `${d ? " L" : "M"}${geo.cx(i).toFixed(1)},${geo.cy(v).toFixed(1)}`;
9753
+ });
9754
+ const last_idx = s.data.length - 1;
9755
+ const last_v = s.data[last_idx];
9756
+ const has_last = last_v !== null && last_v !== void 0 && !Number.isNaN(last_v);
9757
+ return /* @__PURE__ */ jsxs("g", { children: [
9758
+ d && /* @__PURE__ */ jsx("path", { d, stroke: s.color, strokeWidth: 1.7, fill: "none" }),
9759
+ has_last && hover_idx === null && /* @__PURE__ */ jsxs(Fragment$1, { children: [
9760
+ /* @__PURE__ */ jsx(
9761
+ "circle",
9762
+ {
9763
+ cx: geo.cx(last_idx),
9764
+ cy: geo.cy(last_v),
9765
+ r: 3,
9766
+ fill: s.color,
9767
+ fillOpacity: 0.3,
9768
+ stroke: s.color,
9769
+ strokeWidth: 1.5
9770
+ }
9771
+ ),
9772
+ /* @__PURE__ */ jsx(
9773
+ "text",
9774
+ {
9775
+ x: geo.cx(last_idx) - 5,
9776
+ y: geo.cy(last_v) - 5,
9777
+ textAnchor: "end",
9778
+ fill: s.color,
9779
+ fontSize: 9,
9780
+ fontWeight: 700,
9781
+ children: format_num(last_v)
9782
+ }
9783
+ )
9784
+ ] })
9785
+ ] }, `series_${s_idx}`);
9786
+ }),
9787
+ showTooltip && hover_idx !== null && (() => {
9788
+ const x = geo.cx(hover_idx);
9789
+ const items = series.map((s) => ({
9790
+ label: s.label,
9791
+ color: s.color,
9792
+ value: s.data[hover_idx] ?? null
9793
+ })).filter((it) => it.value !== null && !Number.isNaN(it.value));
9794
+ if (items.length === 0) return null;
9795
+ const bubble_w = 70;
9796
+ const row_h = 12;
9797
+ const bubble_h = items.length * row_h + 18;
9798
+ const flip = x + bubble_w + 6 > geo.vbox_w - PAD_RIGHT2;
9799
+ const bubble_x = flip ? x - bubble_w - 6 : x + 6;
9800
+ const bubble_y = Math.max(PAD_TOP2, Math.min(PAD_TOP2 + 5, geo.vbox_h - PAD_BOTTOM2 - bubble_h));
9801
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
9802
+ /* @__PURE__ */ jsx(
9803
+ "line",
9804
+ {
9805
+ x1: x,
9806
+ x2: x,
9807
+ y1: PAD_TOP2,
9808
+ y2: geo.vbox_h - PAD_BOTTOM2,
9809
+ stroke: AXIS_LABEL_COLOR2,
9810
+ strokeWidth: 0.5,
9811
+ strokeDasharray: "2,2",
9812
+ opacity: 0.6
9813
+ }
9814
+ ),
9815
+ items.map((it) => /* @__PURE__ */ jsx(
9816
+ "circle",
9817
+ {
9818
+ cx: x,
9819
+ cy: geo.cy(it.value),
9820
+ r: 3,
9821
+ fill: it.color
9822
+ },
9823
+ `hd_${it.label}`
9824
+ )),
9825
+ /* @__PURE__ */ jsx(
9826
+ "rect",
9827
+ {
9828
+ x: bubble_x,
9829
+ y: bubble_y,
9830
+ width: bubble_w,
9831
+ height: bubble_h,
9832
+ rx: 3,
9833
+ fill: "#0d1117",
9834
+ stroke: AXIS_LABEL_COLOR2,
9835
+ strokeWidth: 0.5,
9836
+ fillOpacity: 0.92
9837
+ }
9838
+ ),
9839
+ /* @__PURE__ */ jsx("text", { x: bubble_x + 6, y: bubble_y + 10, fill: AXIS_LABEL_COLOR2, fontSize: 8, children: dates[hover_idx] ?? "" }),
9840
+ items.map((it, i) => /* @__PURE__ */ jsxs("g", { children: [
9841
+ /* @__PURE__ */ jsx(
9842
+ "rect",
9843
+ {
9844
+ x: bubble_x + 6,
9845
+ y: bubble_y + 14 + i * row_h,
9846
+ width: 6,
9847
+ height: 6,
9848
+ fill: it.color
9849
+ }
9850
+ ),
9851
+ /* @__PURE__ */ jsx(
9852
+ "text",
9853
+ {
9854
+ x: bubble_x + 16,
9855
+ y: bubble_y + 20 + i * row_h,
9856
+ fill: it.color,
9857
+ fontSize: 9,
9858
+ fontWeight: 600,
9859
+ children: format_num(it.value)
9860
+ }
9861
+ )
9862
+ ] }, `row_${i}`))
9863
+ ] });
9864
+ })()
9865
+ ]
9866
+ }
9867
+ ),
9868
+ showLegend && /* @__PURE__ */ jsx(
9869
+ "div",
9870
+ {
9871
+ className: "cls_hazo_chart_legend",
9872
+ style: {
9873
+ display: "flex",
9874
+ gap: "12px",
9875
+ justifyContent: "center",
9876
+ marginTop: "4px",
9877
+ fontSize: "10px",
9878
+ color: AXIS_LABEL_COLOR2,
9879
+ flexWrap: "wrap"
9880
+ },
9881
+ children: series.map((s) => /* @__PURE__ */ jsxs(
9882
+ "span",
9883
+ {
9884
+ style: { display: "inline-flex", alignItems: "center", gap: "4px" },
9885
+ children: [
9886
+ /* @__PURE__ */ jsx(
9887
+ "i",
9888
+ {
9889
+ style: {
9890
+ display: "inline-block",
9891
+ width: "8px",
9892
+ height: "8px",
9893
+ background: s.color,
9894
+ borderRadius: "1px"
9895
+ }
9896
+ }
9897
+ ),
9898
+ s.label
9899
+ ]
9900
+ },
9901
+ s.label
9902
+ ))
9903
+ }
9904
+ )
9905
+ ] });
9906
+ }
9907
+ var PAD_LEFT3 = 32;
9908
+ var PAD_RIGHT3 = 8;
9909
+ var PAD_TOP3 = 10;
9910
+ var PAD_BOTTOM3 = 22;
9911
+ var BAR_GAP_RATIO = 0.25;
9912
+ var AXIS_LABEL_COLOR3 = "#8b949e";
9913
+ function StackedBars({
9914
+ bars,
9915
+ width = 360,
9916
+ height = 140,
9917
+ showYAxis = true,
9918
+ className
9919
+ }) {
9920
+ const totals = bars.map((b) => b.segments.reduce((sum, s) => sum + s.value, 0));
9921
+ const y_max = Math.max(0, ...totals) || 1;
9922
+ const plot_w = width - PAD_LEFT3 - PAD_RIGHT3;
9923
+ const plot_h = height - PAD_TOP3 - PAD_BOTTOM3;
9924
+ const slot_w = bars.length > 0 ? plot_w / bars.length : 0;
9925
+ const bar_w = slot_w * (1 - BAR_GAP_RATIO);
9926
+ const x_for = (i) => PAD_LEFT3 + i * slot_w + (slot_w - bar_w) / 2;
9927
+ const x_label_idx = pick_x_label_indices(bars.length);
9928
+ return /* @__PURE__ */ jsxs(
9929
+ "svg",
9930
+ {
9931
+ viewBox: `0 0 ${width} ${height}`,
9932
+ className: cn("cls_hazo_chart cls_hazo_chart_stacked_bars", className),
9933
+ style: { width: "100%", height: "auto", display: "block", maxHeight: `${height + 10}px` },
9934
+ children: [
9935
+ showYAxis && /* @__PURE__ */ jsxs(Fragment$1, { children: [
9936
+ /* @__PURE__ */ jsx("text", { x: PAD_LEFT3 - 4, y: PAD_TOP3 + 3, textAnchor: "end", fill: AXIS_LABEL_COLOR3, fontSize: 9, children: format_num(y_max) }),
9937
+ /* @__PURE__ */ jsx(
9938
+ "text",
9939
+ {
9940
+ x: PAD_LEFT3 - 4,
9941
+ y: PAD_TOP3 + plot_h / 2 + 3,
9942
+ textAnchor: "end",
9943
+ fill: AXIS_LABEL_COLOR3,
9944
+ fontSize: 9,
9945
+ children: format_num(y_max / 2)
9946
+ }
9947
+ ),
9948
+ /* @__PURE__ */ jsx(
9949
+ "text",
9950
+ {
9951
+ x: PAD_LEFT3 - 4,
9952
+ y: height - PAD_BOTTOM3 + 3,
9953
+ textAnchor: "end",
9954
+ fill: AXIS_LABEL_COLOR3,
9955
+ fontSize: 9,
9956
+ children: "0"
9957
+ }
9958
+ )
9959
+ ] }),
9960
+ bars.map((bar, i) => {
9961
+ let cursor_y = height - PAD_BOTTOM3;
9962
+ return /* @__PURE__ */ jsx("g", { children: bar.segments.map((seg, s_idx) => {
9963
+ if (seg.value <= 0) return null;
9964
+ const seg_h = seg.value / y_max * plot_h;
9965
+ const y = cursor_y - seg_h;
9966
+ cursor_y = y;
9967
+ return /* @__PURE__ */ jsx(
9968
+ "rect",
9969
+ {
9970
+ x: x_for(i),
9971
+ y,
9972
+ width: bar_w,
9973
+ height: seg_h,
9974
+ fill: seg.color
9975
+ },
9976
+ `seg_${s_idx}`
9977
+ );
9978
+ }) }, `bar_${i}`);
9979
+ }),
9980
+ x_label_idx.map((idx, k) => {
9981
+ const anchor = k === 0 ? "start" : k === 1 ? "middle" : "end";
9982
+ const x_pos = x_for(idx) + bar_w / 2;
9983
+ return /* @__PURE__ */ jsx(
9984
+ "text",
9985
+ {
9986
+ x: x_pos,
9987
+ y: height - 6,
9988
+ textAnchor: anchor,
9989
+ fill: AXIS_LABEL_COLOR3,
9990
+ fontSize: 9,
9991
+ children: bars[idx]?.label ?? ""
9992
+ },
9993
+ `x_${k}`
9994
+ );
9995
+ })
9996
+ ]
9997
+ }
9998
+ );
9999
+ }
10000
+ function DateRangeSelector({
10001
+ value,
10002
+ onChange,
10003
+ options,
10004
+ className,
10005
+ ariaLabel = "Date range"
10006
+ }) {
10007
+ return /* @__PURE__ */ jsx(
10008
+ "div",
10009
+ {
10010
+ role: "group",
10011
+ "aria-label": ariaLabel,
10012
+ className: cn(
10013
+ "cls_hazo_date_range_selector inline-flex items-center gap-0 rounded-md border bg-background p-0.5",
10014
+ className
10015
+ ),
10016
+ children: options.map((opt) => {
10017
+ const is_active = opt.value === value;
10018
+ return /* @__PURE__ */ jsx(
10019
+ "button",
10020
+ {
10021
+ type: "button",
10022
+ "aria-pressed": is_active,
10023
+ onClick: () => {
10024
+ if (!is_active) onChange(opt.value);
10025
+ },
10026
+ className: cn(
10027
+ "cls_hazo_date_range_option px-2.5 py-1 text-xs font-medium rounded-sm transition-colors",
10028
+ is_active ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground hover:bg-muted"
10029
+ ),
10030
+ children: opt.label
10031
+ },
10032
+ opt.value
10033
+ );
10034
+ })
10035
+ }
10036
+ );
10037
+ }
9236
10038
 
9237
- export { ANIMATION_PRESETS, Accordion, AccordionContent, AccordionItem, AccordionTrigger, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Collapsible, CollapsibleContent2 as CollapsibleContent, CollapsibleTrigger2 as CollapsibleTrigger, CommandNodeExtension, CommandPill, CommandPopover, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, ErrorBanner, ErrorPage, HazoUiConfirmDialog, HazoUiDialog, DialogClose as HazoUiDialogClose, DialogContent as HazoUiDialogContent, DialogDescription as HazoUiDialogDescription, DialogFooter as HazoUiDialogFooter, DialogHeader as HazoUiDialogHeader, DialogOverlay as HazoUiDialogOverlay, DialogPortal as HazoUiDialogPortal, Dialog as HazoUiDialogRoot, DialogTitle as HazoUiDialogTitle, DialogTrigger as HazoUiDialogTrigger, HazoUiFlexInput, HazoUiFlexRadio, HazoUiKanban, HazoUiKanbanFilter, HazoUiMultiFilterDialog, HazoUiMultiSortDialog, HazoUiPillRadio, HazoUiRte, HazoUiTable, HazoUiTextarea, HazoUiTextbox, HazoUiToaster, HoverCard, HoverCardContent, HoverCardTrigger, Input, Label3 as Label, LoadingTimeout, Popover, PopoverContent, PopoverTrigger, ProgressiveImage, RadioGroup, RadioGroupItem, ScrollArea, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator3 as Separator, Command as ShadcnCommand, CommandEmpty as ShadcnCommandEmpty, CommandGroup as ShadcnCommandGroup, CommandInput as ShadcnCommandInput, CommandItem as ShadcnCommandItem, CommandList as ShadcnCommandList, Skeleton, SkeletonBar, SkeletonCircle, SkeletonGroup, SkeletonRect, Spinner, Switch, Table2 as Table, TableBody, TableCaption, TableCell2 as TableCell, TableFooter, TableHead, TableHeader2 as TableHeader, TableRow2 as TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, applyKanbanFilter, buttonGroupVariants, create_command_suggestion_extension, errorToast, get_hazo_ui_config, parse_commands_from_text, reset_hazo_ui_config, resolve_animation_classes, set_hazo_ui_config, successToast, text_to_tiptap_content, toggleVariants, useErrorDisplay, useLoadingState, useMediaQuery };
10039
+ export { ANIMATION_PRESETS, Accordion, AccordionContent, AccordionItem, AccordionTrigger, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Collapsible, CollapsibleContent2 as CollapsibleContent, CollapsibleTrigger2 as CollapsibleTrigger, CommandNodeExtension, CommandPill, CommandPopover, DateRangeSelector, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, ErrorBanner, ErrorPage, HazoUiConfirmDialog, HazoUiDialog, DialogClose as HazoUiDialogClose, DialogContent as HazoUiDialogContent, DialogDescription as HazoUiDialogDescription, DialogFooter as HazoUiDialogFooter, DialogHeader as HazoUiDialogHeader, DialogOverlay as HazoUiDialogOverlay, DialogPortal as HazoUiDialogPortal, Dialog as HazoUiDialogRoot, DialogTitle as HazoUiDialogTitle, DialogTrigger as HazoUiDialogTrigger, HazoUiFlexInput, HazoUiFlexRadio, HazoUiKanban, HazoUiKanbanFilter, HazoUiMultiFilterDialog, HazoUiMultiSortDialog, HazoUiPillRadio, HazoUiRte, HazoUiTable, HazoUiTextarea, HazoUiTextbox, HazoUiToaster, HoverCard, HoverCardContent, HoverCardTrigger, Input, InverseSparkline, Label3 as Label, LineChart, LoadingTimeout, MultiLineChart, Popover, PopoverContent, PopoverTrigger, ProgressiveImage, RadioGroup, RadioGroupItem, ScrollArea, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator3 as Separator, Command as ShadcnCommand, CommandEmpty as ShadcnCommandEmpty, CommandGroup as ShadcnCommandGroup, CommandInput as ShadcnCommandInput, CommandItem as ShadcnCommandItem, CommandList as ShadcnCommandList, Skeleton, SkeletonBar, SkeletonCircle, SkeletonGroup, SkeletonRect, Sparkline, Spinner, StackedBars, Switch, Table2 as Table, TableBody, TableCaption, TableCell2 as TableCell, TableFooter, TableHead, TableHeader2 as TableHeader, TableRow2 as TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, applyKanbanFilter, buttonGroupVariants, create_command_suggestion_extension, errorToast, format_num, get_hazo_ui_config, parse_commands_from_text, pick_x_label_indices, reset_hazo_ui_config, resolve_animation_classes, set_hazo_ui_config, successToast, text_to_tiptap_content, toggleVariants, useErrorDisplay, useLoadingState, useMediaQuery };
9238
10040
  //# sourceMappingURL=index.js.map
9239
10041
  //# sourceMappingURL=index.js.map