klun-ui 0.1.16 → 0.1.19

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.
@@ -1,5 +1,5 @@
1
1
  import { cx } from './chunk-T225J6LV.js';
2
- import { createContext, forwardRef, useState, useRef, useEffect, Children, useCallback, Fragment as Fragment$1, useContext, isValidElement, createElement, useMemo, useLayoutEffect, cloneElement, useSyncExternalStore } from 'react';
2
+ import { createContext, forwardRef, useState, useRef, useEffect, Children, useCallback, useMemo, useLayoutEffect, Fragment as Fragment$1, useContext, isValidElement, createElement, cloneElement, useSyncExternalStore } from 'react';
3
3
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
4
  import { createPortal } from 'react-dom';
5
5
  import { createRoot } from 'react-dom/client';
@@ -2533,6 +2533,353 @@ var Chip = forwardRef(function Chip2({ children, color = "gray", selected = fals
2533
2533
  );
2534
2534
  });
2535
2535
  Chip.displayName = "Chip";
2536
+ var TONE_COLOR = {
2537
+ ok: "green",
2538
+ warn: "orange",
2539
+ err: "red",
2540
+ info: "blue",
2541
+ neutral: "gray",
2542
+ busy: "blue",
2543
+ // ALM / workflow tones.
2544
+ feature: "purple",
2545
+ verified: "sky",
2546
+ stable: "teal",
2547
+ away: "yellow",
2548
+ highlighted: "pink"
2549
+ };
2550
+ var STATUS_TONE = {
2551
+ // success / live
2552
+ running: "ok",
2553
+ active: "ok",
2554
+ online: "ok",
2555
+ ready: "ok",
2556
+ healthy: "ok",
2557
+ completed: "ok",
2558
+ succeeded: "ok",
2559
+ success: "ok",
2560
+ paid: "ok",
2561
+ connected: "ok",
2562
+ enabled: "ok",
2563
+ // neutral / idle
2564
+ stopped: "neutral",
2565
+ inactive: "neutral",
2566
+ offline: "neutral",
2567
+ disabled: "neutral",
2568
+ draft: "neutral",
2569
+ archived: "neutral",
2570
+ unknown: "neutral",
2571
+ default: "neutral",
2572
+ closed: "neutral",
2573
+ // busy / in-progress (information)
2574
+ pending: "busy",
2575
+ creating: "busy",
2576
+ provisioning: "busy",
2577
+ starting: "busy",
2578
+ stopping: "busy",
2579
+ rebooting: "busy",
2580
+ deploying: "busy",
2581
+ processing: "busy",
2582
+ queued: "busy",
2583
+ migrating: "busy",
2584
+ // warning
2585
+ suspended: "warn",
2586
+ paused: "warn",
2587
+ degraded: "warn",
2588
+ expiring: "warn",
2589
+ warning: "warn",
2590
+ "action-required": "warn",
2591
+ overdue: "warn",
2592
+ locked: "warn",
2593
+ // error
2594
+ failed: "err",
2595
+ error: "err",
2596
+ expired: "err",
2597
+ terminated: "err",
2598
+ destroyed: "err",
2599
+ rejected: "err",
2600
+ cancelled: "err",
2601
+ canceled: "err",
2602
+ offline_error: "err",
2603
+ unpaid: "err",
2604
+ // ALM / issue-tracker workflow vocabulary (koda ALM). Colors via the tones above.
2605
+ new: "info",
2606
+ open: "info",
2607
+ triage: "info",
2608
+ reopened: "info",
2609
+ "in-progress": "away",
2610
+ doing: "away",
2611
+ started: "away",
2612
+ wip: "away",
2613
+ "in-review": "feature",
2614
+ reviewing: "feature",
2615
+ "code-review": "feature",
2616
+ "needs-review": "feature",
2617
+ verified: "verified",
2618
+ qa: "verified",
2619
+ tested: "verified",
2620
+ released: "stable",
2621
+ deployed: "stable",
2622
+ live: "stable",
2623
+ merged: "stable",
2624
+ blocked: "warn",
2625
+ "on-hold": "warn",
2626
+ flagged: "highlighted",
2627
+ starred: "highlighted"
2628
+ };
2629
+ function keyOf(s) {
2630
+ return String(s == null ? "" : s).trim().toLowerCase().replace(/\s+/g, "-").replace(/_/g, "_");
2631
+ }
2632
+ function StatusBadge({
2633
+ status,
2634
+ label,
2635
+ tone,
2636
+ map,
2637
+ dot = true,
2638
+ variant = "light",
2639
+ size = "medium",
2640
+ pulse = false,
2641
+ className,
2642
+ ...props
2643
+ }) {
2644
+ const k2 = keyOf(status);
2645
+ const resolvedTone = tone || map && map[k2] || STATUS_TONE[k2] || "neutral";
2646
+ const color = TONE_COLOR[resolvedTone] || "gray";
2647
+ const text = label != null ? label : String(status == null ? "" : status).replace(/[-_]/g, " ");
2648
+ const display = typeof text === "string" ? text.charAt(0).toUpperCase() + text.slice(1) : text;
2649
+ return /* @__PURE__ */ jsxs(
2650
+ Badge,
2651
+ {
2652
+ color,
2653
+ variant,
2654
+ size,
2655
+ dot: dot && !pulse,
2656
+ className: cx("klun-status-badge", className),
2657
+ ...props,
2658
+ children: [
2659
+ pulse ? /* @__PURE__ */ jsxs("span", { className: "klun-status-badge__ping", children: [
2660
+ /* @__PURE__ */ jsx("span", { className: "klun-status-badge__ping-wave" }),
2661
+ /* @__PURE__ */ jsx("span", { className: "klun-status-badge__ping-core" })
2662
+ ] }) : null,
2663
+ display
2664
+ ]
2665
+ }
2666
+ );
2667
+ }
2668
+ StatusBadge.displayName = "StatusBadge";
2669
+ StatusBadge.STATUS_TONE = STATUS_TONE;
2670
+ StatusBadge.TONE_COLOR = TONE_COLOR;
2671
+ function useTick(ms) {
2672
+ const [, force] = useState(0);
2673
+ useEffect(() => {
2674
+ if (!ms) return;
2675
+ const id = setInterval(() => force((n) => n + 1), ms);
2676
+ return () => clearInterval(id);
2677
+ }, [ms]);
2678
+ }
2679
+ var RelativeTime = forwardRef(function RelativeTime2({ date, locale, live = true, every = 6e4, absolute, className, ...props }, ref) {
2680
+ useTick(live ? every : 0);
2681
+ const rel = Format.relTime(date, { locale });
2682
+ const title = Format.absTime(date, { locale });
2683
+ return /* @__PURE__ */ jsx(
2684
+ "time",
2685
+ {
2686
+ ref,
2687
+ title: absolute ? rel : title,
2688
+ className: cx("klun-relative-time", className),
2689
+ ...props,
2690
+ children: absolute ? title : rel
2691
+ }
2692
+ );
2693
+ });
2694
+ RelativeTime.displayName = "RelativeTime";
2695
+ function defaultFormat(cd) {
2696
+ if (cd.expired) {
2697
+ const d = Math.abs(cd.days);
2698
+ return d >= 1 ? `Expired ${d}d ago` : "Expired";
2699
+ }
2700
+ if (cd.days >= 1) return `${cd.days}d ${cd.hours}h`;
2701
+ if (cd.hours >= 1) return `${cd.hours}h ${cd.minutes}m`;
2702
+ return `${cd.minutes}m`;
2703
+ }
2704
+ var ExpiryCountdown = forwardRef(
2705
+ function ExpiryCountdown2({ date, warnDays = 7, every = 6e4, format, showIcon = true, className, ...props }, ref) {
2706
+ useTick(every);
2707
+ const c = Format.countdown(date);
2708
+ const tone = c.expired ? "err" : c.days < warnDays ? "warn" : "ok";
2709
+ const text = (format || defaultFormat)(c);
2710
+ return /* @__PURE__ */ jsxs(
2711
+ "span",
2712
+ {
2713
+ ref,
2714
+ className: cx("klun-relative-time", `klun-relative-time--${tone}`, className),
2715
+ ...props,
2716
+ children: [
2717
+ showIcon ? /* @__PURE__ */ jsx(
2718
+ "i",
2719
+ {
2720
+ className: cx(
2721
+ "klun-relative-time__icon",
2722
+ c.expired ? "ri-time-line" : "ri-timer-line"
2723
+ ),
2724
+ "aria-hidden": true
2725
+ }
2726
+ ) : null,
2727
+ text
2728
+ ]
2729
+ }
2730
+ );
2731
+ }
2732
+ );
2733
+ ExpiryCountdown.displayName = "ExpiryCountdown";
2734
+ var Skeleton = forwardRef(function Skeleton2({ width = "100%", height = 16, circle = false, className, style, ...props }, ref) {
2735
+ return /* @__PURE__ */ jsx(
2736
+ "span",
2737
+ {
2738
+ ref,
2739
+ "aria-hidden": true,
2740
+ className: cx("klun-skeleton", circle && "klun-skeleton--circle", className),
2741
+ style: {
2742
+ ["--klun-skeleton-width"]: typeof width === "number" ? `${width}px` : width,
2743
+ ["--klun-skeleton-height"]: typeof height === "number" ? `${height}px` : height,
2744
+ ...style
2745
+ },
2746
+ ...props
2747
+ }
2748
+ );
2749
+ });
2750
+ Skeleton.displayName = "Skeleton";
2751
+ var KIND_ICON = {
2752
+ artifact: "ri-price-tag-3-line",
2753
+ pr: "ri-git-pull-request-line",
2754
+ commit: "ri-git-commit-line",
2755
+ doc: "ri-file-text-line",
2756
+ wiki: "ri-book-2-line"
2757
+ };
2758
+ var KIND_COLOR = {
2759
+ artifact: "gray",
2760
+ pr: "purple",
2761
+ commit: "gray",
2762
+ doc: "blue",
2763
+ wiki: "sky"
2764
+ };
2765
+ var OPEN_DELAY = 300;
2766
+ function isStructured(p) {
2767
+ return !!p && typeof p === "object" && !isValidElement(p) && "title" in p;
2768
+ }
2769
+ var CrossRefBadge = forwardRef(
2770
+ function CrossRefBadge2({ label, kind = "artifact", href, onClick, status, color, preview, resolved = false, disabled = false, className }, ref) {
2771
+ const [open, setOpen] = useState(false);
2772
+ const [loading, setLoading] = useState(false);
2773
+ const [loaded, setLoaded] = useState(null);
2774
+ const timer = useRef(null);
2775
+ const loadedOnce = useRef(false);
2776
+ const hasCard = preview != null;
2777
+ const load = useCallback(() => {
2778
+ if (loadedOnce.current || typeof preview !== "function") return;
2779
+ loadedOnce.current = true;
2780
+ setLoading(true);
2781
+ Promise.resolve(preview()).then((data) => setLoaded(data)).finally(() => setLoading(false));
2782
+ }, [preview]);
2783
+ const scheduleOpen = useCallback(() => {
2784
+ if (disabled || !hasCard) return;
2785
+ if (timer.current) clearTimeout(timer.current);
2786
+ timer.current = setTimeout(() => {
2787
+ setOpen(true);
2788
+ load();
2789
+ }, OPEN_DELAY);
2790
+ }, [disabled, hasCard, load]);
2791
+ const close = useCallback(() => {
2792
+ if (timer.current) clearTimeout(timer.current);
2793
+ setOpen(false);
2794
+ }, []);
2795
+ useEffect(() => () => {
2796
+ if (timer.current) clearTimeout(timer.current);
2797
+ }, []);
2798
+ useEffect(() => {
2799
+ if (!open) return;
2800
+ const onKey = (e) => {
2801
+ if (e.key === "Escape") close();
2802
+ };
2803
+ document.addEventListener("keydown", onKey);
2804
+ return () => document.removeEventListener("keydown", onKey);
2805
+ }, [open, close]);
2806
+ const iconCls = KIND_ICON[kind];
2807
+ const chipColor = color ?? KIND_COLOR[kind];
2808
+ const inner = /* @__PURE__ */ jsxs(Chip, { color: chipColor, className: "klun-xref__chip", children: [
2809
+ /* @__PURE__ */ jsx("i", { className: cx("klun-xref__icon", iconCls), "aria-hidden": true }),
2810
+ /* @__PURE__ */ jsx("span", { className: "klun-xref__label", children: label })
2811
+ ] });
2812
+ let card = null;
2813
+ if (open) {
2814
+ if (loading) {
2815
+ card = /* @__PURE__ */ jsxs("div", { className: "klun-xref__card klun-xref__card--loading", children: [
2816
+ /* @__PURE__ */ jsx(Skeleton, { width: "70%", height: 14 }),
2817
+ /* @__PURE__ */ jsx(Skeleton, { width: "45%", height: 12 })
2818
+ ] });
2819
+ } else {
2820
+ const data = loaded ?? (isStructured(preview) ? preview : null);
2821
+ if (data) {
2822
+ card = /* @__PURE__ */ jsxs("div", { className: "klun-xref__card", role: "dialog", children: [
2823
+ /* @__PURE__ */ jsx("div", { className: "klun-xref__card-title", children: data.title }),
2824
+ (data.status || data.assignee) && /* @__PURE__ */ jsxs("div", { className: "klun-xref__card-meta", children: [
2825
+ data.status ? /* @__PURE__ */ jsx(StatusBadge, { status: data.status, size: "small" }) : null,
2826
+ data.assignee ? /* @__PURE__ */ jsx("span", { className: "klun-xref__assignee", children: data.assignee }) : null
2827
+ ] }),
2828
+ data.updatedAt != null ? /* @__PURE__ */ jsx(RelativeTime, { className: "klun-xref__time", date: data.updatedAt }) : null,
2829
+ data.meta ? /* @__PURE__ */ jsx("div", { className: "klun-xref__card-extra", children: data.meta }) : null
2830
+ ] });
2831
+ } else if (!isStructured(preview) && typeof preview !== "function") {
2832
+ card = /* @__PURE__ */ jsx("div", { className: "klun-xref__card", role: "dialog", children: preview });
2833
+ }
2834
+ }
2835
+ }
2836
+ const commonProps = {
2837
+ className: "klun-xref__trigger",
2838
+ onMouseEnter: scheduleOpen,
2839
+ onMouseLeave: close,
2840
+ onFocus: () => {
2841
+ if (!disabled && hasCard) {
2842
+ setOpen(true);
2843
+ load();
2844
+ }
2845
+ },
2846
+ onBlur: close
2847
+ };
2848
+ return /* @__PURE__ */ jsxs(
2849
+ "span",
2850
+ {
2851
+ ref,
2852
+ className: cx(
2853
+ "klun-xref",
2854
+ resolved && "klun-xref--resolved",
2855
+ disabled && "klun-xref--disabled",
2856
+ className
2857
+ ),
2858
+ children: [
2859
+ href && !disabled ? /* @__PURE__ */ jsx("a", { href, ...commonProps, children: inner }) : /* @__PURE__ */ jsx(
2860
+ "span",
2861
+ {
2862
+ ...commonProps,
2863
+ role: "button",
2864
+ tabIndex: disabled ? void 0 : 0,
2865
+ "aria-disabled": disabled || void 0,
2866
+ onClick: disabled ? void 0 : onClick,
2867
+ onKeyDown: (e) => {
2868
+ if (!disabled && onClick && (e.key === "Enter" || e.key === " ")) {
2869
+ e.preventDefault();
2870
+ onClick();
2871
+ }
2872
+ },
2873
+ children: inner
2874
+ }
2875
+ ),
2876
+ card
2877
+ ]
2878
+ }
2879
+ );
2880
+ }
2881
+ );
2882
+ CrossRefBadge.displayName = "CrossRefBadge";
2536
2883
  var CodeViewer = forwardRef(function CodeViewer2({
2537
2884
  code = "",
2538
2885
  title,
@@ -2772,6 +3119,225 @@ var Descriptions = forwardRef(
2772
3119
  }
2773
3120
  );
2774
3121
  Descriptions.displayName = "Descriptions";
3122
+ var HUNK_RE = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@(.*)$/;
3123
+ function parsePatch(patch) {
3124
+ const hunks = [];
3125
+ let cur = null;
3126
+ let oldNo = 0;
3127
+ let newNo = 0;
3128
+ for (const raw of String(patch).replace(/\n$/, "").split("\n")) {
3129
+ const m = HUNK_RE.exec(raw);
3130
+ if (m) {
3131
+ oldNo = parseInt(m[1], 10);
3132
+ newNo = parseInt(m[2], 10);
3133
+ cur = { header: m[3].trim() || void 0, lines: [{ type: "hunk", oldNo: null, newNo: null, text: raw }] };
3134
+ hunks.push(cur);
3135
+ continue;
3136
+ }
3137
+ if (!cur) {
3138
+ cur = { lines: [] };
3139
+ hunks.push(cur);
3140
+ }
3141
+ const c = raw[0];
3142
+ if (c === "+") cur.lines.push({ type: "add", oldNo: null, newNo: newNo++, text: raw.slice(1) });
3143
+ else if (c === "-") cur.lines.push({ type: "del", oldNo: oldNo++, newNo: null, text: raw.slice(1) });
3144
+ else if (c === "\\") cur.lines.push({ type: "context", oldNo: null, newNo: null, text: raw });
3145
+ else cur.lines.push({ type: "context", oldNo: oldNo++, newNo: newNo++, text: c === " " ? raw.slice(1) : raw });
3146
+ }
3147
+ return hunks;
3148
+ }
3149
+ function toSplitRows(lines) {
3150
+ const rows = [];
3151
+ let dels = [];
3152
+ let adds = [];
3153
+ const flush = () => {
3154
+ const n = Math.max(dels.length, adds.length);
3155
+ for (let i = 0; i < n; i++) rows.push({ kind: "pair", left: dels[i], right: adds[i] });
3156
+ dels = [];
3157
+ adds = [];
3158
+ };
3159
+ for (const ln of lines) {
3160
+ if (ln.type === "hunk") {
3161
+ flush();
3162
+ rows.push({ kind: "hunk", text: ln.text });
3163
+ } else if (ln.type === "del") dels.push(ln);
3164
+ else if (ln.type === "add") adds.push(ln);
3165
+ else {
3166
+ flush();
3167
+ rows.push({ kind: "pair", left: ln, right: ln });
3168
+ }
3169
+ }
3170
+ flush();
3171
+ return rows;
3172
+ }
3173
+ var DiffViewer = forwardRef(function DiffViewer2({
3174
+ hunks,
3175
+ patch,
3176
+ filename,
3177
+ additions,
3178
+ deletions,
3179
+ mode = "split",
3180
+ language,
3181
+ wrap = false,
3182
+ collapsible = false,
3183
+ defaultCollapsed = false,
3184
+ comments,
3185
+ onAddComment,
3186
+ toolbar,
3187
+ dark = false,
3188
+ maxHeight = 480,
3189
+ className,
3190
+ style,
3191
+ ...props
3192
+ }, ref) {
3193
+ const [collapsed, setCollapsed] = useState(defaultCollapsed);
3194
+ const data = useMemo(
3195
+ () => hunks ?? (patch != null ? parsePatch(patch) : []),
3196
+ [hunks, patch]
3197
+ );
3198
+ const allLines = useMemo(() => data.flatMap((h) => h.lines), [data]);
3199
+ const gutter = (side, no) => {
3200
+ if (no == null) return /* @__PURE__ */ jsx("span", { className: "klun-diff__num" });
3201
+ const addable = !!onAddComment;
3202
+ return /* @__PURE__ */ jsxs(
3203
+ "span",
3204
+ {
3205
+ className: cx("klun-diff__num", addable && "klun-diff__num--addable"),
3206
+ onClick: addable ? () => onAddComment({ side, lineNo: no }) : void 0,
3207
+ role: addable ? "button" : void 0,
3208
+ "aria-label": addable ? `Comment on ${side} line ${no}` : void 0,
3209
+ children: [
3210
+ addable ? /* @__PURE__ */ jsx("span", { className: "klun-diff__add-icon", "aria-hidden": true, children: "+" }) : null,
3211
+ /* @__PURE__ */ jsx("span", { className: "klun-diff__num-val", children: no })
3212
+ ]
3213
+ }
3214
+ );
3215
+ };
3216
+ const commentFor = (side, no) => {
3217
+ if (no == null || !comments) return null;
3218
+ return comments[`${side}:${no}`] ?? null;
3219
+ };
3220
+ const wrapCls = wrap && "klun-diff__code--wrap";
3221
+ const renderUnified = () => /* @__PURE__ */ jsx("table", { className: "klun-diff__table klun-diff__table--unified", children: /* @__PURE__ */ jsx("tbody", { children: allLines.map((ln, i) => {
3222
+ if (ln.type === "hunk") {
3223
+ return /* @__PURE__ */ jsxs("tr", { className: "klun-diff__row klun-diff__row--hunk", children: [
3224
+ /* @__PURE__ */ jsx("td", { className: "klun-diff__gutter", colSpan: 2 }),
3225
+ /* @__PURE__ */ jsx("td", { className: "klun-diff__code", children: ln.text })
3226
+ ] }, i);
3227
+ }
3228
+ const side = ln.type === "del" ? "old" : "new";
3229
+ const no = ln.type === "del" ? ln.oldNo : ln.newNo;
3230
+ const comment = commentFor(side, no);
3231
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
3232
+ /* @__PURE__ */ jsxs("tr", { className: cx("klun-diff__row", `klun-diff__row--${ln.type}`), children: [
3233
+ /* @__PURE__ */ jsx("td", { className: "klun-diff__gutter", children: gutter("old", ln.oldNo) }),
3234
+ /* @__PURE__ */ jsx("td", { className: "klun-diff__gutter", children: gutter("new", ln.newNo) }),
3235
+ /* @__PURE__ */ jsxs("td", { className: cx("klun-diff__code", wrapCls), children: [
3236
+ /* @__PURE__ */ jsx("span", { className: "klun-diff__sign", "aria-hidden": true, children: ln.type === "add" ? "+" : ln.type === "del" ? "-" : " " }),
3237
+ ln.text
3238
+ ] })
3239
+ ] }),
3240
+ comment ? /* @__PURE__ */ jsxs("tr", { className: "klun-diff__row klun-diff__row--comment", children: [
3241
+ /* @__PURE__ */ jsx("td", { className: "klun-diff__gutter", colSpan: 2 }),
3242
+ /* @__PURE__ */ jsx("td", { className: "klun-diff__comment", children: comment })
3243
+ ] }) : null
3244
+ ] }, i);
3245
+ }) }) });
3246
+ const renderSplit = () => {
3247
+ const rows = toSplitRows(allLines);
3248
+ return /* @__PURE__ */ jsx("table", { className: "klun-diff__table klun-diff__table--split", children: /* @__PURE__ */ jsx("tbody", { children: rows.map((row, i) => {
3249
+ if (row.kind === "hunk") {
3250
+ return /* @__PURE__ */ jsx("tr", { className: "klun-diff__row klun-diff__row--hunk", children: /* @__PURE__ */ jsx("td", { className: "klun-diff__code", colSpan: 4, children: row.text }) }, i);
3251
+ }
3252
+ const { left, right } = row;
3253
+ const cL = commentFor("old", left?.oldNo);
3254
+ const cR = commentFor("new", right?.newNo);
3255
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
3256
+ /* @__PURE__ */ jsxs("tr", { className: "klun-diff__row", children: [
3257
+ /* @__PURE__ */ jsx("td", { className: "klun-diff__gutter", children: gutter("old", left?.oldNo) }),
3258
+ /* @__PURE__ */ jsx(
3259
+ "td",
3260
+ {
3261
+ className: cx(
3262
+ "klun-diff__code",
3263
+ wrapCls,
3264
+ left && `klun-diff__code--${left.type}`,
3265
+ !left && "klun-diff__code--empty"
3266
+ ),
3267
+ children: left?.text
3268
+ }
3269
+ ),
3270
+ /* @__PURE__ */ jsx("td", { className: "klun-diff__gutter", children: gutter("new", right?.newNo) }),
3271
+ /* @__PURE__ */ jsx(
3272
+ "td",
3273
+ {
3274
+ className: cx(
3275
+ "klun-diff__code",
3276
+ wrapCls,
3277
+ right && `klun-diff__code--${right.type}`,
3278
+ !right && "klun-diff__code--empty"
3279
+ ),
3280
+ children: right?.text
3281
+ }
3282
+ )
3283
+ ] }),
3284
+ cL || cR ? /* @__PURE__ */ jsx("tr", { className: "klun-diff__row klun-diff__row--comment", children: /* @__PURE__ */ jsxs("td", { className: "klun-diff__comment", colSpan: 4, children: [
3285
+ cL,
3286
+ cR
3287
+ ] }) }) : null
3288
+ ] }, i);
3289
+ }) }) });
3290
+ };
3291
+ const hasHeader = filename != null || additions != null || deletions != null || toolbar != null || collapsible;
3292
+ return /* @__PURE__ */ jsxs(
3293
+ "div",
3294
+ {
3295
+ ref,
3296
+ className: cx(
3297
+ "klun-diff",
3298
+ `klun-diff--${mode}`,
3299
+ dark && "klun-diff--dark",
3300
+ className
3301
+ ),
3302
+ "data-language": language || void 0,
3303
+ style,
3304
+ ...props,
3305
+ children: [
3306
+ hasHeader ? /* @__PURE__ */ jsxs("div", { className: "klun-diff__header", children: [
3307
+ collapsible ? /* @__PURE__ */ jsx(
3308
+ "button",
3309
+ {
3310
+ type: "button",
3311
+ className: "klun-diff__toggle",
3312
+ "aria-expanded": !collapsed,
3313
+ onClick: () => setCollapsed((c) => !c),
3314
+ children: /* @__PURE__ */ jsx("i", { className: cx("ri-arrow-right-s-line", !collapsed && "klun-diff__toggle-icon--open"), "aria-hidden": true })
3315
+ }
3316
+ ) : null,
3317
+ filename != null ? /* @__PURE__ */ jsx("span", { className: "klun-diff__filename", children: filename }) : null,
3318
+ additions != null ? /* @__PURE__ */ jsxs("span", { className: "klun-diff__stat klun-diff__stat--add", children: [
3319
+ "+",
3320
+ additions
3321
+ ] }) : null,
3322
+ deletions != null ? /* @__PURE__ */ jsxs("span", { className: "klun-diff__stat klun-diff__stat--del", children: [
3323
+ "\u2212",
3324
+ deletions
3325
+ ] }) : null,
3326
+ toolbar != null ? /* @__PURE__ */ jsx("span", { className: "klun-diff__toolbar", children: toolbar }) : null
3327
+ ] }) : null,
3328
+ !collapsed ? /* @__PURE__ */ jsx(
3329
+ "div",
3330
+ {
3331
+ className: "klun-diff__scroll",
3332
+ style: { ["--klun-diff-max-height"]: `${maxHeight}px` },
3333
+ children: mode === "unified" ? renderUnified() : renderSplit()
3334
+ }
3335
+ ) : null
3336
+ ]
3337
+ }
3338
+ );
3339
+ });
3340
+ DiffViewer.displayName = "DiffViewer";
2775
3341
  var Kbd = forwardRef(function Kbd2({ className, children, ...props }, ref) {
2776
3342
  return /* @__PURE__ */ jsx("kbd", { ref, className: cx("klun-kbd", className), ...props, children });
2777
3343
  });
@@ -3107,202 +3673,33 @@ var Rating = forwardRef(function Rating2({ value = 0, max = 5, onChange, readOnl
3107
3673
  )) });
3108
3674
  });
3109
3675
  Rating.displayName = "Rating";
3110
- function useTick(ms) {
3111
- const [, force] = useState(0);
3112
- useEffect(() => {
3113
- if (!ms) return;
3114
- const id = setInterval(() => force((n) => n + 1), ms);
3115
- return () => clearInterval(id);
3116
- }, [ms]);
3117
- }
3118
- var RelativeTime = forwardRef(function RelativeTime2({ date, locale, live = true, every = 6e4, absolute, className, ...props }, ref) {
3119
- useTick(live ? every : 0);
3120
- const rel = Format.relTime(date, { locale });
3121
- const title = Format.absTime(date, { locale });
3122
- return /* @__PURE__ */ jsx(
3123
- "time",
3124
- {
3125
- ref,
3126
- title: absolute ? rel : title,
3127
- className: cx("klun-relative-time", className),
3128
- ...props,
3129
- children: absolute ? title : rel
3130
- }
3131
- );
3132
- });
3133
- RelativeTime.displayName = "RelativeTime";
3134
- function defaultFormat(cd) {
3135
- if (cd.expired) {
3136
- const d = Math.abs(cd.days);
3137
- return d >= 1 ? `Expired ${d}d ago` : "Expired";
3138
- }
3139
- if (cd.days >= 1) return `${cd.days}d ${cd.hours}h`;
3140
- if (cd.hours >= 1) return `${cd.hours}h ${cd.minutes}m`;
3141
- return `${cd.minutes}m`;
3142
- }
3143
- var ExpiryCountdown = forwardRef(
3144
- function ExpiryCountdown2({ date, warnDays = 7, every = 6e4, format, showIcon = true, className, ...props }, ref) {
3145
- useTick(every);
3146
- const c = Format.countdown(date);
3147
- const tone = c.expired ? "err" : c.days < warnDays ? "warn" : "ok";
3148
- const text = (format || defaultFormat)(c);
3149
- return /* @__PURE__ */ jsxs(
3150
- "span",
3151
- {
3152
- ref,
3153
- className: cx("klun-relative-time", `klun-relative-time--${tone}`, className),
3154
- ...props,
3155
- children: [
3156
- showIcon ? /* @__PURE__ */ jsx(
3157
- "i",
3158
- {
3159
- className: cx(
3160
- "klun-relative-time__icon",
3161
- c.expired ? "ri-time-line" : "ri-timer-line"
3162
- ),
3163
- "aria-hidden": true
3164
- }
3165
- ) : null,
3166
- text
3167
- ]
3168
- }
3169
- );
3170
- }
3171
- );
3172
- ExpiryCountdown.displayName = "ExpiryCountdown";
3173
3676
  var TREND_ICONS = {
3174
3677
  up: "ri-arrow-up-line",
3175
3678
  down: "ri-arrow-down-line",
3176
- flat: "ri-subtract-line"
3177
- };
3178
- var Statistic = forwardRef(function Statistic2({ label, value, prefix, suffix, precision, trend, trendValue, icon, size: sizeProp, className, ...props }, ref) {
3179
- const size = useSize(sizeProp);
3180
- const formatted = precision != null && typeof value === "number" ? value.toFixed(precision) : value;
3181
- return /* @__PURE__ */ jsxs("div", { ref, className: cx("klun-statistic", `klun-statistic--${size}`, className), ...props, children: [
3182
- /* @__PURE__ */ jsxs("div", { className: "klun-statistic__head", children: [
3183
- icon ? /* @__PURE__ */ jsx("span", { className: "klun-statistic__icon", children: icon }) : null,
3184
- /* @__PURE__ */ jsx("span", { className: "klun-statistic__label", children: label })
3185
- ] }),
3186
- /* @__PURE__ */ jsxs("div", { className: "klun-statistic__body", children: [
3187
- /* @__PURE__ */ jsxs("span", { className: "klun-statistic__value", children: [
3188
- prefix ? /* @__PURE__ */ jsx("span", { className: "klun-statistic__prefix", children: prefix }) : null,
3189
- formatted,
3190
- suffix ? /* @__PURE__ */ jsx("span", { className: "klun-statistic__suffix", children: suffix }) : null
3191
- ] }),
3192
- trend ? /* @__PURE__ */ jsxs("span", { className: cx("klun-statistic__trend", `klun-statistic__trend--${trend}`), children: [
3193
- /* @__PURE__ */ jsx("i", { className: TREND_ICONS[trend] }),
3194
- trendValue
3195
- ] }) : null
3196
- ] })
3197
- ] });
3198
- });
3199
- Statistic.displayName = "Statistic";
3200
- var TONE_COLOR = {
3201
- ok: "green",
3202
- warn: "orange",
3203
- err: "red",
3204
- info: "blue",
3205
- neutral: "gray",
3206
- busy: "blue"
3207
- };
3208
- var STATUS_TONE = {
3209
- // success / live
3210
- running: "ok",
3211
- active: "ok",
3212
- online: "ok",
3213
- ready: "ok",
3214
- healthy: "ok",
3215
- completed: "ok",
3216
- succeeded: "ok",
3217
- success: "ok",
3218
- verified: "ok",
3219
- paid: "ok",
3220
- connected: "ok",
3221
- enabled: "ok",
3222
- // neutral / idle
3223
- stopped: "neutral",
3224
- inactive: "neutral",
3225
- offline: "neutral",
3226
- disabled: "neutral",
3227
- draft: "neutral",
3228
- archived: "neutral",
3229
- unknown: "neutral",
3230
- default: "neutral",
3231
- closed: "neutral",
3232
- // busy / in-progress (information)
3233
- pending: "busy",
3234
- creating: "busy",
3235
- provisioning: "busy",
3236
- starting: "busy",
3237
- stopping: "busy",
3238
- rebooting: "busy",
3239
- deploying: "busy",
3240
- processing: "busy",
3241
- queued: "busy",
3242
- migrating: "busy",
3243
- // warning
3244
- suspended: "warn",
3245
- paused: "warn",
3246
- degraded: "warn",
3247
- expiring: "warn",
3248
- warning: "warn",
3249
- "action-required": "warn",
3250
- overdue: "warn",
3251
- locked: "warn",
3252
- // error
3253
- failed: "err",
3254
- error: "err",
3255
- expired: "err",
3256
- terminated: "err",
3257
- destroyed: "err",
3258
- rejected: "err",
3259
- cancelled: "err",
3260
- canceled: "err",
3261
- offline_error: "err",
3262
- unpaid: "err"
3263
- };
3264
- function keyOf(s) {
3265
- return String(s == null ? "" : s).trim().toLowerCase().replace(/\s+/g, "-").replace(/_/g, "_");
3266
- }
3267
- function StatusBadge({
3268
- status,
3269
- label,
3270
- tone,
3271
- map,
3272
- dot = true,
3273
- variant = "light",
3274
- size = "medium",
3275
- pulse = false,
3276
- className,
3277
- ...props
3278
- }) {
3279
- const k2 = keyOf(status);
3280
- const resolvedTone = tone || map && map[k2] || STATUS_TONE[k2] || "neutral";
3281
- const color = TONE_COLOR[resolvedTone] || "gray";
3282
- const text = label != null ? label : String(status == null ? "" : status).replace(/[-_]/g, " ");
3283
- const display = typeof text === "string" ? text.charAt(0).toUpperCase() + text.slice(1) : text;
3284
- return /* @__PURE__ */ jsxs(
3285
- Badge,
3286
- {
3287
- color,
3288
- variant,
3289
- size,
3290
- dot: dot && !pulse,
3291
- className: cx("klun-status-badge", className),
3292
- ...props,
3293
- children: [
3294
- pulse ? /* @__PURE__ */ jsxs("span", { className: "klun-status-badge__ping", children: [
3295
- /* @__PURE__ */ jsx("span", { className: "klun-status-badge__ping-wave" }),
3296
- /* @__PURE__ */ jsx("span", { className: "klun-status-badge__ping-core" })
3297
- ] }) : null,
3298
- display
3299
- ]
3300
- }
3301
- );
3302
- }
3303
- StatusBadge.displayName = "StatusBadge";
3304
- StatusBadge.STATUS_TONE = STATUS_TONE;
3305
- StatusBadge.TONE_COLOR = TONE_COLOR;
3679
+ flat: "ri-subtract-line"
3680
+ };
3681
+ var Statistic = forwardRef(function Statistic2({ label, value, prefix, suffix, precision, trend, trendValue, icon, size: sizeProp, className, ...props }, ref) {
3682
+ const size = useSize(sizeProp);
3683
+ const formatted = precision != null && typeof value === "number" ? value.toFixed(precision) : value;
3684
+ return /* @__PURE__ */ jsxs("div", { ref, className: cx("klun-statistic", `klun-statistic--${size}`, className), ...props, children: [
3685
+ /* @__PURE__ */ jsxs("div", { className: "klun-statistic__head", children: [
3686
+ icon ? /* @__PURE__ */ jsx("span", { className: "klun-statistic__icon", children: icon }) : null,
3687
+ /* @__PURE__ */ jsx("span", { className: "klun-statistic__label", children: label })
3688
+ ] }),
3689
+ /* @__PURE__ */ jsxs("div", { className: "klun-statistic__body", children: [
3690
+ /* @__PURE__ */ jsxs("span", { className: "klun-statistic__value", children: [
3691
+ prefix ? /* @__PURE__ */ jsx("span", { className: "klun-statistic__prefix", children: prefix }) : null,
3692
+ formatted,
3693
+ suffix ? /* @__PURE__ */ jsx("span", { className: "klun-statistic__suffix", children: suffix }) : null
3694
+ ] }),
3695
+ trend ? /* @__PURE__ */ jsxs("span", { className: cx("klun-statistic__trend", `klun-statistic__trend--${trend}`), children: [
3696
+ /* @__PURE__ */ jsx("i", { className: TREND_ICONS[trend] }),
3697
+ trendValue
3698
+ ] }) : null
3699
+ ] })
3700
+ ] });
3701
+ });
3702
+ Statistic.displayName = "Statistic";
3306
3703
  var SIZES2 = { xs: 6, sm: 8, md: 10, lg: 12 };
3307
3704
  var StatusDot = forwardRef(function StatusDot2({ tone = "neutral", size = "sm", pulse = false, label, className, style, ...props }, ref) {
3308
3705
  const sizeStyle = { ["--klun-status-dot-size"]: `${SIZES2[size] ?? SIZES2.sm}px` };
@@ -4917,9 +5314,14 @@ var Timeline = forwardRef(function Timeline2({ items: items2 = [], mode = "left"
4917
5314
  }) });
4918
5315
  });
4919
5316
  Timeline.displayName = "Timeline";
5317
+ var GAP = 6;
5318
+ var MARGIN = 8;
4920
5319
  var Tooltip = forwardRef(function Tooltip2({ content, side = "top", delay = 200, disabled = false, children, style, className }, ref) {
4921
5320
  const [open, setOpen] = useState(false);
5321
+ const [pos, setPos] = useState(null);
4922
5322
  const timer = useRef(null);
5323
+ const innerRef = useRef(null);
5324
+ const bubbleRef = useRef(null);
4923
5325
  const show = () => {
4924
5326
  if (disabled || !content) return;
4925
5327
  if (timer.current) clearTimeout(timer.current);
@@ -4932,10 +5334,70 @@ var Tooltip = forwardRef(function Tooltip2({ content, side = "top", delay = 200,
4932
5334
  useEffect(() => () => {
4933
5335
  if (timer.current) clearTimeout(timer.current);
4934
5336
  }, []);
5337
+ useLayoutEffect(() => {
5338
+ if (!open) {
5339
+ setPos(null);
5340
+ return;
5341
+ }
5342
+ const place2 = () => {
5343
+ const trigger = innerRef.current;
5344
+ const bubble = bubbleRef.current;
5345
+ if (!trigger || !bubble) return;
5346
+ const tr = trigger.getBoundingClientRect();
5347
+ const bw = bubble.offsetWidth;
5348
+ const bh = bubble.offsetHeight;
5349
+ const vw = document.documentElement.clientWidth;
5350
+ const vh = document.documentElement.clientHeight;
5351
+ let top;
5352
+ let left;
5353
+ switch (side) {
5354
+ case "bottom":
5355
+ top = tr.bottom + GAP;
5356
+ left = tr.left + tr.width / 2 - bw / 2;
5357
+ break;
5358
+ case "left":
5359
+ left = tr.left - bw - GAP;
5360
+ top = tr.top + tr.height / 2 - bh / 2;
5361
+ break;
5362
+ case "right":
5363
+ left = tr.right + GAP;
5364
+ top = tr.top + tr.height / 2 - bh / 2;
5365
+ break;
5366
+ default:
5367
+ top = tr.top - bh - GAP;
5368
+ left = tr.left + tr.width / 2 - bw / 2;
5369
+ }
5370
+ left = Math.max(MARGIN, Math.min(left, vw - bw - MARGIN));
5371
+ top = Math.max(MARGIN, Math.min(top, vh - bh - MARGIN));
5372
+ setPos({ top, left });
5373
+ };
5374
+ place2();
5375
+ window.addEventListener("scroll", place2, true);
5376
+ window.addEventListener("resize", place2);
5377
+ return () => {
5378
+ window.removeEventListener("scroll", place2, true);
5379
+ window.removeEventListener("resize", place2);
5380
+ };
5381
+ }, [open, side, content]);
5382
+ const setRefs = (node) => {
5383
+ innerRef.current = node;
5384
+ if (typeof ref === "function") ref(node);
5385
+ else if (ref) ref.current = node;
5386
+ };
5387
+ const bubbleStyle = {
5388
+ ...style,
5389
+ position: "fixed",
5390
+ top: pos ? pos.top : 0,
5391
+ left: pos ? pos.left : 0,
5392
+ right: "auto",
5393
+ bottom: "auto",
5394
+ transform: "none",
5395
+ visibility: pos ? "visible" : "hidden"
5396
+ };
4935
5397
  return /* @__PURE__ */ jsxs(
4936
5398
  "span",
4937
5399
  {
4938
- ref,
5400
+ ref: setRefs,
4939
5401
  className: cx("klun-tooltip", className),
4940
5402
  onMouseEnter: show,
4941
5403
  onMouseLeave: hide,
@@ -4943,7 +5405,19 @@ var Tooltip = forwardRef(function Tooltip2({ content, side = "top", delay = 200,
4943
5405
  onBlur: hide,
4944
5406
  children: [
4945
5407
  children,
4946
- open ? /* @__PURE__ */ jsx("span", { role: "tooltip", className: cx("klun-tooltip__bubble", `klun-tooltip__bubble--${side}`), style, children: content }) : null
5408
+ open && content && typeof document !== "undefined" ? createPortal(
5409
+ /* @__PURE__ */ jsx(
5410
+ "span",
5411
+ {
5412
+ ref: bubbleRef,
5413
+ role: "tooltip",
5414
+ className: cx("klun-tooltip__bubble", `klun-tooltip__bubble--${side}`),
5415
+ style: bubbleStyle,
5416
+ children: content
5417
+ }
5418
+ ),
5419
+ document.body
5420
+ ) : null
4947
5421
  ]
4948
5422
  }
4949
5423
  );
@@ -5561,23 +6035,6 @@ var SegmentedProgress = forwardRef(
5561
6035
  }
5562
6036
  );
5563
6037
  SegmentedProgress.displayName = "SegmentedProgress";
5564
- var Skeleton = forwardRef(function Skeleton2({ width = "100%", height = 16, circle = false, className, style, ...props }, ref) {
5565
- return /* @__PURE__ */ jsx(
5566
- "span",
5567
- {
5568
- ref,
5569
- "aria-hidden": true,
5570
- className: cx("klun-skeleton", circle && "klun-skeleton--circle", className),
5571
- style: {
5572
- ["--klun-skeleton-width"]: typeof width === "number" ? `${width}px` : width,
5573
- ["--klun-skeleton-height"]: typeof height === "number" ? `${height}px` : height,
5574
- ...style
5575
- },
5576
- ...props
5577
- }
5578
- );
5579
- });
5580
- Skeleton.displayName = "Skeleton";
5581
6038
  var Toast = forwardRef(function Toast2({ status = "success", icon, title, action, onClose, rich = false, className, children, ...props }, ref) {
5582
6039
  const { toast: toast2 } = useLocale();
5583
6040
  return /* @__PURE__ */ jsxs(
@@ -6465,7 +6922,10 @@ function useSelect({ children, value, defaultValue, disabled, rootRef }) {
6465
6922
  useEffect(() => {
6466
6923
  if (!open) return;
6467
6924
  const onDown = (e) => {
6468
- if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);
6925
+ const t = e.target;
6926
+ if (rootRef.current && rootRef.current.contains(t)) return;
6927
+ if (t instanceof Element && t.closest(".klun-select-listbox")) return;
6928
+ setOpen(false);
6469
6929
  };
6470
6930
  document.addEventListener("mousedown", onDown);
6471
6931
  return () => document.removeEventListener("mousedown", onDown);
@@ -6577,13 +7037,58 @@ function SelectListbox({
6577
7037
  active,
6578
7038
  onActivate,
6579
7039
  onPick,
6580
- className
7040
+ className,
7041
+ anchorRef
6581
7042
  }) {
6582
- return /* @__PURE__ */ jsx(
7043
+ const portal = !!anchorRef && typeof document !== "undefined";
7044
+ const panelRef = useRef(null);
7045
+ const [pos, setPos] = useState(null);
7046
+ useLayoutEffect(() => {
7047
+ if (!portal) return;
7048
+ const anchor = anchorRef.current;
7049
+ const panel = panelRef.current;
7050
+ if (!anchor || !panel) return;
7051
+ const place2 = () => {
7052
+ const a = anchor.getBoundingClientRect();
7053
+ const gap = 6;
7054
+ const margin = 8;
7055
+ const ph = panel.offsetHeight;
7056
+ const pw = panel.offsetWidth;
7057
+ const below = window.innerHeight - a.bottom;
7058
+ const openUp = below < ph + gap && a.top > below;
7059
+ const top = openUp ? Math.max(margin, a.top - gap - ph) : Math.min(a.bottom + gap, window.innerHeight - margin - ph);
7060
+ let left = a.left;
7061
+ if (left + pw > window.innerWidth - margin) {
7062
+ left = Math.max(margin, window.innerWidth - margin - pw);
7063
+ }
7064
+ if (left < margin) left = margin;
7065
+ setPos({ left, top, minWidth: a.width });
7066
+ };
7067
+ place2();
7068
+ window.addEventListener("scroll", place2, true);
7069
+ window.addEventListener("resize", place2);
7070
+ return () => {
7071
+ window.removeEventListener("scroll", place2, true);
7072
+ window.removeEventListener("resize", place2);
7073
+ };
7074
+ }, [portal, anchorRef, options.length]);
7075
+ const initMinWidth = portal ? anchorRef.current?.offsetWidth : void 0;
7076
+ const portalStyle = portal ? {
7077
+ position: "fixed",
7078
+ left: pos?.left ?? 0,
7079
+ top: pos?.top ?? 0,
7080
+ minWidth: pos?.minWidth ?? initMinWidth,
7081
+ zIndex: 1150,
7082
+ // keep hidden until measured to avoid a flash at the corner
7083
+ visibility: pos ? "visible" : "hidden"
7084
+ } : void 0;
7085
+ const node = /* @__PURE__ */ jsx(
6583
7086
  "div",
6584
7087
  {
7088
+ ref: panelRef,
6585
7089
  role: "listbox",
6586
7090
  className: className ? `klun-select-listbox ${className}` : "klun-select-listbox",
7091
+ style: portalStyle,
6587
7092
  children: options.map((o, i) => /* @__PURE__ */ jsxs(
6588
7093
  "button",
6589
7094
  {
@@ -6624,6 +7129,7 @@ function SelectListbox({
6624
7129
  ))
6625
7130
  }
6626
7131
  );
7132
+ return portal ? createPortal(node, document.body) : node;
6627
7133
  }
6628
7134
  var CompactSelect = forwardRef(
6629
7135
  function CompactSelect2({
@@ -6716,12 +7222,12 @@ var CompactSelect = forwardRef(
6716
7222
  s.open ? /* @__PURE__ */ jsx(
6717
7223
  SelectListbox,
6718
7224
  {
7225
+ anchorRef: s.triggerRef,
6719
7226
  options: s.options,
6720
7227
  current: s.current,
6721
7228
  active: s.active,
6722
7229
  onActivate: s.setActive,
6723
- onPick: s.pick,
6724
- className: s.placement === "up" ? "klun-select-listbox--up" : void 0
7230
+ onPick: s.pick
6725
7231
  }
6726
7232
  ) : null
6727
7233
  ]
@@ -6813,12 +7319,12 @@ var CompactSelectForInput = forwardRef(function CompactSelectForInput2({
6813
7319
  s.open ? /* @__PURE__ */ jsx(
6814
7320
  SelectListbox,
6815
7321
  {
7322
+ anchorRef: s.triggerRef,
6816
7323
  options: s.options,
6817
7324
  current: s.current,
6818
7325
  active: s.active,
6819
7326
  onActivate: s.setActive,
6820
- onPick: s.pick,
6821
- className: s.placement === "up" ? "klun-select-listbox--up" : void 0
7327
+ onPick: s.pick
6822
7328
  }
6823
7329
  ) : null
6824
7330
  ]
@@ -6980,31 +7486,83 @@ var Input = forwardRef(function Input2({
6980
7486
  Input.displayName = "Input";
6981
7487
  var Popover = forwardRef(function Popover2({ trigger, children, align = "left", width = 280, className, ...props }, ref) {
6982
7488
  const [open, setOpen] = useState(false);
6983
- const innerRef = useRef(null);
7489
+ const wrapRef = useRef(null);
7490
+ const panelRef = useRef(null);
7491
+ const portal = typeof document !== "undefined";
7492
+ const [pos, setPos] = useState(null);
6984
7493
  useEffect(() => {
6985
7494
  if (!open) return;
6986
7495
  const h = (e) => {
6987
- if (innerRef.current && !innerRef.current.contains(e.target)) setOpen(false);
7496
+ const t = e.target;
7497
+ if (wrapRef.current?.contains(t)) return;
7498
+ if (panelRef.current?.contains(t)) return;
7499
+ setOpen(false);
6988
7500
  };
6989
7501
  document.addEventListener("mousedown", h);
6990
7502
  return () => document.removeEventListener("mousedown", h);
6991
7503
  }, [open]);
7504
+ useLayoutEffect(() => {
7505
+ if (!open || !portal) return;
7506
+ const anchor = wrapRef.current;
7507
+ const panel2 = panelRef.current;
7508
+ if (!anchor || !panel2) return;
7509
+ const place2 = () => {
7510
+ const a = anchor.getBoundingClientRect();
7511
+ const gap = 8;
7512
+ const margin = 8;
7513
+ const pw = panel2.offsetWidth || width;
7514
+ const ph = panel2.offsetHeight;
7515
+ const below = window.innerHeight - a.bottom;
7516
+ const openUp = below < ph + gap && a.top > below;
7517
+ const top = openUp ? Math.max(margin, a.top - gap - ph) : Math.min(a.bottom + gap, window.innerHeight - margin - ph);
7518
+ let left = align === "right" ? a.right - pw : a.left;
7519
+ if (left + pw > window.innerWidth - margin) left = window.innerWidth - margin - pw;
7520
+ if (left < margin) left = margin;
7521
+ setPos({ left, top });
7522
+ };
7523
+ place2();
7524
+ window.addEventListener("scroll", place2, true);
7525
+ window.addEventListener("resize", place2);
7526
+ return () => {
7527
+ window.removeEventListener("scroll", place2, true);
7528
+ window.removeEventListener("resize", place2);
7529
+ };
7530
+ }, [open, portal, align, width]);
7531
+ useEffect(() => {
7532
+ if (!open) setPos(null);
7533
+ }, [open]);
6992
7534
  const setRefs = (node) => {
6993
- innerRef.current = node;
7535
+ wrapRef.current = node;
6994
7536
  if (typeof ref === "function") ref(node);
6995
7537
  else if (ref) ref.current = node;
6996
7538
  };
7539
+ const content = typeof children === "function" ? children(() => setOpen(false)) : children;
7540
+ const portalStyle = {
7541
+ position: "fixed",
7542
+ left: pos?.left ?? 0,
7543
+ top: pos?.top ?? 0,
7544
+ width,
7545
+ zIndex: 1150,
7546
+ // keep hidden until measured to avoid a flash at the top-left corner
7547
+ visibility: pos ? "visible" : "hidden"
7548
+ };
7549
+ const panel = open ? /* @__PURE__ */ jsx(
7550
+ "div",
7551
+ {
7552
+ ref: panelRef,
7553
+ role: "dialog",
7554
+ className: cx(
7555
+ "klun-popover__panel",
7556
+ // in-flow fallback keeps the legacy left/right anchoring classes
7557
+ !portal && `klun-popover__panel--${align}`
7558
+ ),
7559
+ style: portal ? portalStyle : { width },
7560
+ children: content
7561
+ }
7562
+ ) : null;
6997
7563
  return /* @__PURE__ */ jsxs("span", { ref: setRefs, className: cx("klun-popover", className), ...props, children: [
6998
7564
  /* @__PURE__ */ jsx("span", { className: "klun-popover__trigger", onClick: () => setOpen((o) => !o), children: trigger }),
6999
- open ? /* @__PURE__ */ jsx(
7000
- "div",
7001
- {
7002
- role: "dialog",
7003
- className: cx("klun-popover__panel", `klun-popover__panel--${align}`),
7004
- style: { width },
7005
- children: typeof children === "function" ? children(() => setOpen(false)) : children
7006
- }
7007
- ) : null
7565
+ portal ? panel && createPortal(panel, document.body) : panel
7008
7566
  ] });
7009
7567
  });
7010
7568
  Popover.displayName = "Popover";
@@ -8114,12 +8672,12 @@ var InlineSelect = forwardRef(
8114
8672
  s.open ? /* @__PURE__ */ jsx(
8115
8673
  SelectListbox,
8116
8674
  {
8675
+ anchorRef: s.triggerRef,
8117
8676
  options: s.options,
8118
8677
  current: s.current,
8119
8678
  active: s.active,
8120
8679
  onActivate: s.setActive,
8121
- onPick: s.pick,
8122
- className: s.placement === "up" ? "klun-select-listbox--up" : void 0
8680
+ onPick: s.pick
8123
8681
  }
8124
8682
  ) : null
8125
8683
  ]
@@ -8461,12 +9019,12 @@ var Select = forwardRef(function Select2({
8461
9019
  s.open ? /* @__PURE__ */ jsx(
8462
9020
  SelectListbox,
8463
9021
  {
9022
+ anchorRef: s.triggerRef,
8464
9023
  options: s.options,
8465
9024
  current: s.current,
8466
9025
  active: s.active,
8467
9026
  onActivate: s.setActive,
8468
- onPick: s.pick,
8469
- className: s.placement === "up" ? "klun-select-listbox--up" : void 0
9027
+ onPick: s.pick
8470
9028
  }
8471
9029
  ) : null
8472
9030
  ] });
@@ -9106,9 +9664,9 @@ function Masonry({
9106
9664
  );
9107
9665
  }
9108
9666
  Masonry.displayName = "Masonry";
9109
- var GAP = { none: 0, xs: 4, sm: 8, md: 12, lg: 16, xl: 24, "2xl": 32 };
9667
+ var GAP2 = { none: 0, xs: 4, sm: 8, md: 12, lg: 16, xl: 24, "2xl": 32 };
9110
9668
  function resolveGap(g) {
9111
- return typeof g === "number" ? g : GAP[g] ?? GAP.md;
9669
+ return typeof g === "number" ? g : GAP2[g] ?? GAP2.md;
9112
9670
  }
9113
9671
  var Space = forwardRef(function Space2({ direction = "row", size = "md", align, justify, wrap = true, split, children, className, style, ...props }, ref) {
9114
9672
  const gap = resolveGap(size);
@@ -9475,6 +10033,108 @@ var Breadcrumb = forwardRef(function Breadcrumb2({ items: items2 = [], className
9475
10033
  }) });
9476
10034
  });
9477
10035
  Breadcrumb.displayName = "Breadcrumb";
10036
+ var Tabs = forwardRef(function Tabs2({ items: items2 = [], value, onChange, variant = "underline", className, ...props }, ref) {
10037
+ const active = value ?? items2[0]?.value;
10038
+ return /* @__PURE__ */ jsx(
10039
+ "div",
10040
+ {
10041
+ ref,
10042
+ role: "tablist",
10043
+ className: cx("klun-tabs", `klun-tabs--${variant}`, className),
10044
+ ...props,
10045
+ children: items2.map((it) => {
10046
+ const on = it.value === active;
10047
+ return /* @__PURE__ */ jsxs(
10048
+ "button",
10049
+ {
10050
+ type: "button",
10051
+ role: "tab",
10052
+ "aria-selected": on,
10053
+ "data-active": on ? "true" : void 0,
10054
+ onClick: () => onChange?.(it.value),
10055
+ className: "klun-tabs__tab",
10056
+ children: [
10057
+ it.icon ? /* @__PURE__ */ jsx("span", { className: "klun-tabs__icon", children: it.icon }) : null,
10058
+ it.label,
10059
+ it.badge != null ? /* @__PURE__ */ jsx("span", { className: "klun-tabs__badge", children: it.badge }) : null
10060
+ ]
10061
+ },
10062
+ it.value
10063
+ );
10064
+ })
10065
+ }
10066
+ );
10067
+ });
10068
+ Tabs.displayName = "Tabs";
10069
+ var PageHeader = forwardRef(function PageHeader2({
10070
+ title,
10071
+ avatar,
10072
+ tags,
10073
+ subtitle,
10074
+ breadcrumb,
10075
+ onBack,
10076
+ extra,
10077
+ tabs,
10078
+ activeTab,
10079
+ onTabChange,
10080
+ sticky = false,
10081
+ bordered = true,
10082
+ className,
10083
+ children,
10084
+ ...props
10085
+ }, ref) {
10086
+ return /* @__PURE__ */ jsxs(
10087
+ "header",
10088
+ {
10089
+ ref,
10090
+ className: cx(
10091
+ "klun-page-header",
10092
+ sticky && "klun-page-header--sticky",
10093
+ bordered && "klun-page-header--bordered",
10094
+ className
10095
+ ),
10096
+ ...props,
10097
+ children: [
10098
+ breadcrumb ? /* @__PURE__ */ jsx("div", { className: "klun-page-header__breadcrumb", children: breadcrumb }) : null,
10099
+ /* @__PURE__ */ jsxs("div", { className: "klun-page-header__bar", children: [
10100
+ onBack ? /* @__PURE__ */ jsx(
10101
+ Button,
10102
+ {
10103
+ iconOnly: true,
10104
+ appearance: "ghost",
10105
+ variant: "neutral",
10106
+ size: "small",
10107
+ "aria-label": "Back",
10108
+ className: "klun-page-header__back",
10109
+ icon: /* @__PURE__ */ jsx("i", { className: "ri-arrow-left-line", "aria-hidden": true }),
10110
+ onClick: onBack
10111
+ }
10112
+ ) : null,
10113
+ avatar ? /* @__PURE__ */ jsx("span", { className: "klun-page-header__avatar", children: avatar }) : null,
10114
+ /* @__PURE__ */ jsxs("div", { className: "klun-page-header__heading", children: [
10115
+ /* @__PURE__ */ jsxs("div", { className: "klun-page-header__title-row", children: [
10116
+ /* @__PURE__ */ jsx("h1", { className: "klun-page-header__title", children: title }),
10117
+ tags ? /* @__PURE__ */ jsx("span", { className: "klun-page-header__tags", children: tags }) : null
10118
+ ] }),
10119
+ subtitle ? /* @__PURE__ */ jsx("div", { className: "klun-page-header__subtitle", children: subtitle }) : null
10120
+ ] }),
10121
+ extra ? /* @__PURE__ */ jsx("div", { className: "klun-page-header__extra", children: extra }) : null
10122
+ ] }),
10123
+ children ? /* @__PURE__ */ jsx("div", { className: "klun-page-header__content", children }) : null,
10124
+ tabs && tabs.length ? /* @__PURE__ */ jsx(
10125
+ Tabs,
10126
+ {
10127
+ className: "klun-page-header__tabs",
10128
+ items: tabs.map((t) => ({ value: t.key, label: t.label })),
10129
+ value: activeTab,
10130
+ onChange: onTabChange
10131
+ }
10132
+ ) : null
10133
+ ]
10134
+ }
10135
+ );
10136
+ });
10137
+ PageHeader.displayName = "PageHeader";
9478
10138
  var Divider = forwardRef(function Divider2({ variant = "line", className, children, ...props }, ref) {
9479
10139
  if (variant !== "line" && children != null) {
9480
10140
  return /* @__PURE__ */ jsxs(
@@ -9716,39 +10376,6 @@ var StepIndicator = forwardRef(function StepIndicator2({ steps = [], current = 0
9716
10376
  }) });
9717
10377
  });
9718
10378
  StepIndicator.displayName = "StepIndicator";
9719
- var Tabs = forwardRef(function Tabs2({ items: items2 = [], value, onChange, variant = "underline", className, ...props }, ref) {
9720
- const active = value ?? items2[0]?.value;
9721
- return /* @__PURE__ */ jsx(
9722
- "div",
9723
- {
9724
- ref,
9725
- role: "tablist",
9726
- className: cx("klun-tabs", `klun-tabs--${variant}`, className),
9727
- ...props,
9728
- children: items2.map((it) => {
9729
- const on = it.value === active;
9730
- return /* @__PURE__ */ jsxs(
9731
- "button",
9732
- {
9733
- type: "button",
9734
- role: "tab",
9735
- "aria-selected": on,
9736
- "data-active": on ? "true" : void 0,
9737
- onClick: () => onChange?.(it.value),
9738
- className: "klun-tabs__tab",
9739
- children: [
9740
- it.icon ? /* @__PURE__ */ jsx("span", { className: "klun-tabs__icon", children: it.icon }) : null,
9741
- it.label,
9742
- it.badge != null ? /* @__PURE__ */ jsx("span", { className: "klun-tabs__badge", children: it.badge }) : null
9743
- ]
9744
- },
9745
- it.value
9746
- );
9747
- })
9748
- }
9749
- );
9750
- });
9751
- Tabs.displayName = "Tabs";
9752
10379
  var Wizard = forwardRef(function Wizard2({
9753
10380
  steps = [],
9754
10381
  current,
@@ -10045,7 +10672,7 @@ function Confirm(opts = {}) {
10045
10672
  }
10046
10673
  var isSelectable = (it) => !it.divider && !it.disabled;
10047
10674
  var hasChildren = (it) => !!it.children && it.children.length > 0;
10048
- var GAP2 = 8;
10675
+ var GAP3 = 8;
10049
10676
  function place(w, h, opts) {
10050
10677
  const vw = window.innerWidth;
10051
10678
  const vh = window.innerHeight;
@@ -10053,15 +10680,15 @@ function place(w, h, opts) {
10053
10680
  let top;
10054
10681
  if (opts.rect) {
10055
10682
  const r = opts.rect;
10056
- left = r.right - 4 + w > vw - GAP2 ? r.left - w + 4 : r.right - 4;
10683
+ left = r.right - 4 + w > vw - GAP3 ? r.left - w + 4 : r.right - 4;
10057
10684
  top = r.top - 4;
10058
10685
  } else {
10059
10686
  const p = opts.point ?? { x: 0, y: 0 };
10060
- left = p.x + w > vw - GAP2 ? p.x - w : p.x;
10061
- top = p.y + h > vh - GAP2 ? p.y - h : p.y;
10687
+ left = p.x + w > vw - GAP3 ? p.x - w : p.x;
10688
+ top = p.y + h > vh - GAP3 ? p.y - h : p.y;
10062
10689
  }
10063
- left = Math.min(Math.max(GAP2, left), vw - w - GAP2);
10064
- top = Math.min(Math.max(GAP2, top), vh - h - GAP2);
10690
+ left = Math.min(Math.max(GAP3, left), vw - w - GAP3);
10691
+ top = Math.min(Math.max(GAP3, top), vh - h - GAP3);
10065
10692
  return { left, top };
10066
10693
  }
10067
10694
  function MenuPanel({ items: items2, width, className, point, anchorEl, autoActive, depth, onCloseAll, onCloseSelf }) {
@@ -10661,8 +11288,8 @@ var Modal = forwardRef(function Modal2({
10661
11288
  );
10662
11289
  });
10663
11290
  Modal.displayName = "Modal";
10664
- var GAP3 = 8;
10665
- var MARGIN = 8;
11291
+ var GAP4 = 8;
11292
+ var MARGIN2 = 8;
10666
11293
  var Popconfirm = forwardRef(function Popconfirm2({
10667
11294
  title: titleProp,
10668
11295
  description,
@@ -10723,23 +11350,23 @@ var Popconfirm = forwardRef(function Popconfirm2({
10723
11350
  let left;
10724
11351
  switch (placement) {
10725
11352
  case "bottom":
10726
- top = tr.bottom + GAP3;
11353
+ top = tr.bottom + GAP4;
10727
11354
  left = tr.left + tr.width / 2 - bw / 2;
10728
11355
  break;
10729
11356
  case "left":
10730
- left = tr.left - bw - GAP3;
11357
+ left = tr.left - bw - GAP4;
10731
11358
  top = tr.top + tr.height / 2 - bh / 2;
10732
11359
  break;
10733
11360
  case "right":
10734
- left = tr.right + GAP3;
11361
+ left = tr.right + GAP4;
10735
11362
  top = tr.top + tr.height / 2 - bh / 2;
10736
11363
  break;
10737
11364
  default:
10738
- top = tr.top - bh - GAP3;
11365
+ top = tr.top - bh - GAP4;
10739
11366
  left = tr.left + tr.width / 2 - bw / 2;
10740
11367
  }
10741
- left = Math.max(MARGIN, Math.min(left, vw - bw - MARGIN));
10742
- top = Math.max(MARGIN, Math.min(top, vh - bh - MARGIN));
11368
+ left = Math.max(MARGIN2, Math.min(left, vw - bw - MARGIN2));
11369
+ top = Math.max(MARGIN2, Math.min(top, vh - bh - MARGIN2));
10743
11370
  setPos({ top, left });
10744
11371
  };
10745
11372
  place2();
@@ -10835,6 +11462,6 @@ var Popconfirm = forwardRef(function Popconfirm2({
10835
11462
  });
10836
11463
  Popconfirm.displayName = "Popconfirm";
10837
11464
 
10838
- export { Accordion, Alert, AutoComplete, Avatar, AvatarGroup, Badge, Banner, Breadcrumb, BulkAction, BulkActionBar, Button, ButtonGroup, Card, Carousel, Cascader, CharacterCounter, ChartLegend, ChartTooltip, Checkbox, CheckboxCard, CheckboxGroup, Chip, CircularProgress, CodeViewer, Col, Collapse, ColorDot, ColorPicker, ColorSlider, CommandMenu, CompactSelect, CompactSelectForInput, ConfigProvider, Confirm, ContentLabel, ContextMenu2 as ContextMenu, CopyButton, CounterInput, DatePicker, DateRangePicker, DateTimePicker, Descriptions, DigitInput, Divider, Drawer, Dropdown, ExpiryCountdown, FileUpload, Flex, Form, FormItem, FormList, Format, GaugeBar, Grid, Hint, HorizontalFilter, ImageUpload, ImageViewer, InlineInput, InlineSelect, Input, Kbd, KeyIcon, Label, Layout, LayoutContent, LayoutFooter, LayoutHeader, LayoutSider, List, ListItem, LiveDot, LogViewer, Markdown, Masonry, Menu, Message, Modal, Money, Notification, Pagination, PasswordStrength, Popconfirm, Popover, ProgressBar, Radio, RadioCard, RangeSlider, Rating, RelativeTime, RichEditorToolbar, Row, SegmentedControl, SegmentedProgress, Select, SelectMenu, Skeleton, Slider, Space, Spinner2 as Spinner, Splitter2 as Splitter, SplitterPanel, Statistic, StatusBadge, StatusDot, StepIndicator, Switch, Table, Tabs, Tag, Textarea, TimePicker, Timeline, Toast, Toaster, Tooltip, Tree, Wizard, arSA, deDE, enUS, esES, fmt, frFR, idID, itIT, jaJP, koKR, locales, nlNL, plPL, primaryColorVars, ptBR, renderMarkdown, ruRU, toast, trTR, useBreakpoint, useConfig, useContextMenu, useForm, useLocale, useMappedSize, useMessage, useNotification, useSize, viVN, zhCN, zhTW };
10839
- //# sourceMappingURL=chunk-VATUPFE3.js.map
10840
- //# sourceMappingURL=chunk-VATUPFE3.js.map
11465
+ export { Accordion, Alert, AutoComplete, Avatar, AvatarGroup, Badge, Banner, Breadcrumb, BulkAction, BulkActionBar, Button, ButtonGroup, Card, Carousel, Cascader, CharacterCounter, ChartLegend, ChartTooltip, Checkbox, CheckboxCard, CheckboxGroup, Chip, CircularProgress, CodeViewer, Col, Collapse, ColorDot, ColorPicker, ColorSlider, CommandMenu, CompactSelect, CompactSelectForInput, ConfigProvider, Confirm, ContentLabel, ContextMenu2 as ContextMenu, CopyButton, CounterInput, CrossRefBadge, DatePicker, DateRangePicker, DateTimePicker, Descriptions, DiffViewer, DigitInput, Divider, Drawer, Dropdown, ExpiryCountdown, FileUpload, Flex, Form, FormItem, FormList, Format, GaugeBar, Grid, Hint, HorizontalFilter, ImageUpload, ImageViewer, InlineInput, InlineSelect, Input, Kbd, KeyIcon, Label, Layout, LayoutContent, LayoutFooter, LayoutHeader, LayoutSider, List, ListItem, LiveDot, LogViewer, Markdown, Masonry, Menu, Message, Modal, Money, Notification, PageHeader, Pagination, PasswordStrength, Popconfirm, Popover, ProgressBar, Radio, RadioCard, RangeSlider, Rating, RelativeTime, RichEditorToolbar, Row, SegmentedControl, SegmentedProgress, Select, SelectMenu, Skeleton, Slider, Space, Spinner2 as Spinner, Splitter2 as Splitter, SplitterPanel, Statistic, StatusBadge, StatusDot, StepIndicator, Switch, Table, Tabs, Tag, Textarea, TimePicker, Timeline, Toast, Toaster, Tooltip, Tree, Wizard, arSA, deDE, enUS, esES, fmt, frFR, idID, itIT, jaJP, koKR, locales, nlNL, parsePatch, plPL, primaryColorVars, ptBR, renderMarkdown, ruRU, toast, trTR, useBreakpoint, useConfig, useContextMenu, useForm, useLocale, useMappedSize, useMessage, useNotification, useSize, viVN, zhCN, zhTW };
11466
+ //# sourceMappingURL=chunk-NYIPE2T7.js.map
11467
+ //# sourceMappingURL=chunk-NYIPE2T7.js.map