klun-ui 0.1.16 → 0.1.20

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,69 +3673,6 @@ 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",
@@ -3197,112 +3700,30 @@ var Statistic = forwardRef(function Statistic2({ label, value, prefix, suffix, p
3197
3700
  ] });
3198
3701
  });
3199
3702
  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;
3703
+ var StatCard = forwardRef(function StatCard2({ label, value, delta, trend = "up", icon, tint = "feature", className, ...props }, ref) {
3704
+ const up = trend === "up";
3705
+ return /* @__PURE__ */ jsxs("div", { ref, className: cx("klun-stat-card", `klun-stat-card--${tint}`, className), ...props, children: [
3706
+ /* @__PURE__ */ jsxs("div", { className: "klun-stat-card__head", children: [
3707
+ /* @__PURE__ */ jsx("span", { className: "klun-stat-card__icon", children: icon }),
3708
+ delta != null ? /* @__PURE__ */ jsxs(
3709
+ "span",
3710
+ {
3711
+ className: cx(
3712
+ "klun-stat-card__delta",
3713
+ up ? "klun-stat-card__delta--up" : "klun-stat-card__delta--down"
3714
+ ),
3715
+ children: [
3716
+ /* @__PURE__ */ jsx("i", { className: up ? "ri-arrow-up-line" : "ri-arrow-down-line" }),
3717
+ delta
3718
+ ]
3719
+ }
3720
+ ) : null
3721
+ ] }),
3722
+ /* @__PURE__ */ jsx("div", { className: "klun-stat-card__label", children: label }),
3723
+ /* @__PURE__ */ jsx("div", { className: "klun-stat-card__value", children: value })
3724
+ ] });
3725
+ });
3726
+ StatCard.displayName = "StatCard";
3306
3727
  var SIZES2 = { xs: 6, sm: 8, md: 10, lg: 12 };
3307
3728
  var StatusDot = forwardRef(function StatusDot2({ tone = "neutral", size = "sm", pulse = false, label, className, style, ...props }, ref) {
3308
3729
  const sizeStyle = { ["--klun-status-dot-size"]: `${SIZES2[size] ?? SIZES2.sm}px` };
@@ -4917,9 +5338,14 @@ var Timeline = forwardRef(function Timeline2({ items: items2 = [], mode = "left"
4917
5338
  }) });
4918
5339
  });
4919
5340
  Timeline.displayName = "Timeline";
5341
+ var GAP = 6;
5342
+ var MARGIN = 8;
4920
5343
  var Tooltip = forwardRef(function Tooltip2({ content, side = "top", delay = 200, disabled = false, children, style, className }, ref) {
4921
5344
  const [open, setOpen] = useState(false);
5345
+ const [pos, setPos] = useState(null);
4922
5346
  const timer = useRef(null);
5347
+ const innerRef = useRef(null);
5348
+ const bubbleRef = useRef(null);
4923
5349
  const show = () => {
4924
5350
  if (disabled || !content) return;
4925
5351
  if (timer.current) clearTimeout(timer.current);
@@ -4932,10 +5358,70 @@ var Tooltip = forwardRef(function Tooltip2({ content, side = "top", delay = 200,
4932
5358
  useEffect(() => () => {
4933
5359
  if (timer.current) clearTimeout(timer.current);
4934
5360
  }, []);
5361
+ useLayoutEffect(() => {
5362
+ if (!open) {
5363
+ setPos(null);
5364
+ return;
5365
+ }
5366
+ const place2 = () => {
5367
+ const trigger = innerRef.current;
5368
+ const bubble = bubbleRef.current;
5369
+ if (!trigger || !bubble) return;
5370
+ const tr = trigger.getBoundingClientRect();
5371
+ const bw = bubble.offsetWidth;
5372
+ const bh = bubble.offsetHeight;
5373
+ const vw = document.documentElement.clientWidth;
5374
+ const vh = document.documentElement.clientHeight;
5375
+ let top;
5376
+ let left;
5377
+ switch (side) {
5378
+ case "bottom":
5379
+ top = tr.bottom + GAP;
5380
+ left = tr.left + tr.width / 2 - bw / 2;
5381
+ break;
5382
+ case "left":
5383
+ left = tr.left - bw - GAP;
5384
+ top = tr.top + tr.height / 2 - bh / 2;
5385
+ break;
5386
+ case "right":
5387
+ left = tr.right + GAP;
5388
+ top = tr.top + tr.height / 2 - bh / 2;
5389
+ break;
5390
+ default:
5391
+ top = tr.top - bh - GAP;
5392
+ left = tr.left + tr.width / 2 - bw / 2;
5393
+ }
5394
+ left = Math.max(MARGIN, Math.min(left, vw - bw - MARGIN));
5395
+ top = Math.max(MARGIN, Math.min(top, vh - bh - MARGIN));
5396
+ setPos({ top, left });
5397
+ };
5398
+ place2();
5399
+ window.addEventListener("scroll", place2, true);
5400
+ window.addEventListener("resize", place2);
5401
+ return () => {
5402
+ window.removeEventListener("scroll", place2, true);
5403
+ window.removeEventListener("resize", place2);
5404
+ };
5405
+ }, [open, side, content]);
5406
+ const setRefs = (node) => {
5407
+ innerRef.current = node;
5408
+ if (typeof ref === "function") ref(node);
5409
+ else if (ref) ref.current = node;
5410
+ };
5411
+ const bubbleStyle = {
5412
+ ...style,
5413
+ position: "fixed",
5414
+ top: pos ? pos.top : 0,
5415
+ left: pos ? pos.left : 0,
5416
+ right: "auto",
5417
+ bottom: "auto",
5418
+ transform: "none",
5419
+ visibility: pos ? "visible" : "hidden"
5420
+ };
4935
5421
  return /* @__PURE__ */ jsxs(
4936
5422
  "span",
4937
5423
  {
4938
- ref,
5424
+ ref: setRefs,
4939
5425
  className: cx("klun-tooltip", className),
4940
5426
  onMouseEnter: show,
4941
5427
  onMouseLeave: hide,
@@ -4943,7 +5429,19 @@ var Tooltip = forwardRef(function Tooltip2({ content, side = "top", delay = 200,
4943
5429
  onBlur: hide,
4944
5430
  children: [
4945
5431
  children,
4946
- open ? /* @__PURE__ */ jsx("span", { role: "tooltip", className: cx("klun-tooltip__bubble", `klun-tooltip__bubble--${side}`), style, children: content }) : null
5432
+ open && content && typeof document !== "undefined" ? createPortal(
5433
+ /* @__PURE__ */ jsx(
5434
+ "span",
5435
+ {
5436
+ ref: bubbleRef,
5437
+ role: "tooltip",
5438
+ className: cx("klun-tooltip__bubble", `klun-tooltip__bubble--${side}`),
5439
+ style: bubbleStyle,
5440
+ children: content
5441
+ }
5442
+ ),
5443
+ document.body
5444
+ ) : null
4947
5445
  ]
4948
5446
  }
4949
5447
  );
@@ -5189,6 +5687,43 @@ var Alert = forwardRef(function Alert2({ status = "information", variant = "ligh
5189
5687
  );
5190
5688
  });
5191
5689
  Alert.displayName = "Alert";
5690
+ var EmptyState = forwardRef(function EmptyState2({ icon, title, description, action, className, ...props }, ref) {
5691
+ return /* @__PURE__ */ jsxs("div", { ref, className: cx("klun-empty-state", className), ...props, children: [
5692
+ icon ? /* @__PURE__ */ jsx("div", { className: "klun-empty-state__icon", children: icon }) : null,
5693
+ title ? /* @__PURE__ */ jsx("div", { className: "klun-empty-state__title", children: title }) : null,
5694
+ description ? /* @__PURE__ */ jsx("div", { className: "klun-empty-state__desc", children: description }) : null,
5695
+ action ? /* @__PURE__ */ jsx("div", { className: "klun-empty-state__action", children: action }) : null
5696
+ ] });
5697
+ });
5698
+ EmptyState.displayName = "EmptyState";
5699
+ var PRESET_ICON = {
5700
+ success: "ri-checkbox-circle-fill",
5701
+ error: "ri-close-circle-fill",
5702
+ warning: "ri-alert-fill",
5703
+ info: "ri-information-fill",
5704
+ "404": "ri-compass-3-line",
5705
+ "403": "ri-lock-2-line",
5706
+ "500": "ri-server-line"
5707
+ };
5708
+ var Result = forwardRef(function Result2({ status = "info", icon, title, subtitle, extra, children, className, ...props }, ref) {
5709
+ const big = status === "404" || status === "403" || status === "500";
5710
+ return /* @__PURE__ */ jsxs(
5711
+ "div",
5712
+ {
5713
+ ref,
5714
+ className: cx("klun-result", `klun-result--${status}`, big && "klun-result--big", className),
5715
+ ...props,
5716
+ children: [
5717
+ /* @__PURE__ */ jsx("div", { className: "klun-result__icon", children: icon || /* @__PURE__ */ jsx("i", { className: PRESET_ICON[status] }) }),
5718
+ title ? /* @__PURE__ */ jsx("div", { className: "klun-result__title", children: title }) : null,
5719
+ subtitle ? /* @__PURE__ */ jsx("div", { className: "klun-result__subtitle", children: subtitle }) : null,
5720
+ extra ? /* @__PURE__ */ jsx("div", { className: "klun-result__extra", children: extra }) : null,
5721
+ children ? /* @__PURE__ */ jsx("div", { className: "klun-result__content", children }) : null
5722
+ ]
5723
+ }
5724
+ );
5725
+ });
5726
+ Result.displayName = "Result";
5192
5727
  var Banner = forwardRef(function Banner2({ status = "information", variant = "filled", icon, action, onClose, className, children, ...props }, ref) {
5193
5728
  const { banner } = useLocale();
5194
5729
  return /* @__PURE__ */ jsxs(
@@ -5561,23 +6096,6 @@ var SegmentedProgress = forwardRef(
5561
6096
  }
5562
6097
  );
5563
6098
  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
6099
  var Toast = forwardRef(function Toast2({ status = "success", icon, title, action, onClose, rich = false, className, children, ...props }, ref) {
5582
6100
  const { toast: toast2 } = useLocale();
5583
6101
  return /* @__PURE__ */ jsxs(
@@ -6465,7 +6983,10 @@ function useSelect({ children, value, defaultValue, disabled, rootRef }) {
6465
6983
  useEffect(() => {
6466
6984
  if (!open) return;
6467
6985
  const onDown = (e) => {
6468
- if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);
6986
+ const t = e.target;
6987
+ if (rootRef.current && rootRef.current.contains(t)) return;
6988
+ if (t instanceof Element && t.closest(".klun-select-listbox")) return;
6989
+ setOpen(false);
6469
6990
  };
6470
6991
  document.addEventListener("mousedown", onDown);
6471
6992
  return () => document.removeEventListener("mousedown", onDown);
@@ -6577,13 +7098,58 @@ function SelectListbox({
6577
7098
  active,
6578
7099
  onActivate,
6579
7100
  onPick,
6580
- className
7101
+ className,
7102
+ anchorRef
6581
7103
  }) {
6582
- return /* @__PURE__ */ jsx(
7104
+ const portal = !!anchorRef && typeof document !== "undefined";
7105
+ const panelRef = useRef(null);
7106
+ const [pos, setPos] = useState(null);
7107
+ useLayoutEffect(() => {
7108
+ if (!portal) return;
7109
+ const anchor = anchorRef.current;
7110
+ const panel = panelRef.current;
7111
+ if (!anchor || !panel) return;
7112
+ const place2 = () => {
7113
+ const a = anchor.getBoundingClientRect();
7114
+ const gap = 6;
7115
+ const margin = 8;
7116
+ const ph = panel.offsetHeight;
7117
+ const pw = panel.offsetWidth;
7118
+ const below = window.innerHeight - a.bottom;
7119
+ const openUp = below < ph + gap && a.top > below;
7120
+ const top = openUp ? Math.max(margin, a.top - gap - ph) : Math.min(a.bottom + gap, window.innerHeight - margin - ph);
7121
+ let left = a.left;
7122
+ if (left + pw > window.innerWidth - margin) {
7123
+ left = Math.max(margin, window.innerWidth - margin - pw);
7124
+ }
7125
+ if (left < margin) left = margin;
7126
+ setPos({ left, top, minWidth: a.width });
7127
+ };
7128
+ place2();
7129
+ window.addEventListener("scroll", place2, true);
7130
+ window.addEventListener("resize", place2);
7131
+ return () => {
7132
+ window.removeEventListener("scroll", place2, true);
7133
+ window.removeEventListener("resize", place2);
7134
+ };
7135
+ }, [portal, anchorRef, options.length]);
7136
+ const initMinWidth = portal ? anchorRef.current?.offsetWidth : void 0;
7137
+ const portalStyle = portal ? {
7138
+ position: "fixed",
7139
+ left: pos?.left ?? 0,
7140
+ top: pos?.top ?? 0,
7141
+ minWidth: pos?.minWidth ?? initMinWidth,
7142
+ zIndex: 1150,
7143
+ // keep hidden until measured to avoid a flash at the corner
7144
+ visibility: pos ? "visible" : "hidden"
7145
+ } : void 0;
7146
+ const node = /* @__PURE__ */ jsx(
6583
7147
  "div",
6584
7148
  {
7149
+ ref: panelRef,
6585
7150
  role: "listbox",
6586
7151
  className: className ? `klun-select-listbox ${className}` : "klun-select-listbox",
7152
+ style: portalStyle,
6587
7153
  children: options.map((o, i) => /* @__PURE__ */ jsxs(
6588
7154
  "button",
6589
7155
  {
@@ -6624,6 +7190,7 @@ function SelectListbox({
6624
7190
  ))
6625
7191
  }
6626
7192
  );
7193
+ return portal ? createPortal(node, document.body) : node;
6627
7194
  }
6628
7195
  var CompactSelect = forwardRef(
6629
7196
  function CompactSelect2({
@@ -6716,12 +7283,12 @@ var CompactSelect = forwardRef(
6716
7283
  s.open ? /* @__PURE__ */ jsx(
6717
7284
  SelectListbox,
6718
7285
  {
7286
+ anchorRef: s.triggerRef,
6719
7287
  options: s.options,
6720
7288
  current: s.current,
6721
7289
  active: s.active,
6722
7290
  onActivate: s.setActive,
6723
- onPick: s.pick,
6724
- className: s.placement === "up" ? "klun-select-listbox--up" : void 0
7291
+ onPick: s.pick
6725
7292
  }
6726
7293
  ) : null
6727
7294
  ]
@@ -6813,12 +7380,12 @@ var CompactSelectForInput = forwardRef(function CompactSelectForInput2({
6813
7380
  s.open ? /* @__PURE__ */ jsx(
6814
7381
  SelectListbox,
6815
7382
  {
7383
+ anchorRef: s.triggerRef,
6816
7384
  options: s.options,
6817
7385
  current: s.current,
6818
7386
  active: s.active,
6819
7387
  onActivate: s.setActive,
6820
- onPick: s.pick,
6821
- className: s.placement === "up" ? "klun-select-listbox--up" : void 0
7388
+ onPick: s.pick
6822
7389
  }
6823
7390
  ) : null
6824
7391
  ]
@@ -6980,31 +7547,83 @@ var Input = forwardRef(function Input2({
6980
7547
  Input.displayName = "Input";
6981
7548
  var Popover = forwardRef(function Popover2({ trigger, children, align = "left", width = 280, className, ...props }, ref) {
6982
7549
  const [open, setOpen] = useState(false);
6983
- const innerRef = useRef(null);
7550
+ const wrapRef = useRef(null);
7551
+ const panelRef = useRef(null);
7552
+ const portal = typeof document !== "undefined";
7553
+ const [pos, setPos] = useState(null);
6984
7554
  useEffect(() => {
6985
7555
  if (!open) return;
6986
7556
  const h = (e) => {
6987
- if (innerRef.current && !innerRef.current.contains(e.target)) setOpen(false);
7557
+ const t = e.target;
7558
+ if (wrapRef.current?.contains(t)) return;
7559
+ if (panelRef.current?.contains(t)) return;
7560
+ setOpen(false);
6988
7561
  };
6989
7562
  document.addEventListener("mousedown", h);
6990
7563
  return () => document.removeEventListener("mousedown", h);
6991
7564
  }, [open]);
7565
+ useLayoutEffect(() => {
7566
+ if (!open || !portal) return;
7567
+ const anchor = wrapRef.current;
7568
+ const panel2 = panelRef.current;
7569
+ if (!anchor || !panel2) return;
7570
+ const place2 = () => {
7571
+ const a = anchor.getBoundingClientRect();
7572
+ const gap = 8;
7573
+ const margin = 8;
7574
+ const pw = panel2.offsetWidth || width;
7575
+ const ph = panel2.offsetHeight;
7576
+ const below = window.innerHeight - a.bottom;
7577
+ const openUp = below < ph + gap && a.top > below;
7578
+ const top = openUp ? Math.max(margin, a.top - gap - ph) : Math.min(a.bottom + gap, window.innerHeight - margin - ph);
7579
+ let left = align === "right" ? a.right - pw : a.left;
7580
+ if (left + pw > window.innerWidth - margin) left = window.innerWidth - margin - pw;
7581
+ if (left < margin) left = margin;
7582
+ setPos({ left, top });
7583
+ };
7584
+ place2();
7585
+ window.addEventListener("scroll", place2, true);
7586
+ window.addEventListener("resize", place2);
7587
+ return () => {
7588
+ window.removeEventListener("scroll", place2, true);
7589
+ window.removeEventListener("resize", place2);
7590
+ };
7591
+ }, [open, portal, align, width]);
7592
+ useEffect(() => {
7593
+ if (!open) setPos(null);
7594
+ }, [open]);
6992
7595
  const setRefs = (node) => {
6993
- innerRef.current = node;
7596
+ wrapRef.current = node;
6994
7597
  if (typeof ref === "function") ref(node);
6995
7598
  else if (ref) ref.current = node;
6996
7599
  };
7600
+ const content = typeof children === "function" ? children(() => setOpen(false)) : children;
7601
+ const portalStyle = {
7602
+ position: "fixed",
7603
+ left: pos?.left ?? 0,
7604
+ top: pos?.top ?? 0,
7605
+ width,
7606
+ zIndex: 1150,
7607
+ // keep hidden until measured to avoid a flash at the top-left corner
7608
+ visibility: pos ? "visible" : "hidden"
7609
+ };
7610
+ const panel = open ? /* @__PURE__ */ jsx(
7611
+ "div",
7612
+ {
7613
+ ref: panelRef,
7614
+ role: "dialog",
7615
+ className: cx(
7616
+ "klun-popover__panel",
7617
+ // in-flow fallback keeps the legacy left/right anchoring classes
7618
+ !portal && `klun-popover__panel--${align}`
7619
+ ),
7620
+ style: portal ? portalStyle : { width },
7621
+ children: content
7622
+ }
7623
+ ) : null;
6997
7624
  return /* @__PURE__ */ jsxs("span", { ref: setRefs, className: cx("klun-popover", className), ...props, children: [
6998
7625
  /* @__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
7626
+ portal ? panel && createPortal(panel, document.body) : panel
7008
7627
  ] });
7009
7628
  });
7010
7629
  Popover.displayName = "Popover";
@@ -8114,12 +8733,12 @@ var InlineSelect = forwardRef(
8114
8733
  s.open ? /* @__PURE__ */ jsx(
8115
8734
  SelectListbox,
8116
8735
  {
8736
+ anchorRef: s.triggerRef,
8117
8737
  options: s.options,
8118
8738
  current: s.current,
8119
8739
  active: s.active,
8120
8740
  onActivate: s.setActive,
8121
- onPick: s.pick,
8122
- className: s.placement === "up" ? "klun-select-listbox--up" : void 0
8741
+ onPick: s.pick
8123
8742
  }
8124
8743
  ) : null
8125
8744
  ]
@@ -8461,12 +9080,12 @@ var Select = forwardRef(function Select2({
8461
9080
  s.open ? /* @__PURE__ */ jsx(
8462
9081
  SelectListbox,
8463
9082
  {
9083
+ anchorRef: s.triggerRef,
8464
9084
  options: s.options,
8465
9085
  current: s.current,
8466
9086
  active: s.active,
8467
9087
  onActivate: s.setActive,
8468
- onPick: s.pick,
8469
- className: s.placement === "up" ? "klun-select-listbox--up" : void 0
9088
+ onPick: s.pick
8470
9089
  }
8471
9090
  ) : null
8472
9091
  ] });
@@ -9106,9 +9725,9 @@ function Masonry({
9106
9725
  );
9107
9726
  }
9108
9727
  Masonry.displayName = "Masonry";
9109
- var GAP = { none: 0, xs: 4, sm: 8, md: 12, lg: 16, xl: 24, "2xl": 32 };
9728
+ var GAP2 = { none: 0, xs: 4, sm: 8, md: 12, lg: 16, xl: 24, "2xl": 32 };
9110
9729
  function resolveGap(g) {
9111
- return typeof g === "number" ? g : GAP[g] ?? GAP.md;
9730
+ return typeof g === "number" ? g : GAP2[g] ?? GAP2.md;
9112
9731
  }
9113
9732
  var Space = forwardRef(function Space2({ direction = "row", size = "md", align, justify, wrap = true, split, children, className, style, ...props }, ref) {
9114
9733
  const gap = resolveGap(size);
@@ -9475,6 +10094,108 @@ var Breadcrumb = forwardRef(function Breadcrumb2({ items: items2 = [], className
9475
10094
  }) });
9476
10095
  });
9477
10096
  Breadcrumb.displayName = "Breadcrumb";
10097
+ var Tabs = forwardRef(function Tabs2({ items: items2 = [], value, onChange, variant = "underline", className, ...props }, ref) {
10098
+ const active = value ?? items2[0]?.value;
10099
+ return /* @__PURE__ */ jsx(
10100
+ "div",
10101
+ {
10102
+ ref,
10103
+ role: "tablist",
10104
+ className: cx("klun-tabs", `klun-tabs--${variant}`, className),
10105
+ ...props,
10106
+ children: items2.map((it) => {
10107
+ const on = it.value === active;
10108
+ return /* @__PURE__ */ jsxs(
10109
+ "button",
10110
+ {
10111
+ type: "button",
10112
+ role: "tab",
10113
+ "aria-selected": on,
10114
+ "data-active": on ? "true" : void 0,
10115
+ onClick: () => onChange?.(it.value),
10116
+ className: "klun-tabs__tab",
10117
+ children: [
10118
+ it.icon ? /* @__PURE__ */ jsx("span", { className: "klun-tabs__icon", children: it.icon }) : null,
10119
+ it.label,
10120
+ it.badge != null ? /* @__PURE__ */ jsx("span", { className: "klun-tabs__badge", children: it.badge }) : null
10121
+ ]
10122
+ },
10123
+ it.value
10124
+ );
10125
+ })
10126
+ }
10127
+ );
10128
+ });
10129
+ Tabs.displayName = "Tabs";
10130
+ var PageHeader = forwardRef(function PageHeader2({
10131
+ title,
10132
+ avatar,
10133
+ tags,
10134
+ subtitle,
10135
+ breadcrumb,
10136
+ onBack,
10137
+ extra,
10138
+ tabs,
10139
+ activeTab,
10140
+ onTabChange,
10141
+ sticky = false,
10142
+ bordered = true,
10143
+ className,
10144
+ children,
10145
+ ...props
10146
+ }, ref) {
10147
+ return /* @__PURE__ */ jsxs(
10148
+ "header",
10149
+ {
10150
+ ref,
10151
+ className: cx(
10152
+ "klun-page-header",
10153
+ sticky && "klun-page-header--sticky",
10154
+ bordered && "klun-page-header--bordered",
10155
+ className
10156
+ ),
10157
+ ...props,
10158
+ children: [
10159
+ breadcrumb ? /* @__PURE__ */ jsx("div", { className: "klun-page-header__breadcrumb", children: breadcrumb }) : null,
10160
+ /* @__PURE__ */ jsxs("div", { className: "klun-page-header__bar", children: [
10161
+ onBack ? /* @__PURE__ */ jsx(
10162
+ Button,
10163
+ {
10164
+ iconOnly: true,
10165
+ appearance: "ghost",
10166
+ variant: "neutral",
10167
+ size: "small",
10168
+ "aria-label": "Back",
10169
+ className: "klun-page-header__back",
10170
+ icon: /* @__PURE__ */ jsx("i", { className: "ri-arrow-left-line", "aria-hidden": true }),
10171
+ onClick: onBack
10172
+ }
10173
+ ) : null,
10174
+ avatar ? /* @__PURE__ */ jsx("span", { className: "klun-page-header__avatar", children: avatar }) : null,
10175
+ /* @__PURE__ */ jsxs("div", { className: "klun-page-header__heading", children: [
10176
+ /* @__PURE__ */ jsxs("div", { className: "klun-page-header__title-row", children: [
10177
+ /* @__PURE__ */ jsx("h1", { className: "klun-page-header__title", children: title }),
10178
+ tags ? /* @__PURE__ */ jsx("span", { className: "klun-page-header__tags", children: tags }) : null
10179
+ ] }),
10180
+ subtitle ? /* @__PURE__ */ jsx("div", { className: "klun-page-header__subtitle", children: subtitle }) : null
10181
+ ] }),
10182
+ extra ? /* @__PURE__ */ jsx("div", { className: "klun-page-header__extra", children: extra }) : null
10183
+ ] }),
10184
+ children ? /* @__PURE__ */ jsx("div", { className: "klun-page-header__content", children }) : null,
10185
+ tabs && tabs.length ? /* @__PURE__ */ jsx(
10186
+ Tabs,
10187
+ {
10188
+ className: "klun-page-header__tabs",
10189
+ items: tabs.map((t) => ({ value: t.key, label: t.label })),
10190
+ value: activeTab,
10191
+ onChange: onTabChange
10192
+ }
10193
+ ) : null
10194
+ ]
10195
+ }
10196
+ );
10197
+ });
10198
+ PageHeader.displayName = "PageHeader";
9478
10199
  var Divider = forwardRef(function Divider2({ variant = "line", className, children, ...props }, ref) {
9479
10200
  if (variant !== "line" && children != null) {
9480
10201
  return /* @__PURE__ */ jsxs(
@@ -9716,39 +10437,6 @@ var StepIndicator = forwardRef(function StepIndicator2({ steps = [], current = 0
9716
10437
  }) });
9717
10438
  });
9718
10439
  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
10440
  var Wizard = forwardRef(function Wizard2({
9753
10441
  steps = [],
9754
10442
  current,
@@ -10045,7 +10733,7 @@ function Confirm(opts = {}) {
10045
10733
  }
10046
10734
  var isSelectable = (it) => !it.divider && !it.disabled;
10047
10735
  var hasChildren = (it) => !!it.children && it.children.length > 0;
10048
- var GAP2 = 8;
10736
+ var GAP3 = 8;
10049
10737
  function place(w, h, opts) {
10050
10738
  const vw = window.innerWidth;
10051
10739
  const vh = window.innerHeight;
@@ -10053,15 +10741,15 @@ function place(w, h, opts) {
10053
10741
  let top;
10054
10742
  if (opts.rect) {
10055
10743
  const r = opts.rect;
10056
- left = r.right - 4 + w > vw - GAP2 ? r.left - w + 4 : r.right - 4;
10744
+ left = r.right - 4 + w > vw - GAP3 ? r.left - w + 4 : r.right - 4;
10057
10745
  top = r.top - 4;
10058
10746
  } else {
10059
10747
  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;
10748
+ left = p.x + w > vw - GAP3 ? p.x - w : p.x;
10749
+ top = p.y + h > vh - GAP3 ? p.y - h : p.y;
10062
10750
  }
10063
- left = Math.min(Math.max(GAP2, left), vw - w - GAP2);
10064
- top = Math.min(Math.max(GAP2, top), vh - h - GAP2);
10751
+ left = Math.min(Math.max(GAP3, left), vw - w - GAP3);
10752
+ top = Math.min(Math.max(GAP3, top), vh - h - GAP3);
10065
10753
  return { left, top };
10066
10754
  }
10067
10755
  function MenuPanel({ items: items2, width, className, point, anchorEl, autoActive, depth, onCloseAll, onCloseSelf }) {
@@ -10661,8 +11349,8 @@ var Modal = forwardRef(function Modal2({
10661
11349
  );
10662
11350
  });
10663
11351
  Modal.displayName = "Modal";
10664
- var GAP3 = 8;
10665
- var MARGIN = 8;
11352
+ var GAP4 = 8;
11353
+ var MARGIN2 = 8;
10666
11354
  var Popconfirm = forwardRef(function Popconfirm2({
10667
11355
  title: titleProp,
10668
11356
  description,
@@ -10723,23 +11411,23 @@ var Popconfirm = forwardRef(function Popconfirm2({
10723
11411
  let left;
10724
11412
  switch (placement) {
10725
11413
  case "bottom":
10726
- top = tr.bottom + GAP3;
11414
+ top = tr.bottom + GAP4;
10727
11415
  left = tr.left + tr.width / 2 - bw / 2;
10728
11416
  break;
10729
11417
  case "left":
10730
- left = tr.left - bw - GAP3;
11418
+ left = tr.left - bw - GAP4;
10731
11419
  top = tr.top + tr.height / 2 - bh / 2;
10732
11420
  break;
10733
11421
  case "right":
10734
- left = tr.right + GAP3;
11422
+ left = tr.right + GAP4;
10735
11423
  top = tr.top + tr.height / 2 - bh / 2;
10736
11424
  break;
10737
11425
  default:
10738
- top = tr.top - bh - GAP3;
11426
+ top = tr.top - bh - GAP4;
10739
11427
  left = tr.left + tr.width / 2 - bw / 2;
10740
11428
  }
10741
- left = Math.max(MARGIN, Math.min(left, vw - bw - MARGIN));
10742
- top = Math.max(MARGIN, Math.min(top, vh - bh - MARGIN));
11429
+ left = Math.max(MARGIN2, Math.min(left, vw - bw - MARGIN2));
11430
+ top = Math.max(MARGIN2, Math.min(top, vh - bh - MARGIN2));
10743
11431
  setPos({ top, left });
10744
11432
  };
10745
11433
  place2();
@@ -10835,6 +11523,6 @@ var Popconfirm = forwardRef(function Popconfirm2({
10835
11523
  });
10836
11524
  Popconfirm.displayName = "Popconfirm";
10837
11525
 
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
11526
+ 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, EmptyState, 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, Result, RichEditorToolbar, Row, SegmentedControl, SegmentedProgress, Select, SelectMenu, Skeleton, Slider, Space, Spinner2 as Spinner, Splitter2 as Splitter, SplitterPanel, StatCard, 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 };
11527
+ //# sourceMappingURL=chunk-JFRI333P.js.map
11528
+ //# sourceMappingURL=chunk-JFRI333P.js.map