hazo_ui 2.16.0 → 3.0.1
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/CHANGE_LOG.md +76 -0
- package/README.md +417 -0
- package/SETUP_CHECKLIST.md +10 -0
- package/dist/index.cjs +1301 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +397 -1
- package/dist/index.d.ts +397 -1
- package/dist/index.js +1283 -2
- package/dist/index.js.map +1 -1
- package/dist/test-harness/index.cjs +18033 -0
- package/dist/test-harness/index.cjs.map +1 -0
- package/dist/test-harness/index.d.cts +144 -0
- package/dist/test-harness/index.d.ts +144 -0
- package/dist/test-harness/index.js +18009 -0
- package/dist/test-harness/index.js.map +1 -0
- package/package.json +8 -1
package/dist/index.cjs
CHANGED
|
@@ -7687,6 +7687,200 @@ function useErrorDisplay() {
|
|
|
7687
7687
|
const clearError = React25.useCallback(() => setRaw(null), []);
|
|
7688
7688
|
return { error, setError, clearError };
|
|
7689
7689
|
}
|
|
7690
|
+
function useDebounce(value, delayMs) {
|
|
7691
|
+
const [debounced_value, set_debounced_value] = React25.useState(value);
|
|
7692
|
+
React25.useEffect(() => {
|
|
7693
|
+
const timer = setTimeout(() => {
|
|
7694
|
+
set_debounced_value(value);
|
|
7695
|
+
}, delayMs);
|
|
7696
|
+
return () => clearTimeout(timer);
|
|
7697
|
+
}, [value, delayMs]);
|
|
7698
|
+
return debounced_value;
|
|
7699
|
+
}
|
|
7700
|
+
function useCopyToClipboard() {
|
|
7701
|
+
const [copied, set_copied] = React25.useState(false);
|
|
7702
|
+
const copy = React25.useCallback(async (text) => {
|
|
7703
|
+
if (typeof navigator === "undefined" || !navigator.clipboard) {
|
|
7704
|
+
return;
|
|
7705
|
+
}
|
|
7706
|
+
try {
|
|
7707
|
+
await navigator.clipboard.writeText(text);
|
|
7708
|
+
set_copied(true);
|
|
7709
|
+
setTimeout(() => set_copied(false), 2e3);
|
|
7710
|
+
} catch {
|
|
7711
|
+
}
|
|
7712
|
+
}, []);
|
|
7713
|
+
return { copied, copy };
|
|
7714
|
+
}
|
|
7715
|
+
|
|
7716
|
+
// src/hooks/use_is_mobile.ts
|
|
7717
|
+
var DEFAULT_BREAKPOINT_PX = 768;
|
|
7718
|
+
function useIsMobile(breakpointPx = DEFAULT_BREAKPOINT_PX) {
|
|
7719
|
+
return useMediaQuery(`(max-width: ${breakpointPx - 1}px)`);
|
|
7720
|
+
}
|
|
7721
|
+
function useViewport(breakpoint = DEFAULT_BREAKPOINT_PX) {
|
|
7722
|
+
return useIsMobile(breakpoint);
|
|
7723
|
+
}
|
|
7724
|
+
function useLocalStorage(key, initialValue) {
|
|
7725
|
+
const read_stored = () => {
|
|
7726
|
+
if (typeof window === "undefined") return initialValue;
|
|
7727
|
+
try {
|
|
7728
|
+
const raw = window.localStorage.getItem(key);
|
|
7729
|
+
return raw !== null ? JSON.parse(raw) : initialValue;
|
|
7730
|
+
} catch {
|
|
7731
|
+
return initialValue;
|
|
7732
|
+
}
|
|
7733
|
+
};
|
|
7734
|
+
const [stored_value, set_stored_value] = React25.useState(read_stored);
|
|
7735
|
+
const set_value = React25.useCallback(
|
|
7736
|
+
(value) => {
|
|
7737
|
+
set_stored_value((prev) => {
|
|
7738
|
+
const next = typeof value === "function" ? value(prev) : value;
|
|
7739
|
+
try {
|
|
7740
|
+
if (typeof window !== "undefined") {
|
|
7741
|
+
window.localStorage.setItem(key, JSON.stringify(next));
|
|
7742
|
+
}
|
|
7743
|
+
} catch {
|
|
7744
|
+
}
|
|
7745
|
+
return next;
|
|
7746
|
+
});
|
|
7747
|
+
},
|
|
7748
|
+
[key]
|
|
7749
|
+
);
|
|
7750
|
+
return [stored_value, set_value];
|
|
7751
|
+
}
|
|
7752
|
+
function useSessionStorage(key, initialValue) {
|
|
7753
|
+
const read_stored = () => {
|
|
7754
|
+
if (typeof window === "undefined") return initialValue;
|
|
7755
|
+
try {
|
|
7756
|
+
const raw = window.sessionStorage.getItem(key);
|
|
7757
|
+
return raw !== null ? JSON.parse(raw) : initialValue;
|
|
7758
|
+
} catch {
|
|
7759
|
+
return initialValue;
|
|
7760
|
+
}
|
|
7761
|
+
};
|
|
7762
|
+
const [stored_value, set_stored_value] = React25.useState(read_stored);
|
|
7763
|
+
const set_value = React25.useCallback(
|
|
7764
|
+
(value) => {
|
|
7765
|
+
set_stored_value((prev) => {
|
|
7766
|
+
const next = typeof value === "function" ? value(prev) : value;
|
|
7767
|
+
try {
|
|
7768
|
+
if (typeof window !== "undefined") {
|
|
7769
|
+
window.sessionStorage.setItem(key, JSON.stringify(next));
|
|
7770
|
+
}
|
|
7771
|
+
} catch {
|
|
7772
|
+
}
|
|
7773
|
+
return next;
|
|
7774
|
+
});
|
|
7775
|
+
},
|
|
7776
|
+
[key]
|
|
7777
|
+
);
|
|
7778
|
+
return [stored_value, set_value];
|
|
7779
|
+
}
|
|
7780
|
+
function useClickOutside(ref, handler) {
|
|
7781
|
+
React25.useEffect(() => {
|
|
7782
|
+
if (typeof document === "undefined") return;
|
|
7783
|
+
const handle_event = (event) => {
|
|
7784
|
+
const target = event.target;
|
|
7785
|
+
if (!ref.current || !target) return;
|
|
7786
|
+
if (!ref.current.contains(target)) {
|
|
7787
|
+
handler();
|
|
7788
|
+
}
|
|
7789
|
+
};
|
|
7790
|
+
document.addEventListener("mousedown", handle_event);
|
|
7791
|
+
document.addEventListener("touchstart", handle_event);
|
|
7792
|
+
return () => {
|
|
7793
|
+
document.removeEventListener("mousedown", handle_event);
|
|
7794
|
+
document.removeEventListener("touchstart", handle_event);
|
|
7795
|
+
};
|
|
7796
|
+
}, [ref, handler]);
|
|
7797
|
+
}
|
|
7798
|
+
function useWakeLock() {
|
|
7799
|
+
const supported = typeof navigator !== "undefined" && "wakeLock" in navigator;
|
|
7800
|
+
const sentinel_ref = React25.useRef(null);
|
|
7801
|
+
const [acquired, set_acquired] = React25.useState(false);
|
|
7802
|
+
const request = React25.useCallback(async () => {
|
|
7803
|
+
if (!supported || sentinel_ref.current) return;
|
|
7804
|
+
try {
|
|
7805
|
+
const sentinel = await navigator.wakeLock.request("screen");
|
|
7806
|
+
sentinel.addEventListener("release", () => {
|
|
7807
|
+
sentinel_ref.current = null;
|
|
7808
|
+
set_acquired(false);
|
|
7809
|
+
});
|
|
7810
|
+
sentinel_ref.current = sentinel;
|
|
7811
|
+
set_acquired(true);
|
|
7812
|
+
} catch {
|
|
7813
|
+
}
|
|
7814
|
+
}, [supported]);
|
|
7815
|
+
const release = React25.useCallback(async () => {
|
|
7816
|
+
if (!sentinel_ref.current) return;
|
|
7817
|
+
try {
|
|
7818
|
+
await sentinel_ref.current.release();
|
|
7819
|
+
} catch {
|
|
7820
|
+
} finally {
|
|
7821
|
+
sentinel_ref.current = null;
|
|
7822
|
+
set_acquired(false);
|
|
7823
|
+
}
|
|
7824
|
+
}, []);
|
|
7825
|
+
React25.useEffect(() => {
|
|
7826
|
+
if (typeof document === "undefined") return;
|
|
7827
|
+
const handle_visibility = () => {
|
|
7828
|
+
if (document.visibilityState === "visible" && acquired && !sentinel_ref.current) {
|
|
7829
|
+
void request();
|
|
7830
|
+
}
|
|
7831
|
+
};
|
|
7832
|
+
document.addEventListener("visibilitychange", handle_visibility);
|
|
7833
|
+
return () => document.removeEventListener("visibilitychange", handle_visibility);
|
|
7834
|
+
}, [acquired, request]);
|
|
7835
|
+
React25.useEffect(() => {
|
|
7836
|
+
return () => {
|
|
7837
|
+
if (sentinel_ref.current) {
|
|
7838
|
+
void sentinel_ref.current.release().catch(() => void 0);
|
|
7839
|
+
sentinel_ref.current = null;
|
|
7840
|
+
}
|
|
7841
|
+
};
|
|
7842
|
+
}, []);
|
|
7843
|
+
return { supported, acquired, request, release };
|
|
7844
|
+
}
|
|
7845
|
+
function useFullscreen(elementRef) {
|
|
7846
|
+
const [is_fullscreen, set_is_fullscreen] = React25.useState(false);
|
|
7847
|
+
React25.useEffect(() => {
|
|
7848
|
+
if (typeof document === "undefined") return;
|
|
7849
|
+
const handle_change = () => {
|
|
7850
|
+
set_is_fullscreen(!!document.fullscreenElement);
|
|
7851
|
+
};
|
|
7852
|
+
document.addEventListener("fullscreenchange", handle_change);
|
|
7853
|
+
set_is_fullscreen(!!document.fullscreenElement);
|
|
7854
|
+
return () => document.removeEventListener("fullscreenchange", handle_change);
|
|
7855
|
+
}, []);
|
|
7856
|
+
const get_target = React25.useCallback(() => {
|
|
7857
|
+
return elementRef?.current ?? document.documentElement;
|
|
7858
|
+
}, [elementRef]);
|
|
7859
|
+
const enter = React25.useCallback(async () => {
|
|
7860
|
+
if (typeof document === "undefined" || !document.documentElement.requestFullscreen) return;
|
|
7861
|
+
if (document.fullscreenElement) return;
|
|
7862
|
+
try {
|
|
7863
|
+
await get_target().requestFullscreen();
|
|
7864
|
+
} catch {
|
|
7865
|
+
}
|
|
7866
|
+
}, [get_target]);
|
|
7867
|
+
const exit = React25.useCallback(async () => {
|
|
7868
|
+
if (typeof document === "undefined") return;
|
|
7869
|
+
if (!document.fullscreenElement) return;
|
|
7870
|
+
try {
|
|
7871
|
+
await document.exitFullscreen();
|
|
7872
|
+
} catch {
|
|
7873
|
+
}
|
|
7874
|
+
}, []);
|
|
7875
|
+
const toggle = React25.useCallback(async () => {
|
|
7876
|
+
if (document.fullscreenElement) {
|
|
7877
|
+
await exit();
|
|
7878
|
+
} else {
|
|
7879
|
+
await enter();
|
|
7880
|
+
}
|
|
7881
|
+
}, [enter, exit]);
|
|
7882
|
+
return { isFullscreen: is_fullscreen, enter, exit, toggle };
|
|
7883
|
+
}
|
|
7690
7884
|
function KanbanCard({
|
|
7691
7885
|
item,
|
|
7692
7886
|
renderCard,
|
|
@@ -9301,6 +9495,1093 @@ function SortIndicator({
|
|
|
9301
9495
|
sort.length > 1 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium", children: idx + 1 })
|
|
9302
9496
|
] });
|
|
9303
9497
|
}
|
|
9498
|
+
var VBOX_W = 200;
|
|
9499
|
+
var PAD = 2;
|
|
9500
|
+
function Sparkline({
|
|
9501
|
+
data,
|
|
9502
|
+
color: color2,
|
|
9503
|
+
className,
|
|
9504
|
+
height = 40
|
|
9505
|
+
}) {
|
|
9506
|
+
const vbox_h = height;
|
|
9507
|
+
const non_null_values = data.filter((v) => v !== null && !Number.isNaN(v));
|
|
9508
|
+
if (non_null_values.length === 0) {
|
|
9509
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9510
|
+
"svg",
|
|
9511
|
+
{
|
|
9512
|
+
viewBox: `0 0 ${VBOX_W} ${vbox_h}`,
|
|
9513
|
+
preserveAspectRatio: "none",
|
|
9514
|
+
className,
|
|
9515
|
+
style: { width: "100%", height: `${height}px`, display: "block" }
|
|
9516
|
+
}
|
|
9517
|
+
);
|
|
9518
|
+
}
|
|
9519
|
+
const mn = Math.min(...non_null_values);
|
|
9520
|
+
const mx = Math.max(...non_null_values);
|
|
9521
|
+
const rng = mx - mn || 1;
|
|
9522
|
+
const cx = (i) => data.length <= 1 ? VBOX_W / 2 : PAD + i * (VBOX_W - 2 * PAD) / (data.length - 1);
|
|
9523
|
+
const cy = (v) => vbox_h - PAD - (v - mn) * (vbox_h - 2 * PAD) / rng;
|
|
9524
|
+
let line_d = "";
|
|
9525
|
+
data.forEach((v, i) => {
|
|
9526
|
+
if (v === null || Number.isNaN(v)) return;
|
|
9527
|
+
line_d += `${line_d ? " L" : "M"}${cx(i).toFixed(1)},${cy(v).toFixed(1)}`;
|
|
9528
|
+
});
|
|
9529
|
+
const first_non_null_idx = data.findIndex((v) => v !== null && !Number.isNaN(v));
|
|
9530
|
+
const last_idx = data.length - 1;
|
|
9531
|
+
const last_v = data[last_idx];
|
|
9532
|
+
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`;
|
|
9533
|
+
const has_last = last_v !== null && last_v !== void 0 && !Number.isNaN(last_v);
|
|
9534
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9535
|
+
"svg",
|
|
9536
|
+
{
|
|
9537
|
+
viewBox: `0 0 ${VBOX_W} ${vbox_h}`,
|
|
9538
|
+
preserveAspectRatio: "none",
|
|
9539
|
+
className,
|
|
9540
|
+
style: { width: "100%", height: `${height}px`, display: "block" },
|
|
9541
|
+
children: [
|
|
9542
|
+
area_d && /* @__PURE__ */ jsxRuntime.jsx("path", { d: area_d, fill: color2, fillOpacity: 0.1, stroke: "none" }),
|
|
9543
|
+
line_d && /* @__PURE__ */ jsxRuntime.jsx("path", { d: line_d, stroke: color2, strokeWidth: 1.5, fill: "none" }),
|
|
9544
|
+
has_last && /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: cx(last_idx), cy: cy(last_v), r: 2, fill: color2 })
|
|
9545
|
+
]
|
|
9546
|
+
}
|
|
9547
|
+
);
|
|
9548
|
+
}
|
|
9549
|
+
var PAD2 = 2;
|
|
9550
|
+
function InverseSparkline({
|
|
9551
|
+
data,
|
|
9552
|
+
color: color2,
|
|
9553
|
+
className,
|
|
9554
|
+
width = 62,
|
|
9555
|
+
height = 18
|
|
9556
|
+
}) {
|
|
9557
|
+
const non_null_values = data.filter((v) => v !== null && !Number.isNaN(v));
|
|
9558
|
+
if (non_null_values.length === 0) {
|
|
9559
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9560
|
+
"svg",
|
|
9561
|
+
{
|
|
9562
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
9563
|
+
preserveAspectRatio: "none",
|
|
9564
|
+
className,
|
|
9565
|
+
style: { width: `${width}px`, height: `${height}px`, display: "block" }
|
|
9566
|
+
}
|
|
9567
|
+
);
|
|
9568
|
+
}
|
|
9569
|
+
const mn = Math.min(...non_null_values);
|
|
9570
|
+
const mx = Math.max(...non_null_values);
|
|
9571
|
+
const rng = mx - mn || 1;
|
|
9572
|
+
const cx = (i) => data.length <= 1 ? width / 2 : PAD2 + i * (width - 2 * PAD2) / (data.length - 1);
|
|
9573
|
+
const cy = (v) => PAD2 + (v - mn) * (height - 2 * PAD2) / rng;
|
|
9574
|
+
let line_d = "";
|
|
9575
|
+
data.forEach((v, i) => {
|
|
9576
|
+
if (v === null || Number.isNaN(v)) return;
|
|
9577
|
+
line_d += `${line_d ? " L" : "M"}${cx(i).toFixed(1)},${cy(v).toFixed(1)}`;
|
|
9578
|
+
});
|
|
9579
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9580
|
+
"svg",
|
|
9581
|
+
{
|
|
9582
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
9583
|
+
preserveAspectRatio: "none",
|
|
9584
|
+
className,
|
|
9585
|
+
style: { width: `${width}px`, height: `${height}px`, display: "block" },
|
|
9586
|
+
children: line_d && /* @__PURE__ */ jsxRuntime.jsx("path", { d: line_d, stroke: color2, strokeWidth: 1.4, fill: "none" })
|
|
9587
|
+
}
|
|
9588
|
+
);
|
|
9589
|
+
}
|
|
9590
|
+
|
|
9591
|
+
// src/components/hazo_ui_charts/format.ts
|
|
9592
|
+
function format_num(n) {
|
|
9593
|
+
if (n === null || n === void 0) return "";
|
|
9594
|
+
if (typeof n === "string") return n;
|
|
9595
|
+
if (Number.isNaN(n)) return "";
|
|
9596
|
+
const abs = Math.abs(n);
|
|
9597
|
+
if (abs >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
9598
|
+
if (abs >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
|
|
9599
|
+
if (Number.isInteger(n)) return n.toString();
|
|
9600
|
+
return n.toFixed(1);
|
|
9601
|
+
}
|
|
9602
|
+
function pick_x_label_indices(length) {
|
|
9603
|
+
if (length <= 0) return [0, 0, 0];
|
|
9604
|
+
if (length === 1) return [0, 0, 0];
|
|
9605
|
+
if (length === 2) return [0, 0, 1];
|
|
9606
|
+
return [0, Math.floor(length / 2), length - 1];
|
|
9607
|
+
}
|
|
9608
|
+
var PAD_LEFT = 38;
|
|
9609
|
+
var PAD_RIGHT = 14;
|
|
9610
|
+
var PAD_TOP = 12;
|
|
9611
|
+
var PAD_BOTTOM = 22;
|
|
9612
|
+
var AXIS_LABEL_COLOR = "#8b949e";
|
|
9613
|
+
var GRIDLINE_COLOR = "#2a3441";
|
|
9614
|
+
function compute_geometry(data, width, height) {
|
|
9615
|
+
const vals = data.filter((v) => v !== null && !Number.isNaN(v));
|
|
9616
|
+
if (vals.length === 0) return null;
|
|
9617
|
+
const mn = Math.min(...vals);
|
|
9618
|
+
const mx = Math.max(...vals);
|
|
9619
|
+
const rng = mx - mn || mx * 0.1 + 1;
|
|
9620
|
+
const y_min = Math.max(0, mn - rng * 0.1);
|
|
9621
|
+
const y_max = mx + rng * 0.1;
|
|
9622
|
+
const y_rng = y_max - y_min || 1;
|
|
9623
|
+
const plot_w = width - PAD_LEFT - PAD_RIGHT;
|
|
9624
|
+
const plot_h = height - PAD_TOP - PAD_BOTTOM;
|
|
9625
|
+
return {
|
|
9626
|
+
vbox_w: width,
|
|
9627
|
+
vbox_h: height,
|
|
9628
|
+
y_min,
|
|
9629
|
+
y_max,
|
|
9630
|
+
cx: (i) => data.length <= 1 ? PAD_LEFT + plot_w / 2 : PAD_LEFT + i * plot_w / (data.length - 1),
|
|
9631
|
+
cy: (v) => PAD_TOP + (1 - (v - y_min) / y_rng) * plot_h
|
|
9632
|
+
};
|
|
9633
|
+
}
|
|
9634
|
+
function build_paths(data, g) {
|
|
9635
|
+
let line_d = "";
|
|
9636
|
+
let first_idx = -1;
|
|
9637
|
+
data.forEach((v, i) => {
|
|
9638
|
+
if (v === null || Number.isNaN(v)) return;
|
|
9639
|
+
if (first_idx === -1) first_idx = i;
|
|
9640
|
+
line_d += `${line_d ? " L" : "M"}${g.cx(i).toFixed(1)},${g.cy(v).toFixed(1)}`;
|
|
9641
|
+
});
|
|
9642
|
+
const last_idx = data.length - 1;
|
|
9643
|
+
const last_v = data[last_idx];
|
|
9644
|
+
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` : "";
|
|
9645
|
+
return { line_d, area_d };
|
|
9646
|
+
}
|
|
9647
|
+
function LineChart({
|
|
9648
|
+
data,
|
|
9649
|
+
dates,
|
|
9650
|
+
color: color2,
|
|
9651
|
+
width = 360,
|
|
9652
|
+
height = 130,
|
|
9653
|
+
unit = "",
|
|
9654
|
+
showTooltip = true,
|
|
9655
|
+
className
|
|
9656
|
+
}) {
|
|
9657
|
+
const geo = compute_geometry(data, width, height);
|
|
9658
|
+
const svg_ref = React25__namespace.useRef(null);
|
|
9659
|
+
const [hover_idx, set_hover_idx] = React25__namespace.useState(null);
|
|
9660
|
+
const handle_mouse_move = React25__namespace.useCallback(
|
|
9661
|
+
(e) => {
|
|
9662
|
+
if (!geo) return;
|
|
9663
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
9664
|
+
if (rect.width === 0) return;
|
|
9665
|
+
const vbox_x = (e.clientX - rect.left) / rect.width * geo.vbox_w;
|
|
9666
|
+
const plot_w = geo.vbox_w - PAD_LEFT - PAD_RIGHT;
|
|
9667
|
+
if (vbox_x < PAD_LEFT || vbox_x > geo.vbox_w - PAD_RIGHT) {
|
|
9668
|
+
set_hover_idx(null);
|
|
9669
|
+
return;
|
|
9670
|
+
}
|
|
9671
|
+
const ratio = (vbox_x - PAD_LEFT) / plot_w;
|
|
9672
|
+
const idx = Math.round(ratio * (data.length - 1));
|
|
9673
|
+
const clamped = Math.max(0, Math.min(data.length - 1, idx));
|
|
9674
|
+
let resolved = clamped;
|
|
9675
|
+
if (data[resolved] === null || Number.isNaN(data[resolved])) {
|
|
9676
|
+
for (let step = 1; step < data.length; step += 1) {
|
|
9677
|
+
const left_i = clamped - step;
|
|
9678
|
+
const right_i = clamped + step;
|
|
9679
|
+
if (left_i >= 0 && data[left_i] !== null && !Number.isNaN(data[left_i])) {
|
|
9680
|
+
resolved = left_i;
|
|
9681
|
+
break;
|
|
9682
|
+
}
|
|
9683
|
+
if (right_i < data.length && data[right_i] !== null && !Number.isNaN(data[right_i])) {
|
|
9684
|
+
resolved = right_i;
|
|
9685
|
+
break;
|
|
9686
|
+
}
|
|
9687
|
+
}
|
|
9688
|
+
}
|
|
9689
|
+
set_hover_idx(resolved);
|
|
9690
|
+
},
|
|
9691
|
+
[geo, data]
|
|
9692
|
+
);
|
|
9693
|
+
const handle_mouse_leave = React25__namespace.useCallback(() => set_hover_idx(null), []);
|
|
9694
|
+
if (!geo) {
|
|
9695
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9696
|
+
"svg",
|
|
9697
|
+
{
|
|
9698
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
9699
|
+
className: cn("cls_hazo_chart cls_hazo_chart_empty", className),
|
|
9700
|
+
style: { width: "100%", height: "auto", display: "block", maxHeight: `${height + 10}px` }
|
|
9701
|
+
}
|
|
9702
|
+
);
|
|
9703
|
+
}
|
|
9704
|
+
const { line_d, area_d } = build_paths(data, geo);
|
|
9705
|
+
const x_label_idx = pick_x_label_indices(data.length);
|
|
9706
|
+
const last_idx = data.length - 1;
|
|
9707
|
+
const last_v = data[last_idx];
|
|
9708
|
+
const has_last = last_v !== null && last_v !== void 0 && !Number.isNaN(last_v);
|
|
9709
|
+
const hover_v = hover_idx !== null ? data[hover_idx] : null;
|
|
9710
|
+
const hover_has_v = hover_v !== null && hover_v !== void 0 && !Number.isNaN(hover_v);
|
|
9711
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9712
|
+
"svg",
|
|
9713
|
+
{
|
|
9714
|
+
ref: svg_ref,
|
|
9715
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
9716
|
+
onMouseMove: showTooltip ? handle_mouse_move : void 0,
|
|
9717
|
+
onMouseLeave: showTooltip ? handle_mouse_leave : void 0,
|
|
9718
|
+
className: cn("cls_hazo_chart", className),
|
|
9719
|
+
style: {
|
|
9720
|
+
width: "100%",
|
|
9721
|
+
height: "auto",
|
|
9722
|
+
display: "block",
|
|
9723
|
+
maxHeight: `${height + 10}px`,
|
|
9724
|
+
cursor: showTooltip ? "crosshair" : "default"
|
|
9725
|
+
},
|
|
9726
|
+
children: [
|
|
9727
|
+
[0, 1, 2].map((i) => {
|
|
9728
|
+
const y = PAD_TOP + i / 2 * (geo.vbox_h - PAD_TOP - PAD_BOTTOM);
|
|
9729
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9730
|
+
"line",
|
|
9731
|
+
{
|
|
9732
|
+
x1: PAD_LEFT,
|
|
9733
|
+
x2: geo.vbox_w - PAD_RIGHT,
|
|
9734
|
+
y1: y,
|
|
9735
|
+
y2: y,
|
|
9736
|
+
stroke: GRIDLINE_COLOR,
|
|
9737
|
+
strokeWidth: 0.5,
|
|
9738
|
+
strokeDasharray: "2,3"
|
|
9739
|
+
},
|
|
9740
|
+
i
|
|
9741
|
+
);
|
|
9742
|
+
}),
|
|
9743
|
+
area_d && /* @__PURE__ */ jsxRuntime.jsx("path", { d: area_d, fill: color2, fillOpacity: 0.12, stroke: "none" }),
|
|
9744
|
+
line_d && /* @__PURE__ */ jsxRuntime.jsx("path", { d: line_d, stroke: color2, strokeWidth: 1.8, fill: "none" }),
|
|
9745
|
+
/* @__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) }),
|
|
9746
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9747
|
+
"text",
|
|
9748
|
+
{
|
|
9749
|
+
x: PAD_LEFT - 4,
|
|
9750
|
+
y: PAD_TOP + (geo.vbox_h - PAD_TOP - PAD_BOTTOM) / 2 + 3,
|
|
9751
|
+
textAnchor: "end",
|
|
9752
|
+
fill: AXIS_LABEL_COLOR,
|
|
9753
|
+
fontSize: 9,
|
|
9754
|
+
children: format_num((geo.y_max + geo.y_min) / 2)
|
|
9755
|
+
}
|
|
9756
|
+
),
|
|
9757
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9758
|
+
"text",
|
|
9759
|
+
{
|
|
9760
|
+
x: PAD_LEFT - 4,
|
|
9761
|
+
y: geo.vbox_h - PAD_BOTTOM + 3,
|
|
9762
|
+
textAnchor: "end",
|
|
9763
|
+
fill: AXIS_LABEL_COLOR,
|
|
9764
|
+
fontSize: 9,
|
|
9765
|
+
children: format_num(geo.y_min)
|
|
9766
|
+
}
|
|
9767
|
+
),
|
|
9768
|
+
x_label_idx.map((idx, k) => {
|
|
9769
|
+
const anchor = k === 0 ? "start" : k === 1 ? "middle" : "end";
|
|
9770
|
+
const x_pos = geo.cx(idx);
|
|
9771
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9772
|
+
"text",
|
|
9773
|
+
{
|
|
9774
|
+
x: x_pos,
|
|
9775
|
+
y: geo.vbox_h - 6,
|
|
9776
|
+
textAnchor: anchor,
|
|
9777
|
+
fill: AXIS_LABEL_COLOR,
|
|
9778
|
+
fontSize: 9,
|
|
9779
|
+
children: dates[idx] ?? ""
|
|
9780
|
+
},
|
|
9781
|
+
`x_${k}`
|
|
9782
|
+
);
|
|
9783
|
+
}),
|
|
9784
|
+
has_last && hover_idx === null && (() => {
|
|
9785
|
+
const x = geo.cx(last_idx);
|
|
9786
|
+
const y = geo.cy(last_v);
|
|
9787
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
9788
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9789
|
+
"line",
|
|
9790
|
+
{
|
|
9791
|
+
x1: x,
|
|
9792
|
+
x2: PAD_LEFT,
|
|
9793
|
+
y1: y,
|
|
9794
|
+
y2: y,
|
|
9795
|
+
stroke: color2,
|
|
9796
|
+
strokeWidth: 0.5,
|
|
9797
|
+
strokeDasharray: "2,2",
|
|
9798
|
+
opacity: 0.4
|
|
9799
|
+
}
|
|
9800
|
+
),
|
|
9801
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: x, cy: y, r: 3.5, fill: color2, stroke: color2, strokeWidth: 2, fillOpacity: 0.3 }),
|
|
9802
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: x, cy: y, r: 2, fill: color2 }),
|
|
9803
|
+
/* @__PURE__ */ jsxRuntime.jsxs("text", { x: x - 6, y: y - 6, textAnchor: "end", fill: color2, fontSize: 10, fontWeight: 700, children: [
|
|
9804
|
+
format_num(last_v),
|
|
9805
|
+
unit
|
|
9806
|
+
] })
|
|
9807
|
+
] });
|
|
9808
|
+
})(),
|
|
9809
|
+
showTooltip && hover_idx !== null && hover_has_v && (() => {
|
|
9810
|
+
const x = geo.cx(hover_idx);
|
|
9811
|
+
const y = geo.cy(hover_v);
|
|
9812
|
+
const label = `${format_num(hover_v)}${unit}`;
|
|
9813
|
+
const date = dates[hover_idx] ?? "";
|
|
9814
|
+
const bubble_text_w = Math.max(label.length, date.length) * 5.5 + 12;
|
|
9815
|
+
const bubble_w = Math.max(bubble_text_w, 40);
|
|
9816
|
+
const bubble_h = 26;
|
|
9817
|
+
const flip = x + bubble_w + 6 > geo.vbox_w - PAD_RIGHT;
|
|
9818
|
+
const bubble_x = flip ? x - bubble_w - 6 : x + 6;
|
|
9819
|
+
const bubble_y = Math.max(PAD_TOP, Math.min(y - bubble_h / 2, geo.vbox_h - PAD_BOTTOM - bubble_h));
|
|
9820
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
9821
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9822
|
+
"line",
|
|
9823
|
+
{
|
|
9824
|
+
x1: x,
|
|
9825
|
+
x2: x,
|
|
9826
|
+
y1: PAD_TOP,
|
|
9827
|
+
y2: geo.vbox_h - PAD_BOTTOM,
|
|
9828
|
+
stroke: color2,
|
|
9829
|
+
strokeWidth: 0.5,
|
|
9830
|
+
strokeDasharray: "2,2",
|
|
9831
|
+
opacity: 0.6
|
|
9832
|
+
}
|
|
9833
|
+
),
|
|
9834
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: x, cy: y, r: 3, fill: color2 }),
|
|
9835
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9836
|
+
"rect",
|
|
9837
|
+
{
|
|
9838
|
+
x: bubble_x,
|
|
9839
|
+
y: bubble_y,
|
|
9840
|
+
width: bubble_w,
|
|
9841
|
+
height: bubble_h,
|
|
9842
|
+
rx: 3,
|
|
9843
|
+
fill: "#0d1117",
|
|
9844
|
+
stroke: color2,
|
|
9845
|
+
strokeWidth: 0.5,
|
|
9846
|
+
fillOpacity: 0.92
|
|
9847
|
+
}
|
|
9848
|
+
),
|
|
9849
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9850
|
+
"text",
|
|
9851
|
+
{
|
|
9852
|
+
x: bubble_x + bubble_w / 2,
|
|
9853
|
+
y: bubble_y + 11,
|
|
9854
|
+
textAnchor: "middle",
|
|
9855
|
+
fill: color2,
|
|
9856
|
+
fontSize: 10,
|
|
9857
|
+
fontWeight: 700,
|
|
9858
|
+
children: label
|
|
9859
|
+
}
|
|
9860
|
+
),
|
|
9861
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9862
|
+
"text",
|
|
9863
|
+
{
|
|
9864
|
+
x: bubble_x + bubble_w / 2,
|
|
9865
|
+
y: bubble_y + 22,
|
|
9866
|
+
textAnchor: "middle",
|
|
9867
|
+
fill: AXIS_LABEL_COLOR,
|
|
9868
|
+
fontSize: 8,
|
|
9869
|
+
children: date
|
|
9870
|
+
}
|
|
9871
|
+
)
|
|
9872
|
+
] });
|
|
9873
|
+
})()
|
|
9874
|
+
]
|
|
9875
|
+
}
|
|
9876
|
+
);
|
|
9877
|
+
}
|
|
9878
|
+
var PAD_LEFT2 = 38;
|
|
9879
|
+
var PAD_RIGHT2 = 14;
|
|
9880
|
+
var PAD_TOP2 = 12;
|
|
9881
|
+
var PAD_BOTTOM2 = 22;
|
|
9882
|
+
var AXIS_LABEL_COLOR2 = "#8b949e";
|
|
9883
|
+
var GRIDLINE_COLOR2 = "#2a3441";
|
|
9884
|
+
function compute_geometry2(series, width, height) {
|
|
9885
|
+
const all_vals = [];
|
|
9886
|
+
let n = 0;
|
|
9887
|
+
for (const s of series) {
|
|
9888
|
+
n = Math.max(n, s.data.length);
|
|
9889
|
+
for (const v of s.data) if (v !== null && !Number.isNaN(v)) all_vals.push(v);
|
|
9890
|
+
}
|
|
9891
|
+
if (all_vals.length === 0 || n === 0) return null;
|
|
9892
|
+
const mn = Math.min(...all_vals);
|
|
9893
|
+
const mx = Math.max(...all_vals);
|
|
9894
|
+
const rng = mx - mn || mx * 0.1 + 1;
|
|
9895
|
+
const y_min = Math.max(0, mn - rng * 0.05);
|
|
9896
|
+
const y_max = mx + rng * 0.05;
|
|
9897
|
+
const y_rng = y_max - y_min || 1;
|
|
9898
|
+
const plot_w = width - PAD_LEFT2 - PAD_RIGHT2;
|
|
9899
|
+
const plot_h = height - PAD_TOP2 - PAD_BOTTOM2;
|
|
9900
|
+
return {
|
|
9901
|
+
vbox_w: width,
|
|
9902
|
+
vbox_h: height,
|
|
9903
|
+
y_min,
|
|
9904
|
+
y_max,
|
|
9905
|
+
point_count: n,
|
|
9906
|
+
cx: (i) => n <= 1 ? PAD_LEFT2 + plot_w / 2 : PAD_LEFT2 + i * plot_w / (n - 1),
|
|
9907
|
+
cy: (v) => PAD_TOP2 + (1 - (v - y_min) / y_rng) * plot_h
|
|
9908
|
+
};
|
|
9909
|
+
}
|
|
9910
|
+
function MultiLineChart({
|
|
9911
|
+
series,
|
|
9912
|
+
dates,
|
|
9913
|
+
width = 360,
|
|
9914
|
+
height = 140,
|
|
9915
|
+
showTooltip = true,
|
|
9916
|
+
showLegend = true,
|
|
9917
|
+
className
|
|
9918
|
+
}) {
|
|
9919
|
+
const geo = compute_geometry2(series, width, height);
|
|
9920
|
+
const [hover_idx, set_hover_idx] = React25__namespace.useState(null);
|
|
9921
|
+
const handle_mouse_move = React25__namespace.useCallback(
|
|
9922
|
+
(e) => {
|
|
9923
|
+
if (!geo) return;
|
|
9924
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
9925
|
+
if (rect.width === 0) return;
|
|
9926
|
+
const vbox_x = (e.clientX - rect.left) / rect.width * geo.vbox_w;
|
|
9927
|
+
const plot_w = geo.vbox_w - PAD_LEFT2 - PAD_RIGHT2;
|
|
9928
|
+
if (vbox_x < PAD_LEFT2 || vbox_x > geo.vbox_w - PAD_RIGHT2) {
|
|
9929
|
+
set_hover_idx(null);
|
|
9930
|
+
return;
|
|
9931
|
+
}
|
|
9932
|
+
const ratio = (vbox_x - PAD_LEFT2) / plot_w;
|
|
9933
|
+
const idx = Math.round(ratio * (geo.point_count - 1));
|
|
9934
|
+
set_hover_idx(Math.max(0, Math.min(geo.point_count - 1, idx)));
|
|
9935
|
+
},
|
|
9936
|
+
[geo]
|
|
9937
|
+
);
|
|
9938
|
+
const handle_mouse_leave = React25__namespace.useCallback(() => set_hover_idx(null), []);
|
|
9939
|
+
if (!geo) {
|
|
9940
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9941
|
+
"svg",
|
|
9942
|
+
{
|
|
9943
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
9944
|
+
className: cn("cls_hazo_chart cls_hazo_chart_empty", className),
|
|
9945
|
+
style: { width: "100%", height: "auto", display: "block", maxHeight: `${height + 10}px` }
|
|
9946
|
+
}
|
|
9947
|
+
);
|
|
9948
|
+
}
|
|
9949
|
+
const x_label_idx = pick_x_label_indices(geo.point_count);
|
|
9950
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("cls_hazo_chart_wrapper", className), children: [
|
|
9951
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
9952
|
+
"svg",
|
|
9953
|
+
{
|
|
9954
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
9955
|
+
onMouseMove: showTooltip ? handle_mouse_move : void 0,
|
|
9956
|
+
onMouseLeave: showTooltip ? handle_mouse_leave : void 0,
|
|
9957
|
+
className: "cls_hazo_chart cls_hazo_chart_multi",
|
|
9958
|
+
style: {
|
|
9959
|
+
width: "100%",
|
|
9960
|
+
height: "auto",
|
|
9961
|
+
display: "block",
|
|
9962
|
+
maxHeight: `${height + 10}px`,
|
|
9963
|
+
cursor: showTooltip ? "crosshair" : "default"
|
|
9964
|
+
},
|
|
9965
|
+
children: [
|
|
9966
|
+
[0, 1, 2].map((i) => {
|
|
9967
|
+
const y = PAD_TOP2 + i / 2 * (geo.vbox_h - PAD_TOP2 - PAD_BOTTOM2);
|
|
9968
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9969
|
+
"line",
|
|
9970
|
+
{
|
|
9971
|
+
x1: PAD_LEFT2,
|
|
9972
|
+
x2: geo.vbox_w - PAD_RIGHT2,
|
|
9973
|
+
y1: y,
|
|
9974
|
+
y2: y,
|
|
9975
|
+
stroke: GRIDLINE_COLOR2,
|
|
9976
|
+
strokeWidth: 0.5,
|
|
9977
|
+
strokeDasharray: "2,3"
|
|
9978
|
+
},
|
|
9979
|
+
i
|
|
9980
|
+
);
|
|
9981
|
+
}),
|
|
9982
|
+
/* @__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) }),
|
|
9983
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9984
|
+
"text",
|
|
9985
|
+
{
|
|
9986
|
+
x: PAD_LEFT2 - 4,
|
|
9987
|
+
y: PAD_TOP2 + (geo.vbox_h - PAD_TOP2 - PAD_BOTTOM2) / 2 + 3,
|
|
9988
|
+
textAnchor: "end",
|
|
9989
|
+
fill: AXIS_LABEL_COLOR2,
|
|
9990
|
+
fontSize: 9,
|
|
9991
|
+
children: format_num((geo.y_max + geo.y_min) / 2)
|
|
9992
|
+
}
|
|
9993
|
+
),
|
|
9994
|
+
/* @__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) }),
|
|
9995
|
+
x_label_idx.map((idx, k) => {
|
|
9996
|
+
const anchor = k === 0 ? "start" : k === 1 ? "middle" : "end";
|
|
9997
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9998
|
+
"text",
|
|
9999
|
+
{
|
|
10000
|
+
x: geo.cx(idx),
|
|
10001
|
+
y: geo.vbox_h - 6,
|
|
10002
|
+
textAnchor: anchor,
|
|
10003
|
+
fill: AXIS_LABEL_COLOR2,
|
|
10004
|
+
fontSize: 9,
|
|
10005
|
+
children: dates[idx] ?? ""
|
|
10006
|
+
},
|
|
10007
|
+
`x_${k}`
|
|
10008
|
+
);
|
|
10009
|
+
}),
|
|
10010
|
+
series.map((s, s_idx) => {
|
|
10011
|
+
let d = "";
|
|
10012
|
+
s.data.forEach((v, i) => {
|
|
10013
|
+
if (v === null || Number.isNaN(v)) return;
|
|
10014
|
+
d += `${d ? " L" : "M"}${geo.cx(i).toFixed(1)},${geo.cy(v).toFixed(1)}`;
|
|
10015
|
+
});
|
|
10016
|
+
const last_idx = s.data.length - 1;
|
|
10017
|
+
const last_v = s.data[last_idx];
|
|
10018
|
+
const has_last = last_v !== null && last_v !== void 0 && !Number.isNaN(last_v);
|
|
10019
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
|
|
10020
|
+
d && /* @__PURE__ */ jsxRuntime.jsx("path", { d, stroke: s.color, strokeWidth: 1.7, fill: "none" }),
|
|
10021
|
+
has_last && hover_idx === null && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
10022
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10023
|
+
"circle",
|
|
10024
|
+
{
|
|
10025
|
+
cx: geo.cx(last_idx),
|
|
10026
|
+
cy: geo.cy(last_v),
|
|
10027
|
+
r: 3,
|
|
10028
|
+
fill: s.color,
|
|
10029
|
+
fillOpacity: 0.3,
|
|
10030
|
+
stroke: s.color,
|
|
10031
|
+
strokeWidth: 1.5
|
|
10032
|
+
}
|
|
10033
|
+
),
|
|
10034
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10035
|
+
"text",
|
|
10036
|
+
{
|
|
10037
|
+
x: geo.cx(last_idx) - 5,
|
|
10038
|
+
y: geo.cy(last_v) - 5,
|
|
10039
|
+
textAnchor: "end",
|
|
10040
|
+
fill: s.color,
|
|
10041
|
+
fontSize: 9,
|
|
10042
|
+
fontWeight: 700,
|
|
10043
|
+
children: format_num(last_v)
|
|
10044
|
+
}
|
|
10045
|
+
)
|
|
10046
|
+
] })
|
|
10047
|
+
] }, `series_${s_idx}`);
|
|
10048
|
+
}),
|
|
10049
|
+
showTooltip && hover_idx !== null && (() => {
|
|
10050
|
+
const x = geo.cx(hover_idx);
|
|
10051
|
+
const items = series.map((s) => ({
|
|
10052
|
+
label: s.label,
|
|
10053
|
+
color: s.color,
|
|
10054
|
+
value: s.data[hover_idx] ?? null
|
|
10055
|
+
})).filter((it) => it.value !== null && !Number.isNaN(it.value));
|
|
10056
|
+
if (items.length === 0) return null;
|
|
10057
|
+
const bubble_w = 70;
|
|
10058
|
+
const row_h = 12;
|
|
10059
|
+
const bubble_h = items.length * row_h + 18;
|
|
10060
|
+
const flip = x + bubble_w + 6 > geo.vbox_w - PAD_RIGHT2;
|
|
10061
|
+
const bubble_x = flip ? x - bubble_w - 6 : x + 6;
|
|
10062
|
+
const bubble_y = Math.max(PAD_TOP2, Math.min(PAD_TOP2 + 5, geo.vbox_h - PAD_BOTTOM2 - bubble_h));
|
|
10063
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
10064
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10065
|
+
"line",
|
|
10066
|
+
{
|
|
10067
|
+
x1: x,
|
|
10068
|
+
x2: x,
|
|
10069
|
+
y1: PAD_TOP2,
|
|
10070
|
+
y2: geo.vbox_h - PAD_BOTTOM2,
|
|
10071
|
+
stroke: AXIS_LABEL_COLOR2,
|
|
10072
|
+
strokeWidth: 0.5,
|
|
10073
|
+
strokeDasharray: "2,2",
|
|
10074
|
+
opacity: 0.6
|
|
10075
|
+
}
|
|
10076
|
+
),
|
|
10077
|
+
items.map((it) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
10078
|
+
"circle",
|
|
10079
|
+
{
|
|
10080
|
+
cx: x,
|
|
10081
|
+
cy: geo.cy(it.value),
|
|
10082
|
+
r: 3,
|
|
10083
|
+
fill: it.color
|
|
10084
|
+
},
|
|
10085
|
+
`hd_${it.label}`
|
|
10086
|
+
)),
|
|
10087
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10088
|
+
"rect",
|
|
10089
|
+
{
|
|
10090
|
+
x: bubble_x,
|
|
10091
|
+
y: bubble_y,
|
|
10092
|
+
width: bubble_w,
|
|
10093
|
+
height: bubble_h,
|
|
10094
|
+
rx: 3,
|
|
10095
|
+
fill: "#0d1117",
|
|
10096
|
+
stroke: AXIS_LABEL_COLOR2,
|
|
10097
|
+
strokeWidth: 0.5,
|
|
10098
|
+
fillOpacity: 0.92
|
|
10099
|
+
}
|
|
10100
|
+
),
|
|
10101
|
+
/* @__PURE__ */ jsxRuntime.jsx("text", { x: bubble_x + 6, y: bubble_y + 10, fill: AXIS_LABEL_COLOR2, fontSize: 8, children: dates[hover_idx] ?? "" }),
|
|
10102
|
+
items.map((it, i) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
|
|
10103
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10104
|
+
"rect",
|
|
10105
|
+
{
|
|
10106
|
+
x: bubble_x + 6,
|
|
10107
|
+
y: bubble_y + 14 + i * row_h,
|
|
10108
|
+
width: 6,
|
|
10109
|
+
height: 6,
|
|
10110
|
+
fill: it.color
|
|
10111
|
+
}
|
|
10112
|
+
),
|
|
10113
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10114
|
+
"text",
|
|
10115
|
+
{
|
|
10116
|
+
x: bubble_x + 16,
|
|
10117
|
+
y: bubble_y + 20 + i * row_h,
|
|
10118
|
+
fill: it.color,
|
|
10119
|
+
fontSize: 9,
|
|
10120
|
+
fontWeight: 600,
|
|
10121
|
+
children: format_num(it.value)
|
|
10122
|
+
}
|
|
10123
|
+
)
|
|
10124
|
+
] }, `row_${i}`))
|
|
10125
|
+
] });
|
|
10126
|
+
})()
|
|
10127
|
+
]
|
|
10128
|
+
}
|
|
10129
|
+
),
|
|
10130
|
+
showLegend && /* @__PURE__ */ jsxRuntime.jsx(
|
|
10131
|
+
"div",
|
|
10132
|
+
{
|
|
10133
|
+
className: "cls_hazo_chart_legend",
|
|
10134
|
+
style: {
|
|
10135
|
+
display: "flex",
|
|
10136
|
+
gap: "12px",
|
|
10137
|
+
justifyContent: "center",
|
|
10138
|
+
marginTop: "4px",
|
|
10139
|
+
fontSize: "10px",
|
|
10140
|
+
color: AXIS_LABEL_COLOR2,
|
|
10141
|
+
flexWrap: "wrap"
|
|
10142
|
+
},
|
|
10143
|
+
children: series.map((s) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10144
|
+
"span",
|
|
10145
|
+
{
|
|
10146
|
+
style: { display: "inline-flex", alignItems: "center", gap: "4px" },
|
|
10147
|
+
children: [
|
|
10148
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10149
|
+
"i",
|
|
10150
|
+
{
|
|
10151
|
+
style: {
|
|
10152
|
+
display: "inline-block",
|
|
10153
|
+
width: "8px",
|
|
10154
|
+
height: "8px",
|
|
10155
|
+
background: s.color,
|
|
10156
|
+
borderRadius: "1px"
|
|
10157
|
+
}
|
|
10158
|
+
}
|
|
10159
|
+
),
|
|
10160
|
+
s.label
|
|
10161
|
+
]
|
|
10162
|
+
},
|
|
10163
|
+
s.label
|
|
10164
|
+
))
|
|
10165
|
+
}
|
|
10166
|
+
)
|
|
10167
|
+
] });
|
|
10168
|
+
}
|
|
10169
|
+
var PAD_LEFT3 = 32;
|
|
10170
|
+
var PAD_RIGHT3 = 8;
|
|
10171
|
+
var PAD_TOP3 = 10;
|
|
10172
|
+
var PAD_BOTTOM3 = 22;
|
|
10173
|
+
var BAR_GAP_RATIO = 0.25;
|
|
10174
|
+
var AXIS_LABEL_COLOR3 = "#8b949e";
|
|
10175
|
+
function StackedBars({
|
|
10176
|
+
bars,
|
|
10177
|
+
width = 360,
|
|
10178
|
+
height = 140,
|
|
10179
|
+
showYAxis = true,
|
|
10180
|
+
className
|
|
10181
|
+
}) {
|
|
10182
|
+
const totals = bars.map((b) => b.segments.reduce((sum, s) => sum + s.value, 0));
|
|
10183
|
+
const y_max = Math.max(0, ...totals) || 1;
|
|
10184
|
+
const plot_w = width - PAD_LEFT3 - PAD_RIGHT3;
|
|
10185
|
+
const plot_h = height - PAD_TOP3 - PAD_BOTTOM3;
|
|
10186
|
+
const slot_w = bars.length > 0 ? plot_w / bars.length : 0;
|
|
10187
|
+
const bar_w = slot_w * (1 - BAR_GAP_RATIO);
|
|
10188
|
+
const x_for = (i) => PAD_LEFT3 + i * slot_w + (slot_w - bar_w) / 2;
|
|
10189
|
+
const x_label_idx = pick_x_label_indices(bars.length);
|
|
10190
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10191
|
+
"svg",
|
|
10192
|
+
{
|
|
10193
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
10194
|
+
className: cn("cls_hazo_chart cls_hazo_chart_stacked_bars", className),
|
|
10195
|
+
style: { width: "100%", height: "auto", display: "block", maxHeight: `${height + 10}px` },
|
|
10196
|
+
children: [
|
|
10197
|
+
showYAxis && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
10198
|
+
/* @__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) }),
|
|
10199
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10200
|
+
"text",
|
|
10201
|
+
{
|
|
10202
|
+
x: PAD_LEFT3 - 4,
|
|
10203
|
+
y: PAD_TOP3 + plot_h / 2 + 3,
|
|
10204
|
+
textAnchor: "end",
|
|
10205
|
+
fill: AXIS_LABEL_COLOR3,
|
|
10206
|
+
fontSize: 9,
|
|
10207
|
+
children: format_num(y_max / 2)
|
|
10208
|
+
}
|
|
10209
|
+
),
|
|
10210
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10211
|
+
"text",
|
|
10212
|
+
{
|
|
10213
|
+
x: PAD_LEFT3 - 4,
|
|
10214
|
+
y: height - PAD_BOTTOM3 + 3,
|
|
10215
|
+
textAnchor: "end",
|
|
10216
|
+
fill: AXIS_LABEL_COLOR3,
|
|
10217
|
+
fontSize: 9,
|
|
10218
|
+
children: "0"
|
|
10219
|
+
}
|
|
10220
|
+
)
|
|
10221
|
+
] }),
|
|
10222
|
+
bars.map((bar, i) => {
|
|
10223
|
+
let cursor_y = height - PAD_BOTTOM3;
|
|
10224
|
+
return /* @__PURE__ */ jsxRuntime.jsx("g", { children: bar.segments.map((seg, s_idx) => {
|
|
10225
|
+
if (seg.value <= 0) return null;
|
|
10226
|
+
const seg_h = seg.value / y_max * plot_h;
|
|
10227
|
+
const y = cursor_y - seg_h;
|
|
10228
|
+
cursor_y = y;
|
|
10229
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10230
|
+
"rect",
|
|
10231
|
+
{
|
|
10232
|
+
x: x_for(i),
|
|
10233
|
+
y,
|
|
10234
|
+
width: bar_w,
|
|
10235
|
+
height: seg_h,
|
|
10236
|
+
fill: seg.color
|
|
10237
|
+
},
|
|
10238
|
+
`seg_${s_idx}`
|
|
10239
|
+
);
|
|
10240
|
+
}) }, `bar_${i}`);
|
|
10241
|
+
}),
|
|
10242
|
+
x_label_idx.map((idx, k) => {
|
|
10243
|
+
const anchor = k === 0 ? "start" : k === 1 ? "middle" : "end";
|
|
10244
|
+
const x_pos = x_for(idx) + bar_w / 2;
|
|
10245
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10246
|
+
"text",
|
|
10247
|
+
{
|
|
10248
|
+
x: x_pos,
|
|
10249
|
+
y: height - 6,
|
|
10250
|
+
textAnchor: anchor,
|
|
10251
|
+
fill: AXIS_LABEL_COLOR3,
|
|
10252
|
+
fontSize: 9,
|
|
10253
|
+
children: bars[idx]?.label ?? ""
|
|
10254
|
+
},
|
|
10255
|
+
`x_${k}`
|
|
10256
|
+
);
|
|
10257
|
+
})
|
|
10258
|
+
]
|
|
10259
|
+
}
|
|
10260
|
+
);
|
|
10261
|
+
}
|
|
10262
|
+
function DateRangeSelector({
|
|
10263
|
+
value,
|
|
10264
|
+
onChange,
|
|
10265
|
+
options,
|
|
10266
|
+
className,
|
|
10267
|
+
ariaLabel = "Date range"
|
|
10268
|
+
}) {
|
|
10269
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10270
|
+
"div",
|
|
10271
|
+
{
|
|
10272
|
+
role: "group",
|
|
10273
|
+
"aria-label": ariaLabel,
|
|
10274
|
+
className: cn(
|
|
10275
|
+
"cls_hazo_date_range_selector inline-flex items-center gap-0 rounded-md border bg-background p-0.5",
|
|
10276
|
+
className
|
|
10277
|
+
),
|
|
10278
|
+
children: options.map((opt) => {
|
|
10279
|
+
const is_active = opt.value === value;
|
|
10280
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10281
|
+
"button",
|
|
10282
|
+
{
|
|
10283
|
+
type: "button",
|
|
10284
|
+
"aria-pressed": is_active,
|
|
10285
|
+
onClick: () => {
|
|
10286
|
+
if (!is_active) onChange(opt.value);
|
|
10287
|
+
},
|
|
10288
|
+
className: cn(
|
|
10289
|
+
"cls_hazo_date_range_option px-2.5 py-1 text-xs font-medium rounded-sm transition-colors",
|
|
10290
|
+
is_active ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground hover:bg-muted"
|
|
10291
|
+
),
|
|
10292
|
+
children: opt.label
|
|
10293
|
+
},
|
|
10294
|
+
opt.value
|
|
10295
|
+
);
|
|
10296
|
+
})
|
|
10297
|
+
}
|
|
10298
|
+
);
|
|
10299
|
+
}
|
|
10300
|
+
|
|
10301
|
+
// src/assets/celebration-chime.mp3
|
|
10302
|
+
var celebration_chime_default = "data:text/plain;charset=utf-8,";
|
|
10303
|
+
var CELEBRATION_GRADIENT = "linear-gradient(135deg, #667eea 0%, #764ba2 100%)";
|
|
10304
|
+
var CARD_SIZE = 1080;
|
|
10305
|
+
var PREVIEW_SIZE = 400;
|
|
10306
|
+
var CARD_SCALE = PREVIEW_SIZE / CARD_SIZE;
|
|
10307
|
+
var _enqueue = null;
|
|
10308
|
+
function celebrate(payload) {
|
|
10309
|
+
if (!_enqueue) {
|
|
10310
|
+
if (process.env.NODE_ENV !== "production") {
|
|
10311
|
+
console.warn(
|
|
10312
|
+
"[hazo_ui] celebrate() called before <CelebrationProvider /> was mounted"
|
|
10313
|
+
);
|
|
10314
|
+
}
|
|
10315
|
+
return;
|
|
10316
|
+
}
|
|
10317
|
+
_enqueue(payload);
|
|
10318
|
+
}
|
|
10319
|
+
function CelebrationProvider({ children }) {
|
|
10320
|
+
const [queue, set_queue] = React25__namespace.useState([]);
|
|
10321
|
+
const enqueue = React25__namespace.useCallback((payload) => {
|
|
10322
|
+
const storage_key = `hazo_ui_celebration_${payload.id}`;
|
|
10323
|
+
if (typeof window !== "undefined" && sessionStorage.getItem(storage_key)) {
|
|
10324
|
+
return;
|
|
10325
|
+
}
|
|
10326
|
+
set_queue((q) => [...q, payload]);
|
|
10327
|
+
}, []);
|
|
10328
|
+
React25__namespace.useEffect(() => {
|
|
10329
|
+
_enqueue = enqueue;
|
|
10330
|
+
return () => {
|
|
10331
|
+
_enqueue = null;
|
|
10332
|
+
};
|
|
10333
|
+
}, [enqueue]);
|
|
10334
|
+
const handle_close = React25__namespace.useCallback(() => {
|
|
10335
|
+
set_queue((q) => q.slice(1));
|
|
10336
|
+
}, []);
|
|
10337
|
+
const current = queue[0] ?? null;
|
|
10338
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
10339
|
+
children,
|
|
10340
|
+
current && /* @__PURE__ */ jsxRuntime.jsx(
|
|
10341
|
+
CelebrationModalInner,
|
|
10342
|
+
{
|
|
10343
|
+
payload: current,
|
|
10344
|
+
onClose: handle_close
|
|
10345
|
+
},
|
|
10346
|
+
current.id
|
|
10347
|
+
)
|
|
10348
|
+
] });
|
|
10349
|
+
}
|
|
10350
|
+
function CelebrationModalInner({
|
|
10351
|
+
payload,
|
|
10352
|
+
onClose
|
|
10353
|
+
}) {
|
|
10354
|
+
const {
|
|
10355
|
+
id,
|
|
10356
|
+
title,
|
|
10357
|
+
subtitle,
|
|
10358
|
+
shareableCard,
|
|
10359
|
+
autoDismiss = true,
|
|
10360
|
+
autoDismissDelay = 8e3,
|
|
10361
|
+
audioChime = false
|
|
10362
|
+
} = payload;
|
|
10363
|
+
const [visible, set_visible] = React25__namespace.useState(true);
|
|
10364
|
+
const [is_downloading, set_is_downloading] = React25__namespace.useState(false);
|
|
10365
|
+
const canvas_ref = React25__namespace.useRef(null);
|
|
10366
|
+
const card_ref = React25__namespace.useRef(null);
|
|
10367
|
+
React25__namespace.useEffect(() => {
|
|
10368
|
+
sessionStorage.setItem(`hazo_ui_celebration_${id}`, "1");
|
|
10369
|
+
}, [id]);
|
|
10370
|
+
React25__namespace.useEffect(() => {
|
|
10371
|
+
if (!canvas_ref.current) return;
|
|
10372
|
+
let confetti_instance = null;
|
|
10373
|
+
import('canvas-confetti').then(({ default: confetti }) => {
|
|
10374
|
+
if (!canvas_ref.current) return;
|
|
10375
|
+
confetti_instance = confetti.create(canvas_ref.current, { resize: true });
|
|
10376
|
+
confetti_instance({
|
|
10377
|
+
particleCount: 120,
|
|
10378
|
+
spread: 70,
|
|
10379
|
+
origin: { y: 0.5 },
|
|
10380
|
+
colors: ["#667eea", "#764ba2", "#f6d365", "#fda085", "#84fab0"]
|
|
10381
|
+
});
|
|
10382
|
+
});
|
|
10383
|
+
return () => {
|
|
10384
|
+
confetti_instance?.reset();
|
|
10385
|
+
};
|
|
10386
|
+
}, []);
|
|
10387
|
+
React25__namespace.useEffect(() => {
|
|
10388
|
+
if (!audioChime) return;
|
|
10389
|
+
const audio = new Audio(celebration_chime_default);
|
|
10390
|
+
audio.play().catch(() => {
|
|
10391
|
+
});
|
|
10392
|
+
}, [audioChime]);
|
|
10393
|
+
React25__namespace.useEffect(() => {
|
|
10394
|
+
if (!autoDismiss) return;
|
|
10395
|
+
const timer = window.setTimeout(close_modal, autoDismissDelay);
|
|
10396
|
+
return () => window.clearTimeout(timer);
|
|
10397
|
+
}, [autoDismiss, autoDismissDelay]);
|
|
10398
|
+
React25__namespace.useEffect(() => {
|
|
10399
|
+
const on_key = (e) => {
|
|
10400
|
+
if (e.key === "Escape") close_modal();
|
|
10401
|
+
};
|
|
10402
|
+
document.addEventListener("keydown", on_key);
|
|
10403
|
+
return () => document.removeEventListener("keydown", on_key);
|
|
10404
|
+
}, []);
|
|
10405
|
+
function close_modal() {
|
|
10406
|
+
set_visible(false);
|
|
10407
|
+
setTimeout(onClose, 150);
|
|
10408
|
+
}
|
|
10409
|
+
async function handle_download() {
|
|
10410
|
+
if (!card_ref.current) return;
|
|
10411
|
+
set_is_downloading(true);
|
|
10412
|
+
try {
|
|
10413
|
+
const { toPng } = await import('html-to-image');
|
|
10414
|
+
const data_url = await toPng(card_ref.current, {
|
|
10415
|
+
width: CARD_SIZE,
|
|
10416
|
+
height: CARD_SIZE
|
|
10417
|
+
});
|
|
10418
|
+
const a = document.createElement("a");
|
|
10419
|
+
a.download = `${id}.png`;
|
|
10420
|
+
a.href = data_url;
|
|
10421
|
+
a.click();
|
|
10422
|
+
} catch (err) {
|
|
10423
|
+
console.error("[hazo_ui] CelebrationModal: download failed", err);
|
|
10424
|
+
} finally {
|
|
10425
|
+
set_is_downloading(false);
|
|
10426
|
+
}
|
|
10427
|
+
}
|
|
10428
|
+
async function handle_share() {
|
|
10429
|
+
if (!card_ref.current || !navigator.share) return;
|
|
10430
|
+
try {
|
|
10431
|
+
const { toPng } = await import('html-to-image');
|
|
10432
|
+
const data_url = await toPng(card_ref.current, {
|
|
10433
|
+
width: CARD_SIZE,
|
|
10434
|
+
height: CARD_SIZE
|
|
10435
|
+
});
|
|
10436
|
+
const blob = await fetch(data_url).then((r) => r.blob());
|
|
10437
|
+
await navigator.share({
|
|
10438
|
+
files: [new File([blob], `${id}.png`, { type: "image/png" })]
|
|
10439
|
+
});
|
|
10440
|
+
} catch (err) {
|
|
10441
|
+
console.error("[hazo_ui] CelebrationModal: share failed", err);
|
|
10442
|
+
}
|
|
10443
|
+
}
|
|
10444
|
+
async function handle_copy() {
|
|
10445
|
+
if (!card_ref.current) return;
|
|
10446
|
+
try {
|
|
10447
|
+
const { toPng } = await import('html-to-image');
|
|
10448
|
+
const data_url = await toPng(card_ref.current, {
|
|
10449
|
+
width: CARD_SIZE,
|
|
10450
|
+
height: CARD_SIZE
|
|
10451
|
+
});
|
|
10452
|
+
const blob = await fetch(data_url).then((r) => r.blob());
|
|
10453
|
+
await navigator.clipboard.write([
|
|
10454
|
+
new ClipboardItem({ "image/png": blob })
|
|
10455
|
+
]);
|
|
10456
|
+
} catch (err) {
|
|
10457
|
+
console.error("[hazo_ui] CelebrationModal: copy failed", err);
|
|
10458
|
+
}
|
|
10459
|
+
}
|
|
10460
|
+
const resolved_caption = shareableCard?.caption ?? subtitle;
|
|
10461
|
+
const card_bg = shareableCard?.background ?? CELEBRATION_GRADIENT;
|
|
10462
|
+
const can_share = typeof navigator !== "undefined" && "share" in navigator;
|
|
10463
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10464
|
+
"div",
|
|
10465
|
+
{
|
|
10466
|
+
className: cn(
|
|
10467
|
+
"cls_celebration_modal_overlay",
|
|
10468
|
+
"fixed inset-0 z-50 flex items-center justify-center",
|
|
10469
|
+
"bg-black/30 backdrop-blur-sm",
|
|
10470
|
+
"transition-opacity duration-150",
|
|
10471
|
+
visible ? "opacity-100" : "opacity-0 pointer-events-none"
|
|
10472
|
+
),
|
|
10473
|
+
onClick: close_modal,
|
|
10474
|
+
children: [
|
|
10475
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10476
|
+
"canvas",
|
|
10477
|
+
{
|
|
10478
|
+
ref: canvas_ref,
|
|
10479
|
+
className: "cls_celebration_confetti_canvas absolute inset-0 w-full h-full pointer-events-none"
|
|
10480
|
+
}
|
|
10481
|
+
),
|
|
10482
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
10483
|
+
"div",
|
|
10484
|
+
{
|
|
10485
|
+
className: cn(
|
|
10486
|
+
"cls_celebration_modal_card",
|
|
10487
|
+
"relative bg-white rounded-2xl shadow-2xl",
|
|
10488
|
+
"flex flex-col items-center overflow-hidden",
|
|
10489
|
+
"transition-transform duration-150",
|
|
10490
|
+
visible ? "scale-100" : "scale-95"
|
|
10491
|
+
),
|
|
10492
|
+
style: { width: 480, maxWidth: "95vw" },
|
|
10493
|
+
onClick: (e) => e.stopPropagation(),
|
|
10494
|
+
children: [
|
|
10495
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10496
|
+
"button",
|
|
10497
|
+
{
|
|
10498
|
+
className: "cls_celebration_close_btn absolute top-3 right-3 z-10 p-1.5 rounded-full hover:bg-black/10 transition-colors",
|
|
10499
|
+
onClick: close_modal,
|
|
10500
|
+
"aria-label": "Close celebration",
|
|
10501
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-4 w-4 text-gray-600" })
|
|
10502
|
+
}
|
|
10503
|
+
),
|
|
10504
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "cls_celebration_header px-8 pt-8 pb-5 text-center", children: [
|
|
10505
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "cls_celebration_title text-2xl font-bold text-gray-900", children: title }),
|
|
10506
|
+
subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "cls_celebration_subtitle mt-1.5 text-sm text-gray-500 leading-relaxed", children: subtitle })
|
|
10507
|
+
] }),
|
|
10508
|
+
shareableCard && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
10509
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10510
|
+
"div",
|
|
10511
|
+
{
|
|
10512
|
+
className: "cls_celebration_card_preview_wrapper overflow-hidden rounded-lg mx-8",
|
|
10513
|
+
style: { width: PREVIEW_SIZE, height: PREVIEW_SIZE },
|
|
10514
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10515
|
+
"div",
|
|
10516
|
+
{
|
|
10517
|
+
ref: card_ref,
|
|
10518
|
+
className: "cls_celebration_card_inner",
|
|
10519
|
+
style: {
|
|
10520
|
+
width: CARD_SIZE,
|
|
10521
|
+
height: CARD_SIZE,
|
|
10522
|
+
transform: `scale(${CARD_SCALE})`,
|
|
10523
|
+
transformOrigin: "top left",
|
|
10524
|
+
background: card_bg,
|
|
10525
|
+
display: "flex",
|
|
10526
|
+
flexDirection: "column",
|
|
10527
|
+
alignItems: "center",
|
|
10528
|
+
justifyContent: "center",
|
|
10529
|
+
padding: 80,
|
|
10530
|
+
boxSizing: "border-box"
|
|
10531
|
+
},
|
|
10532
|
+
children: [
|
|
10533
|
+
shareableCard.foreground,
|
|
10534
|
+
resolved_caption && /* @__PURE__ */ jsxRuntime.jsx(
|
|
10535
|
+
"p",
|
|
10536
|
+
{
|
|
10537
|
+
style: {
|
|
10538
|
+
marginTop: 32,
|
|
10539
|
+
fontSize: 36,
|
|
10540
|
+
color: "white",
|
|
10541
|
+
textAlign: "center",
|
|
10542
|
+
fontWeight: 600,
|
|
10543
|
+
lineHeight: 1.3
|
|
10544
|
+
},
|
|
10545
|
+
children: resolved_caption
|
|
10546
|
+
}
|
|
10547
|
+
)
|
|
10548
|
+
]
|
|
10549
|
+
}
|
|
10550
|
+
)
|
|
10551
|
+
}
|
|
10552
|
+
),
|
|
10553
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "cls_celebration_card_actions flex flex-wrap gap-2 px-8 py-5", children: [
|
|
10554
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
10555
|
+
Button,
|
|
10556
|
+
{
|
|
10557
|
+
className: "cls_celebration_download_btn",
|
|
10558
|
+
size: "sm",
|
|
10559
|
+
onClick: handle_download,
|
|
10560
|
+
disabled: is_downloading,
|
|
10561
|
+
children: [
|
|
10562
|
+
is_downloading ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-3.5 w-3.5 mr-1.5 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { className: "h-3.5 w-3.5 mr-1.5" }),
|
|
10563
|
+
"Download PNG"
|
|
10564
|
+
]
|
|
10565
|
+
}
|
|
10566
|
+
),
|
|
10567
|
+
can_share && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "outline", size: "sm", onClick: handle_share, children: [
|
|
10568
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Share2, { className: "h-3.5 w-3.5 mr-1.5" }),
|
|
10569
|
+
"Share"
|
|
10570
|
+
] }),
|
|
10571
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "outline", size: "sm", onClick: handle_copy, children: [
|
|
10572
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { className: "h-3.5 w-3.5 mr-1.5" }),
|
|
10573
|
+
"Copy image"
|
|
10574
|
+
] })
|
|
10575
|
+
] })
|
|
10576
|
+
] }),
|
|
10577
|
+
!shareableCard && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "cls_celebration_footer pb-8", children: /* @__PURE__ */ jsxRuntime.jsx(Button, { size: "sm", onClick: close_modal, children: "Dismiss" }) })
|
|
10578
|
+
]
|
|
10579
|
+
}
|
|
10580
|
+
)
|
|
10581
|
+
]
|
|
10582
|
+
}
|
|
10583
|
+
);
|
|
10584
|
+
}
|
|
9304
10585
|
|
|
9305
10586
|
Object.defineProperty(exports, "rawToast", {
|
|
9306
10587
|
enumerable: true,
|
|
@@ -9326,6 +10607,7 @@ exports.Button = Button;
|
|
|
9326
10607
|
exports.ButtonGroup = ButtonGroup;
|
|
9327
10608
|
exports.ButtonGroupSeparator = ButtonGroupSeparator;
|
|
9328
10609
|
exports.ButtonGroupText = ButtonGroupText;
|
|
10610
|
+
exports.CELEBRATION_GRADIENT = CELEBRATION_GRADIENT;
|
|
9329
10611
|
exports.Calendar = Calendar;
|
|
9330
10612
|
exports.Card = Card;
|
|
9331
10613
|
exports.CardContent = CardContent;
|
|
@@ -9333,6 +10615,7 @@ exports.CardDescription = CardDescription;
|
|
|
9333
10615
|
exports.CardFooter = CardFooter;
|
|
9334
10616
|
exports.CardHeader = CardHeader;
|
|
9335
10617
|
exports.CardTitle = CardTitle;
|
|
10618
|
+
exports.CelebrationProvider = CelebrationProvider;
|
|
9336
10619
|
exports.Checkbox = Checkbox;
|
|
9337
10620
|
exports.Collapsible = Collapsible;
|
|
9338
10621
|
exports.CollapsibleContent = CollapsibleContent2;
|
|
@@ -9340,6 +10623,7 @@ exports.CollapsibleTrigger = CollapsibleTrigger2;
|
|
|
9340
10623
|
exports.CommandNodeExtension = CommandNodeExtension;
|
|
9341
10624
|
exports.CommandPill = CommandPill;
|
|
9342
10625
|
exports.CommandPopover = CommandPopover;
|
|
10626
|
+
exports.DateRangeSelector = DateRangeSelector;
|
|
9343
10627
|
exports.Drawer = Drawer;
|
|
9344
10628
|
exports.DrawerClose = DrawerClose;
|
|
9345
10629
|
exports.DrawerContent = DrawerContent;
|
|
@@ -9396,8 +10680,11 @@ exports.HoverCard = HoverCard;
|
|
|
9396
10680
|
exports.HoverCardContent = HoverCardContent;
|
|
9397
10681
|
exports.HoverCardTrigger = HoverCardTrigger;
|
|
9398
10682
|
exports.Input = Input;
|
|
10683
|
+
exports.InverseSparkline = InverseSparkline;
|
|
9399
10684
|
exports.Label = Label3;
|
|
10685
|
+
exports.LineChart = LineChart;
|
|
9400
10686
|
exports.LoadingTimeout = LoadingTimeout;
|
|
10687
|
+
exports.MultiLineChart = MultiLineChart;
|
|
9401
10688
|
exports.Popover = Popover;
|
|
9402
10689
|
exports.PopoverContent = PopoverContent;
|
|
9403
10690
|
exports.PopoverTrigger = PopoverTrigger;
|
|
@@ -9428,7 +10715,9 @@ exports.SkeletonBar = SkeletonBar;
|
|
|
9428
10715
|
exports.SkeletonCircle = SkeletonCircle;
|
|
9429
10716
|
exports.SkeletonGroup = SkeletonGroup;
|
|
9430
10717
|
exports.SkeletonRect = SkeletonRect;
|
|
10718
|
+
exports.Sparkline = Sparkline;
|
|
9431
10719
|
exports.Spinner = Spinner;
|
|
10720
|
+
exports.StackedBars = StackedBars;
|
|
9432
10721
|
exports.Switch = Switch;
|
|
9433
10722
|
exports.Table = Table2;
|
|
9434
10723
|
exports.TableBody = TableBody;
|
|
@@ -9452,18 +10741,30 @@ exports.TooltipProvider = TooltipProvider;
|
|
|
9452
10741
|
exports.TooltipTrigger = TooltipTrigger;
|
|
9453
10742
|
exports.applyKanbanFilter = applyKanbanFilter;
|
|
9454
10743
|
exports.buttonGroupVariants = buttonGroupVariants;
|
|
10744
|
+
exports.celebrate = celebrate;
|
|
9455
10745
|
exports.create_command_suggestion_extension = create_command_suggestion_extension;
|
|
9456
10746
|
exports.errorToast = errorToast;
|
|
10747
|
+
exports.format_num = format_num;
|
|
9457
10748
|
exports.get_hazo_ui_config = get_hazo_ui_config;
|
|
9458
10749
|
exports.parse_commands_from_text = parse_commands_from_text;
|
|
10750
|
+
exports.pick_x_label_indices = pick_x_label_indices;
|
|
9459
10751
|
exports.reset_hazo_ui_config = reset_hazo_ui_config;
|
|
9460
10752
|
exports.resolve_animation_classes = resolve_animation_classes;
|
|
9461
10753
|
exports.set_hazo_ui_config = set_hazo_ui_config;
|
|
9462
10754
|
exports.successToast = successToast;
|
|
9463
10755
|
exports.text_to_tiptap_content = text_to_tiptap_content;
|
|
9464
10756
|
exports.toggleVariants = toggleVariants;
|
|
10757
|
+
exports.useClickOutside = useClickOutside;
|
|
10758
|
+
exports.useCopyToClipboard = useCopyToClipboard;
|
|
10759
|
+
exports.useDebounce = useDebounce;
|
|
9465
10760
|
exports.useErrorDisplay = useErrorDisplay;
|
|
10761
|
+
exports.useFullscreen = useFullscreen;
|
|
10762
|
+
exports.useIsMobile = useIsMobile;
|
|
9466
10763
|
exports.useLoadingState = useLoadingState;
|
|
10764
|
+
exports.useLocalStorage = useLocalStorage;
|
|
9467
10765
|
exports.useMediaQuery = useMediaQuery;
|
|
10766
|
+
exports.useSessionStorage = useSessionStorage;
|
|
10767
|
+
exports.useViewport = useViewport;
|
|
10768
|
+
exports.useWakeLock = useWakeLock;
|
|
9468
10769
|
//# sourceMappingURL=index.cjs.map
|
|
9469
10770
|
//# sourceMappingURL=index.cjs.map
|