klun-ui 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { cx } from './chunk-T225J6LV.js';
2
- import { createContext, forwardRef, useState, useRef, useEffect, Children, useCallback, useMemo, useLayoutEffect, Fragment as Fragment$1, createElement, useContext, isValidElement, cloneElement, useSyncExternalStore } from 'react';
2
+ import { createContext, forwardRef, useState, useRef, useEffect, useMemo, Children, useCallback, useLayoutEffect, Fragment as Fragment$1, createElement, useContext, isValidElement, cloneElement, useSyncExternalStore } from 'react';
3
3
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
4
  import { useSensors, useSensor, PointerSensor, KeyboardSensor, DndContext, closestCorners, useDroppable, closestCenter } from '@dnd-kit/core';
5
5
  import { sortableKeyboardCoordinates, SortableContext, verticalListSortingStrategy, useSortable } from '@dnd-kit/sortable';
@@ -230,6 +230,8 @@ var enUS = {
230
230
  carousel: { previous: "Previous", next: "Next", goToSlide: "Go to slide {n}" },
231
231
  input: { clear: "Clear" },
232
232
  notification: { close: "Close" },
233
+ select: { placeholder: "Select" },
234
+ counterInput: { decrease: "Decrease", increase: "Increase" },
233
235
  form: {
234
236
  required: "This field is required",
235
237
  empty: "Cannot be empty",
@@ -260,7 +262,9 @@ var enUS = {
260
262
  previous: "Previous",
261
263
  next: "Next",
262
264
  pageRange: "{from}\u2013{to} of {total}",
263
- perPage: "{size} / page"
265
+ perPage: "{size} / page",
266
+ selectRow: "Select row",
267
+ selectAll: "Select all rows"
264
268
  }
265
269
  };
266
270
  var zhCN = {
@@ -324,6 +328,8 @@ var zhCN = {
324
328
  carousel: { previous: "\u4E0A\u4E00\u9875", next: "\u4E0B\u4E00\u9875", goToSlide: "\u8DF3\u8F6C\u5230\u7B2C {n} \u5F20" },
325
329
  input: { clear: "\u6E05\u9664" },
326
330
  notification: { close: "\u5173\u95ED" },
331
+ select: { placeholder: "\u8BF7\u9009\u62E9" },
332
+ counterInput: { decrease: "\u51CF\u5C11", increase: "\u589E\u52A0" },
327
333
  form: {
328
334
  required: "\u6B64\u5B57\u6BB5\u4E3A\u5FC5\u586B\u9879",
329
335
  empty: "\u4E0D\u80FD\u4E3A\u7A7A",
@@ -354,7 +360,9 @@ var zhCN = {
354
360
  previous: "\u4E0A\u4E00\u9875",
355
361
  next: "\u4E0B\u4E00\u9875",
356
362
  pageRange: "{from}\u2013{to} \u5171 {total}",
357
- perPage: "{size} / \u9875"
363
+ perPage: "{size} / \u9875",
364
+ selectRow: "\u9009\u62E9\u672C\u884C",
365
+ selectAll: "\u5168\u9009"
358
366
  }
359
367
  };
360
368
  var zhTW = {
@@ -418,6 +426,8 @@ var zhTW = {
418
426
  carousel: { previous: "\u4E0A\u4E00\u9801", next: "\u4E0B\u4E00\u9801", goToSlide: "\u8DF3\u81F3\u7B2C {n} \u5F35" },
419
427
  input: { clear: "\u6E05\u9664" },
420
428
  notification: { close: "\u95DC\u9589" },
429
+ select: { placeholder: "\u8ACB\u9078\u64C7" },
430
+ counterInput: { decrease: "\u6E1B\u5C11", increase: "\u589E\u52A0" },
421
431
  form: {
422
432
  required: "\u6B64\u6B04\u4F4D\u70BA\u5FC5\u586B",
423
433
  empty: "\u4E0D\u80FD\u70BA\u7A7A",
@@ -448,7 +458,9 @@ var zhTW = {
448
458
  previous: "\u4E0A\u4E00\u9801",
449
459
  next: "\u4E0B\u4E00\u9801",
450
460
  pageRange: "{from}\u2013{to} \u5171 {total}",
451
- perPage: "{size} / \u9801"
461
+ perPage: "{size} / \u9801",
462
+ selectRow: "\u9078\u64C7\u672C\u884C",
463
+ selectAll: "\u5168\u9078"
452
464
  }
453
465
  };
454
466
  var jaJP = {
@@ -2231,6 +2243,275 @@ var CopyButton = forwardRef(
2231
2243
  }
2232
2244
  );
2233
2245
  CopyButton.displayName = "CopyButton";
2246
+ var Toggle = forwardRef(function Toggle2({
2247
+ variant = "default",
2248
+ size = "medium",
2249
+ pressed,
2250
+ defaultPressed = false,
2251
+ onPressedChange,
2252
+ className,
2253
+ onClick,
2254
+ disabled,
2255
+ ...props
2256
+ }, ref) {
2257
+ const [internal, setInternal] = useState(defaultPressed);
2258
+ const controlled = pressed !== void 0;
2259
+ const isPressed = controlled ? pressed : internal;
2260
+ return /* @__PURE__ */ jsx(
2261
+ "button",
2262
+ {
2263
+ ref,
2264
+ type: "button",
2265
+ className: cx(
2266
+ "klun-toggle",
2267
+ `klun-toggle--${variant}`,
2268
+ `klun-toggle--${size}`,
2269
+ className
2270
+ ),
2271
+ "aria-pressed": isPressed,
2272
+ disabled,
2273
+ onClick: (event) => {
2274
+ if (!controlled) setInternal(!isPressed);
2275
+ onPressedChange?.(!isPressed);
2276
+ onClick?.(event);
2277
+ },
2278
+ ...props
2279
+ }
2280
+ );
2281
+ });
2282
+ Toggle.displayName = "Toggle";
2283
+ function localTodayIso() {
2284
+ const now = /* @__PURE__ */ new Date();
2285
+ return [now.getFullYear(), now.getMonth() + 1, now.getDate()].map((part, i) => String(part).padStart(i === 0 ? 4 : 2, "0")).join("-");
2286
+ }
2287
+ function addDaysIso(iso, days) {
2288
+ const [y, m, d] = iso.split("-").map(Number);
2289
+ const dt = new Date(Date.UTC(y ?? 1970, (m ?? 1) - 1, (d ?? 1) + days));
2290
+ return [dt.getUTCFullYear(), dt.getUTCMonth() + 1, dt.getUTCDate()].map((part, i) => String(part).padStart(i === 0 ? 4 : 2, "0")).join("-");
2291
+ }
2292
+ function weekStartIso(iso) {
2293
+ const [y, m, d] = iso.split("-").map(Number);
2294
+ const dt = new Date(Date.UTC(y ?? 1970, (m ?? 1) - 1, d ?? 1));
2295
+ const mondayOffset = (dt.getUTCDay() + 6) % 7;
2296
+ return addDaysIso(iso, -mondayOffset);
2297
+ }
2298
+ var WEEKDAY_ANCHOR = Date.UTC(2026, 0, 5);
2299
+ var LABELED_WEEKDAY_INDICES = /* @__PURE__ */ new Set([0, 2, 4]);
2300
+ function buildHeatmapGrid(data, options = {}) {
2301
+ const { weeks = 26, today = localTodayIso(), locale = "en-US" } = options;
2302
+ const lastWeekStart = weekStartIso(today);
2303
+ const startDate = addDaysIso(lastWeekStart, -((weeks - 1) * 7));
2304
+ const todayIndex = (weeks - 1) * 7 + (() => {
2305
+ const [y, m, d] = today.split("-").map(Number);
2306
+ const dt = new Date(Date.UTC(y ?? 1970, (m ?? 1) - 1, d ?? 1));
2307
+ return (dt.getUTCDay() + 6) % 7;
2308
+ })();
2309
+ const all = [];
2310
+ for (let i = 0; i <= todayIndex; i++) {
2311
+ const date = addDaysIso(startDate, i);
2312
+ all.push({
2313
+ date,
2314
+ dayOfWeek: i % 7,
2315
+ week: Math.floor(i / 7),
2316
+ value: data?.[date] ?? 0,
2317
+ level: 0
2318
+ });
2319
+ }
2320
+ const nonZero = all.filter((c) => c.value > 0).map((c) => c.value).sort((a, b) => a - b);
2321
+ for (const cell of all) {
2322
+ if (cell.value <= 0) continue;
2323
+ if (nonZero.length <= 1) {
2324
+ cell.level = 4;
2325
+ continue;
2326
+ }
2327
+ const p = nonZero.indexOf(cell.value) / (nonZero.length - 1);
2328
+ cell.level = p <= 0.25 ? 1 : p <= 0.5 ? 2 : p <= 0.75 ? 3 : 4;
2329
+ }
2330
+ const months = [];
2331
+ let lastMonth = -1;
2332
+ for (const c of all) {
2333
+ const [y, m] = c.date.split("-").map(Number);
2334
+ const dt = new Date(Date.UTC(y ?? 1970, (m ?? 1) - 1, 1));
2335
+ const month = dt.getUTCMonth();
2336
+ if (month !== lastMonth && c.dayOfWeek === 0) {
2337
+ months.push({
2338
+ label: dt.toLocaleString(locale, { month: "short", timeZone: "UTC" }),
2339
+ week: c.week
2340
+ });
2341
+ lastMonth = month;
2342
+ }
2343
+ }
2344
+ const weekdayFmt = new Intl.DateTimeFormat(locale, { weekday: "short", timeZone: "UTC" });
2345
+ const weekdayLabels = Array.from(
2346
+ { length: 7 },
2347
+ (_, i) => weekdayFmt.format(new Date(WEEKDAY_ANCHOR + i * 864e5))
2348
+ );
2349
+ return { cells: all, months, weekdayLabels, windowDays: all.length };
2350
+ }
2351
+ function summarizeHeatmap(data, options = {}) {
2352
+ const { cells } = buildHeatmapGrid(data, options);
2353
+ let total = 0;
2354
+ let busiest = null;
2355
+ const sums = [0, 0, 0, 0, 0, 0, 0];
2356
+ const counts = [0, 0, 0, 0, 0, 0, 0];
2357
+ for (const c of cells) {
2358
+ total += c.value;
2359
+ sums[c.dayOfWeek] = (sums[c.dayOfWeek] ?? 0) + c.value;
2360
+ counts[c.dayOfWeek] = (counts[c.dayOfWeek] ?? 0) + 1;
2361
+ if (c.value > 0 && (!busiest || c.value > busiest.value)) {
2362
+ busiest = { date: c.date, value: c.value };
2363
+ }
2364
+ }
2365
+ if (total === 0) busiest = null;
2366
+ return {
2367
+ total,
2368
+ busiest,
2369
+ dailyAverage: cells.length > 0 ? total / cells.length : 0,
2370
+ weekdayAverages: sums.map((s, i) => (counts[i] ?? 0) > 0 ? s / (counts[i] ?? 1) : 0)
2371
+ };
2372
+ }
2373
+ var CELL_GAP = 3;
2374
+ var LABEL_WIDTH = 28;
2375
+ var HEADER_HEIGHT = 14;
2376
+ function levelFill(color, level) {
2377
+ if (level === 0) return "var(--klun-bg-soft-200)";
2378
+ const mix = ["25%", "50%", "75%", "100%"][level - 1] ?? "100%";
2379
+ return `color-mix(in oklab, ${color} ${mix}, transparent)`;
2380
+ }
2381
+ var ActivityHeatmap = forwardRef(
2382
+ function ActivityHeatmap2({
2383
+ data,
2384
+ weeks = 26,
2385
+ today,
2386
+ cellSize = 16,
2387
+ color = "var(--klun-primary-base)",
2388
+ formatValue = String,
2389
+ locale = "en-US",
2390
+ legendLabels = ["Less", "More"],
2391
+ className,
2392
+ style
2393
+ }, ref) {
2394
+ const { cells, months, weekdayLabels, windowDays } = useMemo(
2395
+ () => buildHeatmapGrid(data, { weeks, today, locale }),
2396
+ [data, weeks, today, locale]
2397
+ );
2398
+ const step = cellSize + CELL_GAP;
2399
+ const svgWidth = LABEL_WIDTH + weeks * step;
2400
+ const svgHeight = HEADER_HEIGHT + 7 * step;
2401
+ return /* @__PURE__ */ jsxs("div", { ref, className: cx("klun-activity-heatmap", className), style, children: [
2402
+ /* @__PURE__ */ jsx("div", { className: "klun-activity-heatmap__scroll", children: /* @__PURE__ */ jsxs(
2403
+ "svg",
2404
+ {
2405
+ className: "klun-activity-heatmap__svg",
2406
+ width: svgWidth,
2407
+ height: svgHeight,
2408
+ role: "img",
2409
+ "aria-label": `Activity heatmap over ${windowDays} days`,
2410
+ children: [
2411
+ months.map((month) => /* @__PURE__ */ jsx(
2412
+ "text",
2413
+ {
2414
+ className: "klun-activity-heatmap__label",
2415
+ x: LABEL_WIDTH + month.week * step,
2416
+ y: 10,
2417
+ fontSize: 9,
2418
+ children: month.label
2419
+ },
2420
+ `${month.label}-${month.week}`
2421
+ )),
2422
+ weekdayLabels.map(
2423
+ (label, i) => LABELED_WEEKDAY_INDICES.has(i) ? /* @__PURE__ */ jsx(
2424
+ "text",
2425
+ {
2426
+ className: "klun-activity-heatmap__label",
2427
+ x: 0,
2428
+ y: HEADER_HEIGHT + i * step + cellSize - 1,
2429
+ fontSize: 9,
2430
+ children: label
2431
+ },
2432
+ i
2433
+ ) : null
2434
+ ),
2435
+ cells.map((cell) => /* @__PURE__ */ jsx(
2436
+ "rect",
2437
+ {
2438
+ className: "klun-activity-heatmap__cell",
2439
+ x: LABEL_WIDTH + cell.week * step,
2440
+ y: HEADER_HEIGHT + cell.dayOfWeek * step,
2441
+ width: cellSize,
2442
+ height: cellSize,
2443
+ rx: 3,
2444
+ fill: levelFill(color, cell.level),
2445
+ "data-level": cell.level,
2446
+ children: /* @__PURE__ */ jsxs("title", { children: [
2447
+ cell.date,
2448
+ ": ",
2449
+ cell.value > 0 ? formatValue(cell.value) : "No activity"
2450
+ ] })
2451
+ },
2452
+ cell.date
2453
+ ))
2454
+ ]
2455
+ }
2456
+ ) }),
2457
+ /* @__PURE__ */ jsxs("div", { className: "klun-activity-heatmap__legend", children: [
2458
+ /* @__PURE__ */ jsx("span", { children: legendLabels[0] }),
2459
+ [0, 1, 2, 3, 4].map((level) => /* @__PURE__ */ jsx(
2460
+ "span",
2461
+ {
2462
+ className: "klun-activity-heatmap__swatch",
2463
+ style: { background: levelFill(color, level) }
2464
+ },
2465
+ level
2466
+ )),
2467
+ /* @__PURE__ */ jsx("span", { children: legendLabels[1] })
2468
+ ] })
2469
+ ] });
2470
+ }
2471
+ );
2472
+ var EMOJI_PREFIX = "emoji:";
2473
+ function parseAvatarEmoji(value) {
2474
+ if (!value?.startsWith(EMOJI_PREFIX)) return null;
2475
+ const emoji = value.slice(EMOJI_PREFIX.length).trim();
2476
+ return emoji || null;
2477
+ }
2478
+ function formatAvatarEmoji(emoji) {
2479
+ return EMOJI_PREFIX + emoji.trim();
2480
+ }
2481
+ var KIND_ICON = {
2482
+ agent: "ri-robot-2-line",
2483
+ squad: "ri-group-line",
2484
+ system: "ri-cpu-line"
2485
+ };
2486
+ var ActorAvatar = forwardRef(
2487
+ function ActorAvatar2({ name, initials, src, emoji, kind = "member", size = "24", className, ...props }, ref) {
2488
+ const [imgError, setImgError] = useState(false);
2489
+ useEffect(() => {
2490
+ setImgError(false);
2491
+ }, [src]);
2492
+ const resolvedEmoji = emoji ?? parseAvatarEmoji(src);
2493
+ return /* @__PURE__ */ jsx(
2494
+ "span",
2495
+ {
2496
+ ref,
2497
+ role: "img",
2498
+ "aria-label": name,
2499
+ className: cx("klun-actor-avatar", `klun-actor-avatar--${size}`, className),
2500
+ ...props,
2501
+ children: resolvedEmoji ? /* @__PURE__ */ jsx("span", { className: "klun-actor-avatar__emoji", children: resolvedEmoji }) : src && !imgError ? /* @__PURE__ */ jsx(
2502
+ "img",
2503
+ {
2504
+ className: "klun-actor-avatar__img",
2505
+ src,
2506
+ alt: "",
2507
+ onError: () => setImgError(true)
2508
+ }
2509
+ ) : kind !== "member" ? /* @__PURE__ */ jsx("i", { className: cx("klun-actor-avatar__icon", KIND_ICON[kind]), "aria-hidden": "true" }) : /* @__PURE__ */ jsx("span", { className: "klun-actor-avatar__initials", children: initials })
2510
+ }
2511
+ );
2512
+ }
2513
+ );
2514
+ ActorAvatar.displayName = "ActorAvatar";
2234
2515
  var Avatar = forwardRef(function Avatar2({ size = "40", src, alt = "", initials, color = "gray", status, icon, className, ...props }, ref) {
2235
2516
  return /* @__PURE__ */ jsxs(
2236
2517
  "span",
@@ -2900,7 +3181,7 @@ var Skeleton = forwardRef(function Skeleton2({ width = "100%", height = 16, circ
2900
3181
  );
2901
3182
  });
2902
3183
  Skeleton.displayName = "Skeleton";
2903
- var KIND_ICON = {
3184
+ var KIND_ICON2 = {
2904
3185
  artifact: "ri-price-tag-3-line",
2905
3186
  pr: "ri-git-pull-request-line",
2906
3187
  commit: "ri-git-commit-line",
@@ -2955,7 +3236,7 @@ var CrossRefBadge = forwardRef(
2955
3236
  document.addEventListener("keydown", onKey);
2956
3237
  return () => document.removeEventListener("keydown", onKey);
2957
3238
  }, [open, close]);
2958
- const iconCls = KIND_ICON[kind];
3239
+ const iconCls = KIND_ICON2[kind];
2959
3240
  const chipColor = color ?? KIND_COLOR[kind];
2960
3241
  const inner = /* @__PURE__ */ jsxs(Chip, { color: chipColor, className: "klun-xref__chip", children: [
2961
3242
  /* @__PURE__ */ jsx("i", { className: cx("klun-xref__icon", iconCls), "aria-hidden": true }),
@@ -3345,6 +3626,98 @@ var DiffViewer = forwardRef(function DiffViewer2({
3345
3626
  );
3346
3627
  });
3347
3628
  DiffViewer.displayName = "DiffViewer";
3629
+ var ItemGroup = forwardRef(
3630
+ function ItemGroup2({ className, ...props }, ref) {
3631
+ return /* @__PURE__ */ jsx(
3632
+ "div",
3633
+ {
3634
+ ref,
3635
+ role: "list",
3636
+ className: cx("klun-item-group", className),
3637
+ ...props
3638
+ }
3639
+ );
3640
+ }
3641
+ );
3642
+ ItemGroup.displayName = "ItemGroup";
3643
+ var Item = forwardRef(function Item2({ variant = "default", size = "default", className, ...props }, ref) {
3644
+ return /* @__PURE__ */ jsx(
3645
+ "div",
3646
+ {
3647
+ ref,
3648
+ "data-variant": variant,
3649
+ "data-size": size,
3650
+ className: cx("klun-item", `klun-item--${variant}`, `klun-item--${size}`, className),
3651
+ ...props
3652
+ }
3653
+ );
3654
+ });
3655
+ Item.displayName = "Item";
3656
+ var ItemSeparator = forwardRef(
3657
+ function ItemSeparator2({ className, ...props }, ref) {
3658
+ return /* @__PURE__ */ jsx(
3659
+ "div",
3660
+ {
3661
+ ref,
3662
+ role: "separator",
3663
+ "aria-orientation": "horizontal",
3664
+ className: cx("klun-item-separator", className),
3665
+ ...props
3666
+ }
3667
+ );
3668
+ }
3669
+ );
3670
+ ItemSeparator.displayName = "ItemSeparator";
3671
+ var ItemMedia = forwardRef(
3672
+ function ItemMedia2({ variant = "default", className, ...props }, ref) {
3673
+ return /* @__PURE__ */ jsx(
3674
+ "div",
3675
+ {
3676
+ ref,
3677
+ "data-variant": variant,
3678
+ className: cx("klun-item__media", className),
3679
+ ...props
3680
+ }
3681
+ );
3682
+ }
3683
+ );
3684
+ ItemMedia.displayName = "ItemMedia";
3685
+ var ItemContent = forwardRef(
3686
+ function ItemContent2({ className, ...props }, ref) {
3687
+ return /* @__PURE__ */ jsx("div", { ref, className: cx("klun-item__content", className), ...props });
3688
+ }
3689
+ );
3690
+ ItemContent.displayName = "ItemContent";
3691
+ var ItemTitle = forwardRef(
3692
+ function ItemTitle2({ className, ...props }, ref) {
3693
+ return /* @__PURE__ */ jsx("div", { ref, className: cx("klun-item__title", className), ...props });
3694
+ }
3695
+ );
3696
+ ItemTitle.displayName = "ItemTitle";
3697
+ var ItemDescription = forwardRef(
3698
+ function ItemDescription2({ className, ...props }, ref) {
3699
+ return /* @__PURE__ */ jsx("div", { ref, className: cx("klun-item__description", className), ...props });
3700
+ }
3701
+ );
3702
+ ItemDescription.displayName = "ItemDescription";
3703
+ var ItemActions = forwardRef(
3704
+ function ItemActions2({ className, ...props }, ref) {
3705
+ return /* @__PURE__ */ jsx("div", { ref, className: cx("klun-item__actions", className), ...props });
3706
+ }
3707
+ );
3708
+ ItemActions.displayName = "ItemActions";
3709
+ var ItemHeader = forwardRef(
3710
+ function ItemHeader2({ className, ...props }, ref) {
3711
+ return /* @__PURE__ */ jsx("div", { ref, className: cx("klun-item__header", className), ...props });
3712
+ }
3713
+ );
3714
+ ItemHeader.displayName = "ItemHeader";
3715
+ var ItemFooter = forwardRef(
3716
+ function ItemFooter2({ className, ...props }, ref) {
3717
+ return /* @__PURE__ */ jsx("div", { ref, className: cx("klun-item__footer", className), ...props });
3718
+ }
3719
+ );
3720
+ ItemFooter.displayName = "ItemFooter";
3348
3721
  function draggedCenterY(active, delta) {
3349
3722
  const translated = active.rect.current.translated;
3350
3723
  if (translated) return translated.top + translated.height / 2;
@@ -3529,72 +3902,6 @@ function KanbanCardView({
3529
3902
  }
3530
3903
  );
3531
3904
  }
3532
- function VirtualTable({
3533
- rows = [],
3534
- columns = [],
3535
- rowHeight = 40,
3536
- height = 420,
3537
- getRowId,
3538
- onRowClick,
3539
- overscan = 8,
3540
- zebra = true,
3541
- empty,
3542
- className,
3543
- style,
3544
- ...props
3545
- }) {
3546
- const [scrollTop, setScrollTop] = useState(0);
3547
- const total = rows.length;
3548
- const template = useMemo(
3549
- () => columns.map((c) => c.width ? `${c.width}px` : "minmax(0, 1fr)").join(" "),
3550
- [columns]
3551
- );
3552
- const bodyHeight = Math.max(0, height - 36);
3553
- const start = Math.max(0, Math.floor(scrollTop / rowHeight) - overscan);
3554
- const end = Math.min(total, Math.ceil((scrollTop + bodyHeight) / rowHeight) + overscan);
3555
- const slice = rows.slice(start, end);
3556
- return /* @__PURE__ */ jsxs("div", { ref: void 0, className: cx("klun-vtable", className), style: { ...style, height }, ...props, children: [
3557
- /* @__PURE__ */ jsx("div", { className: "klun-vtable__head", style: { gridTemplateColumns: template }, children: columns.map((c) => /* @__PURE__ */ jsx("div", { className: cx("klun-vtable__th", c.align && `klun-vtable__th--${c.align}`), children: c.header }, c.key)) }),
3558
- /* @__PURE__ */ jsx(
3559
- "div",
3560
- {
3561
- className: "klun-vtable__body",
3562
- onScroll: (e) => setScrollTop(e.target.scrollTop),
3563
- style: { maxHeight: bodyHeight },
3564
- children: total === 0 ? /* @__PURE__ */ jsx("div", { className: "klun-vtable__empty", children: empty ?? "No rows" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
3565
- /* @__PURE__ */ jsx("div", { style: { height: start * rowHeight } }),
3566
- slice.map((row, i) => {
3567
- const index = start + i;
3568
- const id = getRowId?.(row, index) ?? index;
3569
- return /* @__PURE__ */ jsx(
3570
- "div",
3571
- {
3572
- role: onRowClick ? "button" : void 0,
3573
- tabIndex: onRowClick ? 0 : void 0,
3574
- onClick: () => onRowClick?.(row, index),
3575
- className: cx(
3576
- "klun-vtable__row",
3577
- zebra && index % 2 === 1 && "klun-vtable__row--zebra",
3578
- onRowClick && "klun-vtable__row--clickable"
3579
- ),
3580
- style: { gridTemplateColumns: template, height: rowHeight },
3581
- children: columns.map((c) => /* @__PURE__ */ jsx("div", { className: cx("klun-vtable__td", c.align && `klun-vtable__td--${c.align}`), children: c.render ? c.render(row, index) : String(row[c.key] ?? "") }, c.key))
3582
- },
3583
- id
3584
- );
3585
- }),
3586
- /* @__PURE__ */ jsx("div", { style: { height: Math.max(0, (total - end) * rowHeight) } })
3587
- ] })
3588
- }
3589
- ),
3590
- /* @__PURE__ */ jsxs("div", { className: "klun-vtable__foot", children: [
3591
- total.toLocaleString("en-US"),
3592
- " rows \xB7 rendering ",
3593
- slice.length
3594
- ] })
3595
- ] });
3596
- }
3597
- VirtualTable.displayName = "VirtualTable";
3598
3905
  var Kbd = forwardRef(function Kbd2({ className, children, ...props }, ref) {
3599
3906
  return /* @__PURE__ */ jsx("kbd", { ref, className: cx("klun-kbd", className), ...props, children });
3600
3907
  });
@@ -3755,8 +4062,91 @@ var ListItem = forwardRef(
3755
4062
  }
3756
4063
  );
3757
4064
  ListItem.displayName = "ListItem";
4065
+ var ListGrid = forwardRef(function ListGrid2({ columns, className, style, ...props }, ref) {
4066
+ return /* @__PURE__ */ jsx(
4067
+ "div",
4068
+ {
4069
+ ref,
4070
+ role: "table",
4071
+ className: cx("klun-list-grid", className),
4072
+ style: { gridTemplateColumns: columns, ...style },
4073
+ ...props
4074
+ }
4075
+ );
4076
+ });
4077
+ ListGrid.displayName = "ListGrid";
4078
+ var ListGridHeader = forwardRef(
4079
+ function ListGridHeader2({ className, children, ...props }, ref) {
4080
+ return /* @__PURE__ */ jsxs("div", { ref, role: "row", className: cx("klun-list-grid__header", className), ...props, children: [
4081
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true" }),
4082
+ children,
4083
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true" })
4084
+ ] });
4085
+ }
4086
+ );
4087
+ ListGridHeader.displayName = "ListGridHeader";
4088
+ var ListGridBody = forwardRef(
4089
+ function ListGridBody2({ className, ...props }, ref) {
4090
+ return /* @__PURE__ */ jsx("div", { ref, role: "rowgroup", className: cx("klun-list-grid__body", className), ...props });
4091
+ }
4092
+ );
4093
+ ListGridBody.displayName = "ListGridBody";
4094
+ var ListGridHeaderCell = forwardRef(
4095
+ function ListGridHeaderCell2({ sorted = false, onSort, align = "left", className, children, ...props }, ref) {
4096
+ return /* @__PURE__ */ jsx(
4097
+ "div",
4098
+ {
4099
+ ref,
4100
+ role: "columnheader",
4101
+ "data-sorted": sorted ? "" : void 0,
4102
+ "data-align": align === "right" ? "right" : void 0,
4103
+ "aria-sort": sorted === "asc" ? "ascending" : sorted === "desc" ? "descending" : void 0,
4104
+ className: cx("klun-list-grid__header-cell", className),
4105
+ ...props,
4106
+ children: onSort ? /* @__PURE__ */ jsxs("button", { type: "button", className: "klun-list-grid__sort", onClick: onSort, children: [
4107
+ children,
4108
+ /* @__PURE__ */ jsx(
4109
+ "i",
4110
+ {
4111
+ "aria-hidden": "true",
4112
+ className: cx(
4113
+ "klun-list-grid__sort-icon",
4114
+ sorted === "asc" ? "ri-arrow-up-s-line" : "ri-arrow-down-s-line"
4115
+ )
4116
+ }
4117
+ )
4118
+ ] }) : children
4119
+ }
4120
+ );
4121
+ }
4122
+ );
4123
+ ListGridHeaderCell.displayName = "ListGridHeaderCell";
4124
+ var ListGridRow = forwardRef(
4125
+ function ListGridRow2({ className, children, ...props }, ref) {
4126
+ return /* @__PURE__ */ jsxs("div", { ref, role: "row", className: cx("klun-list-grid__row", className), ...props, children: [
4127
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true" }),
4128
+ children,
4129
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true" })
4130
+ ] });
4131
+ }
4132
+ );
4133
+ ListGridRow.displayName = "ListGridRow";
4134
+ var ListGridCell = forwardRef(
4135
+ function ListGridCell2({ className, ...props }, ref) {
4136
+ return /* @__PURE__ */ jsx("div", { ref, role: "cell", className: cx("klun-list-grid__cell", className), ...props });
4137
+ }
4138
+ );
4139
+ ListGridCell.displayName = "ListGridCell";
4140
+ var LIST_GRID_BOTTOM_CLEARANCE = 64;
3758
4141
  var k = 0;
3759
4142
  var wikiResolve;
4143
+ function safeUrl(value, image = false) {
4144
+ const normalized = value.replace(/[\u0000-\u0020]/g, "");
4145
+ const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(normalized)?.[1].toLowerCase();
4146
+ if (!scheme || ["http", "https", ...image ? ["blob"] : ["mailto", "tel"]].includes(scheme)) return value;
4147
+ if (image && /^data:image\/(png|jpeg|gif|webp|svg\+xml)[;,]/i.test(normalized)) return value;
4148
+ return void 0;
4149
+ }
3760
4150
  function inline(text) {
3761
4151
  const patterns = [
3762
4152
  // [[wiki link]] / [[target|label]] — resolved via the wikiLink prop; falls
@@ -3768,20 +4158,20 @@ function inline(text) {
3768
4158
  const r = wikiResolve?.(target);
3769
4159
  const resolved = typeof r === "string" ? { href: r, label: void 0 } : r || null;
3770
4160
  const label = m[2]?.trim() || resolved?.label || target;
3771
- return resolved ? /* @__PURE__ */ jsx("a", { className: "klun-md-wikilink", href: resolved.href, children: label }, k++) : /* @__PURE__ */ jsx("span", { className: "klun-md-wikilink klun-md-wikilink--broken", children: label }, k++);
4161
+ return resolved && safeUrl(resolved.href) ? /* @__PURE__ */ jsx("a", { className: "klun-md-wikilink", href: safeUrl(resolved.href), children: label }, k++) : /* @__PURE__ */ jsx("span", { className: "klun-md-wikilink klun-md-wikilink--broken", children: label }, k++);
3772
4162
  }
3773
4163
  },
3774
4164
  { re: /`([^`]+)`/, node: (m) => /* @__PURE__ */ jsx("code", { className: "klun-md-code", children: m[1] }, k++) },
3775
4165
  // image before link (`![alt](url)` starts with `!` + link syntax). url allows
3776
4166
  // http(s) and data: URIs (base64 has no `)`), so pasted screenshots embed inline.
3777
- { re: /!\[([^\]]*)\]\(([^)]+)\)/, node: (m) => /* @__PURE__ */ jsx("img", { className: "klun-md-img", src: m[2], alt: m[1] }, k++) },
4167
+ { re: /!\[([^\]]*)\]\(([^)]+)\)/, node: (m) => /* @__PURE__ */ jsx("img", { className: "klun-md-img", src: safeUrl(m[2], true), alt: m[1] }, k++) },
3778
4168
  { re: /\*\*([^*]+)\*\*/, node: (m) => /* @__PURE__ */ jsx("strong", { children: inline(m[1]) }, k++) },
3779
4169
  { re: /~~([^~]+)~~/, node: (m) => /* @__PURE__ */ jsx("del", { children: inline(m[1]) }, k++) },
3780
4170
  { re: /\*([^*]+)\*/, node: (m) => /* @__PURE__ */ jsx("em", { children: inline(m[1]) }, k++) },
3781
4171
  { re: /<u>([\s\S]*?)<\/u>/, node: (m) => /* @__PURE__ */ jsx("u", { children: inline(m[1]) }, k++) },
3782
4172
  {
3783
4173
  re: /\[([^\]]+)\]\(([^)]+)\)/,
3784
- node: (m) => /* @__PURE__ */ jsx("a", { href: m[2], target: "_blank", rel: "noreferrer", children: m[1] }, k++)
4174
+ node: (m) => /* @__PURE__ */ jsx("a", { href: safeUrl(m[2]), target: "_blank", rel: "noreferrer", children: m[1] }, k++)
3785
4175
  },
3786
4176
  // bare autolink: a plain http(s) URL becomes a link. Runs after the
3787
4177
  // [text](url)/image patterns — those start at an earlier index (the `[`/`!`)
@@ -3982,36 +4372,888 @@ var Money = forwardRef(function Money2({
3982
4372
  );
3983
4373
  });
3984
4374
  Money.displayName = "Money";
3985
- var Rating = forwardRef(function Rating2({ value = 0, max = 5, onChange, readOnly = false, size = 20, className, ...props }, ref) {
3986
- const { rating } = useLocale();
3987
- const [hover, setHover] = useState(0);
3988
- const active = hover || value;
3989
- return /* @__PURE__ */ jsx("div", { ref, className: cx("klun-rating", className), ...props, children: Array.from({ length: max }, (_, i) => i + 1).map((i) => /* @__PURE__ */ jsx(
3990
- "button",
4375
+ var DIGITS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
4376
+ function Digit({ value }) {
4377
+ return /* @__PURE__ */ jsx("span", { className: "klun-number-flow__digit", children: /* @__PURE__ */ jsx(
4378
+ "span",
3991
4379
  {
3992
- type: "button",
3993
- className: "klun-rating__star",
3994
- disabled: readOnly,
3995
- "data-active": i <= active ? "" : void 0,
3996
- onMouseEnter: () => !readOnly && setHover(i),
3997
- onMouseLeave: () => !readOnly && setHover(0),
3998
- onClick: () => !readOnly && onChange?.(i),
3999
- "aria-label": fmt(rating.star, { count: i }),
4000
- children: /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M12 2l2.9 6.1 6.6.9-4.8 4.6 1.2 6.6L12 17.8 5.9 20.8l1.2-6.6L2.3 9l6.6-.9L12 2z" }) })
4001
- },
4002
- i
4003
- )) });
4004
- });
4005
- Rating.displayName = "Rating";
4006
- var StatCard = forwardRef(function StatCard2({ label, value, delta, trend = "up", icon, tint = "feature", className, ...props }, ref) {
4007
- const up = trend === "up";
4008
- return /* @__PURE__ */ jsxs("div", { ref, className: cx("klun-stat-card", `klun-stat-card--${tint}`, className), ...props, children: [
4009
- /* @__PURE__ */ jsxs("div", { className: "klun-stat-card__head", children: [
4010
- /* @__PURE__ */ jsx("span", { className: "klun-stat-card__icon", children: icon }),
4011
- delta != null ? /* @__PURE__ */ jsxs(
4012
- "span",
4013
- {
4014
- className: cx(
4380
+ className: "klun-number-flow__track",
4381
+ style: { transform: `translateY(${-value}em)` },
4382
+ children: DIGITS.map((d) => /* @__PURE__ */ jsx("span", { className: "klun-number-flow__num", children: d }, d))
4383
+ }
4384
+ ) });
4385
+ }
4386
+ var NumberFlow = forwardRef(
4387
+ function NumberFlow2({ value, locales: locales2, format, prefix, suffix, label, className, ...props }, ref) {
4388
+ const text = new Intl.NumberFormat(locales2, {
4389
+ minimumFractionDigits: format?.minimumFractionDigits,
4390
+ maximumFractionDigits: format?.maximumFractionDigits,
4391
+ useGrouping: format?.useGrouping
4392
+ }).format(value);
4393
+ const chars = [...text];
4394
+ return /* @__PURE__ */ jsxs(
4395
+ "span",
4396
+ {
4397
+ ref,
4398
+ className: cx("klun-number-flow", className),
4399
+ "aria-label": label ?? text,
4400
+ ...props,
4401
+ children: [
4402
+ prefix != null && /* @__PURE__ */ jsx("span", { className: "klun-number-flow__affix", children: prefix }),
4403
+ /* @__PURE__ */ jsx("span", { className: "klun-number-flow__visual", "aria-hidden": "true", children: chars.map(
4404
+ (ch, i) => ch >= "0" && ch <= "9" ? /* @__PURE__ */ jsx(Digit, { value: Number(ch) }, chars.length - i) : /* @__PURE__ */ jsx("span", { className: "klun-number-flow__char", children: ch }, chars.length - i)
4405
+ ) }),
4406
+ suffix != null && /* @__PURE__ */ jsx("span", { className: "klun-number-flow__affix", children: suffix })
4407
+ ]
4408
+ }
4409
+ );
4410
+ }
4411
+ );
4412
+ NumberFlow.displayName = "NumberFlow";
4413
+ var CurrencyNumberFlow = forwardRef(
4414
+ function CurrencyNumberFlow2({ value, prefix = "$", fractionThreshold = 100, locales: locales2, label, ...props }, ref) {
4415
+ const fractionDigits = Math.abs(value) >= fractionThreshold ? 0 : 2;
4416
+ const format = {
4417
+ minimumFractionDigits: fractionDigits,
4418
+ maximumFractionDigits: fractionDigits,
4419
+ useGrouping: false
4420
+ };
4421
+ const formatted = new Intl.NumberFormat(locales2, format).format(value);
4422
+ return /* @__PURE__ */ jsx(
4423
+ NumberFlow,
4424
+ {
4425
+ ref,
4426
+ value,
4427
+ locales: locales2,
4428
+ prefix,
4429
+ format,
4430
+ label: label ?? `${prefix}${formatted}`,
4431
+ ...props
4432
+ }
4433
+ );
4434
+ }
4435
+ );
4436
+ CurrencyNumberFlow.displayName = "CurrencyNumberFlow";
4437
+ var COMPACT_UNITS = [
4438
+ { divisor: 1, upperSuffix: void 0, lowerSuffix: void 0 },
4439
+ { divisor: 1e3, upperSuffix: "K", lowerSuffix: "k" },
4440
+ { divisor: 1e6, upperSuffix: "M", lowerSuffix: "m" },
4441
+ { divisor: 1e9, upperSuffix: "B", lowerSuffix: "b" },
4442
+ { divisor: 1e12, upperSuffix: "T", lowerSuffix: "t" }
4443
+ ];
4444
+ var CompactNumberFlow = forwardRef(
4445
+ function CompactNumberFlow2({ value, suffixCase = "upper", locales: locales2, label, ...props }, ref) {
4446
+ const magnitude = Math.abs(value);
4447
+ let unitIndex = 0;
4448
+ for (let i = COMPACT_UNITS.length - 1; i >= 0; i--) {
4449
+ if (magnitude >= COMPACT_UNITS[i].divisor) {
4450
+ unitIndex = i;
4451
+ break;
4452
+ }
4453
+ }
4454
+ let unit = COMPACT_UNITS[unitIndex];
4455
+ let scaledValue = value / unit.divisor;
4456
+ if (Math.abs(Number(scaledValue.toFixed(1))) >= 1e3 && unitIndex < COMPACT_UNITS.length - 1) {
4457
+ unit = COMPACT_UNITS[unitIndex + 1];
4458
+ scaledValue = value / unit.divisor;
4459
+ }
4460
+ const roundedValue = Number(scaledValue.toFixed(1));
4461
+ const fractionDigits = unit.divisor > 1 && !Number.isInteger(roundedValue) ? 1 : 0;
4462
+ const suffix = suffixCase === "upper" ? unit.upperSuffix : unit.lowerSuffix;
4463
+ const format = {
4464
+ minimumFractionDigits: fractionDigits,
4465
+ maximumFractionDigits: fractionDigits,
4466
+ useGrouping: unit.divisor === 1
4467
+ };
4468
+ const formatted = new Intl.NumberFormat(locales2, format).format(scaledValue);
4469
+ return /* @__PURE__ */ jsx(
4470
+ NumberFlow,
4471
+ {
4472
+ ref,
4473
+ value: scaledValue,
4474
+ locales: locales2,
4475
+ suffix,
4476
+ format,
4477
+ label: label ?? `${formatted}${suffix ?? ""}`,
4478
+ ...props
4479
+ }
4480
+ );
4481
+ }
4482
+ );
4483
+ CompactNumberFlow.displayName = "CompactNumberFlow";
4484
+ var CappedNumberFlow = forwardRef(
4485
+ function CappedNumberFlow2({ value, max = 99, label, ...props }, ref) {
4486
+ const cappedValue = Math.min(Math.max(value, 0), max);
4487
+ return /* @__PURE__ */ jsx(
4488
+ NumberFlow,
4489
+ {
4490
+ ref,
4491
+ value: cappedValue,
4492
+ suffix: value > max ? "+" : void 0,
4493
+ format: { maximumFractionDigits: 0, useGrouping: false },
4494
+ label: label ?? String(value),
4495
+ ...props
4496
+ }
4497
+ );
4498
+ }
4499
+ );
4500
+ CappedNumberFlow.displayName = "CappedNumberFlow";
4501
+ var Rating = forwardRef(function Rating2({ value = 0, max = 5, onChange, readOnly = false, size = 20, className, ...props }, ref) {
4502
+ const { rating } = useLocale();
4503
+ const [hover, setHover] = useState(0);
4504
+ const active = hover || value;
4505
+ return /* @__PURE__ */ jsx("div", { ref, className: cx("klun-rating", className), ...props, children: Array.from({ length: max }, (_, i) => i + 1).map((i) => /* @__PURE__ */ jsx(
4506
+ "button",
4507
+ {
4508
+ type: "button",
4509
+ className: "klun-rating__star",
4510
+ disabled: readOnly,
4511
+ "data-active": i <= active ? "" : void 0,
4512
+ onMouseEnter: () => !readOnly && setHover(i),
4513
+ onMouseLeave: () => !readOnly && setHover(0),
4514
+ onClick: () => !readOnly && onChange?.(i),
4515
+ "aria-label": fmt(rating.star, { count: i }),
4516
+ children: /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M12 2l2.9 6.1 6.6.9-4.8 4.6 1.2 6.6L12 17.8 5.9 20.8l1.2-6.6L2.3 9l6.6-.9L12 2z" }) })
4517
+ },
4518
+ i
4519
+ )) });
4520
+ });
4521
+ Rating.displayName = "Rating";
4522
+ var Popover = forwardRef(function Popover2({ trigger, children, align = "left", width = 280, className, ...props }, ref) {
4523
+ const [open, setOpen] = useState(false);
4524
+ const wrapRef = useRef(null);
4525
+ const panelRef = useRef(null);
4526
+ const portal = typeof document !== "undefined";
4527
+ const [pos, setPos] = useState(null);
4528
+ useEffect(() => {
4529
+ if (!open) return;
4530
+ const h = (e) => {
4531
+ const t = e.target;
4532
+ if (wrapRef.current?.contains(t)) return;
4533
+ if (panelRef.current?.contains(t)) return;
4534
+ setOpen(false);
4535
+ };
4536
+ document.addEventListener("mousedown", h);
4537
+ return () => document.removeEventListener("mousedown", h);
4538
+ }, [open]);
4539
+ useLayoutEffect(() => {
4540
+ if (!open || !portal) return;
4541
+ const anchor = wrapRef.current;
4542
+ const panel2 = panelRef.current;
4543
+ if (!anchor || !panel2) return;
4544
+ const place2 = () => {
4545
+ const a = anchor.getBoundingClientRect();
4546
+ const gap = 8;
4547
+ const margin = 8;
4548
+ const pw = panel2.offsetWidth || width;
4549
+ const ph = panel2.offsetHeight;
4550
+ const below = window.innerHeight - a.bottom;
4551
+ const openUp = below < ph + gap && a.top > below;
4552
+ const top = openUp ? Math.max(margin, a.top - gap - ph) : Math.min(a.bottom + gap, window.innerHeight - margin - ph);
4553
+ let left = align === "right" ? a.right - pw : a.left;
4554
+ if (left + pw > window.innerWidth - margin) left = window.innerWidth - margin - pw;
4555
+ if (left < margin) left = margin;
4556
+ setPos({ left, top });
4557
+ };
4558
+ place2();
4559
+ window.addEventListener("scroll", place2, true);
4560
+ window.addEventListener("resize", place2);
4561
+ return () => {
4562
+ window.removeEventListener("scroll", place2, true);
4563
+ window.removeEventListener("resize", place2);
4564
+ };
4565
+ }, [open, portal, align, width]);
4566
+ useEffect(() => {
4567
+ if (!open) setPos(null);
4568
+ }, [open]);
4569
+ const setRefs = (node) => {
4570
+ wrapRef.current = node;
4571
+ if (typeof ref === "function") ref(node);
4572
+ else if (ref) ref.current = node;
4573
+ };
4574
+ const content = typeof children === "function" ? children(() => setOpen(false)) : children;
4575
+ const portalStyle = {
4576
+ position: "fixed",
4577
+ left: pos?.left ?? 0,
4578
+ top: pos?.top ?? 0,
4579
+ width,
4580
+ zIndex: 1150,
4581
+ // keep hidden until measured to avoid a flash at the top-left corner
4582
+ visibility: pos ? "visible" : "hidden"
4583
+ };
4584
+ const panel = open ? /* @__PURE__ */ jsx(
4585
+ "div",
4586
+ {
4587
+ ref: panelRef,
4588
+ role: "dialog",
4589
+ className: cx(
4590
+ "klun-popover__panel",
4591
+ // in-flow fallback keeps the legacy left/right anchoring classes
4592
+ !portal && `klun-popover__panel--${align}`
4593
+ ),
4594
+ style: portal ? portalStyle : { width },
4595
+ children: content
4596
+ }
4597
+ ) : null;
4598
+ return /* @__PURE__ */ jsxs("span", { ref: setRefs, className: cx("klun-popover", className), ...props, children: [
4599
+ /* @__PURE__ */ jsx("span", { className: "klun-popover__trigger", onClick: () => setOpen((o) => !o), children: trigger }),
4600
+ portal ? panel && createPortal(panel, document.body) : panel
4601
+ ] });
4602
+ });
4603
+ Popover.displayName = "Popover";
4604
+
4605
+ // src/components/forms/emoji-data.ts
4606
+ var EMOJI_GROUPS = [
4607
+ {
4608
+ id: "people",
4609
+ label: "Smileys & people",
4610
+ emojis: [
4611
+ { emoji: "\u{1F600}", name: "Grinning Face", keywords: ["smile", "happy", "joy", ":D"] },
4612
+ { emoji: "\u{1F603}", name: "Grinning Face with Big Eyes", keywords: ["smiley", "happy", "joy", "haha"] },
4613
+ { emoji: "\u{1F604}", name: "Grinning Face with Smiling Eyes", keywords: ["smile", "happy", "joy", "funny"] },
4614
+ { emoji: "\u{1F601}", name: "Beaming Face with Smiling Eyes", keywords: ["grin", "happy", "smile", "joy"] },
4615
+ { emoji: "\u{1F606}", name: "Grinning Squinting Face", keywords: ["laughing", "satisfied", "happy", "joy"] },
4616
+ { emoji: "\u{1F605}", name: "Grinning Face with Sweat", keywords: ["smile", "hot", "happy", "laugh"] },
4617
+ { emoji: "\u{1F923}", name: "Rolling on the Floor Laughing", keywords: ["face", "lol", "haha", "rofl"] },
4618
+ { emoji: "\u{1F602}", name: "Face with Tears of Joy", keywords: ["cry", "weep", "happy", "happytears"] },
4619
+ { emoji: "\u{1F642}", name: "Slightly Smiling Face", keywords: ["smile"] },
4620
+ { emoji: "\u{1F643}", name: "Upside-Down Face", keywords: ["upside", "down", "flipped", "silly"] },
4621
+ { emoji: "\u{1FAE0}", name: "Melting Face", keywords: ["hot", "heat"] },
4622
+ { emoji: "\u{1F609}", name: "Winking Face", keywords: ["wink", "happy", "mischievous", "secret"] },
4623
+ { emoji: "\u{1F60A}", name: "Smiling Face with Smiling Eyes", keywords: ["blush", "smile", "happy", "flushed"] },
4624
+ { emoji: "\u{1F607}", name: "Smiling Face with Halo", keywords: ["innocent", "angel", "heaven"] },
4625
+ { emoji: "\u{1F970}", name: "Smiling Face with Hearts", keywords: ["3", "love", "like", "affection"] },
4626
+ { emoji: "\u{1F60D}", name: "Smiling Face with Heart-Eyes", keywords: ["heart", "eyes", "love", "like"] },
4627
+ { emoji: "\u{1F929}", name: "Star-Struck", keywords: ["star", "struck", "grinning", "face"] },
4628
+ { emoji: "\u{1F618}", name: "Face Blowing a Kiss", keywords: ["kissing", "heart", "love", "like"] },
4629
+ { emoji: "\u{1F617}", name: "Kissing Face", keywords: ["love", "like", "3", "valentines"] },
4630
+ { emoji: "\u263A\uFE0F", name: "Smiling Face", keywords: ["relaxed", "blush", "massage", "happiness"] },
4631
+ { emoji: "\u{1F61A}", name: "Kissing Face with Closed Eyes", keywords: ["love", "like", "affection", "valentines"] },
4632
+ { emoji: "\u{1F619}", name: "Kissing Face with Smiling Eyes", keywords: ["affection", "valentines", "infatuation", "kiss"] },
4633
+ { emoji: "\u{1F972}", name: "Smiling Face with Tear", keywords: ["sad", "cry", "pretend"] },
4634
+ { emoji: "\u{1F60B}", name: "Face Savoring Food", keywords: ["yum", "happy", "joy", "tongue"] },
4635
+ { emoji: "\u{1F61B}", name: "Face with Tongue", keywords: ["stuck", "out", "prank", "childish"] },
4636
+ { emoji: "\u{1F61C}", name: "Winking Face with Tongue", keywords: ["stuck", "out", "eye", "prank"] },
4637
+ { emoji: "\u{1F92A}", name: "Zany Face", keywords: ["grinning", "with", "one", "large"] },
4638
+ { emoji: "\u{1F61D}", name: "Squinting Face with Tongue", keywords: ["stuck", "out", "closed", "eyes"] },
4639
+ { emoji: "\u{1F911}", name: "Money-Mouth Face", keywords: ["money", "mouth", "rich", "dollar"] },
4640
+ { emoji: "\u{1F917}", name: "Hugging Face", keywords: ["smile", "hug"] },
4641
+ { emoji: "\u{1F92D}", name: "Face with Hand over Mouth", keywords: ["smiling", "eyes", "and", "covering"] },
4642
+ { emoji: "\u{1FAE2}", name: "Face with Open Eyes and Hand over Mouth", keywords: ["silence", "secret", "shock", "surprise"] },
4643
+ { emoji: "\u{1FAE3}", name: "Face with Peeking Eye", keywords: ["scared", "frightening", "embarrassing", "shy"] },
4644
+ { emoji: "\u{1F92B}", name: "Shushing Face", keywords: ["with", "finger", "covering", "closed"] },
4645
+ { emoji: "\u{1F914}", name: "Thinking Face", keywords: ["hmmm", "think", "consider"] },
4646
+ { emoji: "\u{1FAE1}", name: "Saluting Face", keywords: ["respect", "salute"] },
4647
+ { emoji: "\u{1F910}", name: "Zipper-Mouth Face", keywords: ["zipper", "mouth", "sealed", "secret"] },
4648
+ { emoji: "\u{1F928}", name: "Face with Raised Eyebrow", keywords: ["one", "distrust", "scepticism", "disapproval"] },
4649
+ { emoji: "\u{1F610}", name: "Neutral Face", keywords: ["indifference", "meh", ":", ""] },
4650
+ { emoji: "\u{1F611}", name: "Expressionless Face", keywords: ["indifferent", "-", "", "meh"] },
4651
+ { emoji: "\u{1F636}", name: "Face Without Mouth", keywords: ["no", "hellokitty"] },
4652
+ { emoji: "\u{1FAE5}", name: "Dotted Line Face", keywords: ["invisible", "lonely", "isolation", "depression"] },
4653
+ { emoji: "\u{1F636}\u200D\u{1F32B}\uFE0F", name: "Face in Clouds", keywords: ["shower", "steam", "dream"] },
4654
+ { emoji: "\u{1F60F}", name: "Smirking Face", keywords: ["smirk", "smile", "mean", "prank"] },
4655
+ { emoji: "\u{1F612}", name: "Unamused Face", keywords: ["indifference", "bored", "straight", "serious"] },
4656
+ { emoji: "\u{1F644}", name: "Face with Rolling Eyes", keywords: ["eyeroll", "frustrated"] },
4657
+ { emoji: "\u{1F62C}", name: "Grimacing Face", keywords: ["grimace", "teeth"] },
4658
+ { emoji: "\u{1F62E}\u200D\u{1F4A8}", name: "Face Exhaling", keywords: ["relieve", "relief", "tired", "sigh"] },
4659
+ { emoji: "\u{1F925}", name: "Lying Face", keywords: ["lie", "pinocchio"] },
4660
+ { emoji: "\u{1FAE8}", name: "Shaking Face", keywords: ["dizzy", "shock", "blurry", "earthquake"] },
4661
+ { emoji: "\u{1F60C}", name: "Relieved Face", keywords: ["relaxed", "phew", "massage", "happiness"] },
4662
+ { emoji: "\u{1F614}", name: "Pensive Face", keywords: ["sad", "depressed", "upset"] },
4663
+ { emoji: "\u{1F62A}", name: "Sleepy Face", keywords: ["tired", "rest", "nap"] },
4664
+ { emoji: "\u{1F924}", name: "Drooling Face", keywords: [] },
4665
+ { emoji: "\u{1F634}", name: "Sleeping Face", keywords: ["tired", "sleepy", "night", "zzz"] },
4666
+ { emoji: "\u{1F637}", name: "Face with Medical Mask", keywords: ["sick", "ill", "disease", "covid"] },
4667
+ { emoji: "\u{1F912}", name: "Face with Thermometer", keywords: ["sick", "temperature", "cold", "fever"] },
4668
+ { emoji: "\u{1F915}", name: "Face with Head-Bandage", keywords: ["head", "bandage", "injured", "clumsy"] },
4669
+ { emoji: "\u{1F922}", name: "Nauseated Face", keywords: ["vomit", "gross", "green", "sick"] },
4670
+ { emoji: "\u{1F92E}", name: "Face Vomiting", keywords: ["with", "open", "mouth", "sick"] },
4671
+ { emoji: "\u{1F927}", name: "Sneezing Face", keywords: ["gesundheit", "sneeze", "sick", "allergy"] },
4672
+ { emoji: "\u{1F975}", name: "Hot Face", keywords: ["feverish", "heat", "red", "sweating"] },
4673
+ { emoji: "\u{1F976}", name: "Cold Face", keywords: ["blue", "freezing", "frozen", "frostbite"] },
4674
+ { emoji: "\u{1F974}", name: "Woozy Face", keywords: ["dizzy", "intoxicated", "tipsy", "wavy"] }
4675
+ ]
4676
+ },
4677
+ {
4678
+ id: "nature",
4679
+ label: "Animals & nature",
4680
+ emojis: [
4681
+ { emoji: "\u{1F435}", name: "Monkey Face", keywords: ["animal", "nature", "circus"] },
4682
+ { emoji: "\u{1F412}", name: "Monkey", keywords: ["animal", "nature", "banana", "circus"] },
4683
+ { emoji: "\u{1F98D}", name: "Gorilla", keywords: ["animal", "nature", "circus"] },
4684
+ { emoji: "\u{1F9A7}", name: "Orangutan", keywords: ["animal"] },
4685
+ { emoji: "\u{1F436}", name: "Dog Face", keywords: ["animal", "friend", "nature", "woof"] },
4686
+ { emoji: "\u{1F415}", name: "Dog", keywords: ["dog2", "animal", "nature", "friend"] },
4687
+ { emoji: "\u{1F9AE}", name: "Guide Dog", keywords: ["animal", "blind"] },
4688
+ { emoji: "\u{1F415}\u200D\u{1F9BA}", name: "Service Dog", keywords: ["blind", "animal"] },
4689
+ { emoji: "\u{1F429}", name: "Poodle", keywords: ["dog", "animal", "101", "nature"] },
4690
+ { emoji: "\u{1F43A}", name: "Wolf", keywords: ["animal", "nature", "wild"] },
4691
+ { emoji: "\u{1F98A}", name: "Fox", keywords: ["face", "animal", "nature"] },
4692
+ { emoji: "\u{1F99D}", name: "Raccoon", keywords: ["animal", "nature"] },
4693
+ { emoji: "\u{1F431}", name: "Cat Face", keywords: ["animal", "meow", "nature", "pet"] },
4694
+ { emoji: "\u{1F408}", name: "Cat", keywords: ["cat2", "animal", "meow", "pet"] },
4695
+ { emoji: "\u{1F408}\u200D\u2B1B", name: "Black Cat", keywords: ["superstition", "luck"] },
4696
+ { emoji: "\u{1F981}", name: "Lion", keywords: ["face", "animal", "nature"] },
4697
+ { emoji: "\u{1F42F}", name: "Tiger Face", keywords: ["animal", "cat", "danger", "wild"] },
4698
+ { emoji: "\u{1F405}", name: "Tiger", keywords: ["tiger2", "animal", "nature", "roar"] },
4699
+ { emoji: "\u{1F406}", name: "Leopard", keywords: ["animal", "nature"] },
4700
+ { emoji: "\u{1F434}", name: "Horse Face", keywords: ["animal", "brown", "nature"] },
4701
+ { emoji: "\u{1FACE}", name: "Moose", keywords: ["shrek", "canada", "sweden", "sven"] },
4702
+ { emoji: "\u{1FACF}", name: "Donkey", keywords: ["eeyore", "mule"] },
4703
+ { emoji: "\u{1F40E}", name: "Horse", keywords: ["racehorse", "animal", "gamble", "luck"] },
4704
+ { emoji: "\u{1F984}", name: "Unicorn", keywords: ["face", "animal", "nature", "mystical"] },
4705
+ { emoji: "\u{1F993}", name: "Zebra", keywords: ["face", "animal", "nature", "stripes"] },
4706
+ { emoji: "\u{1F98C}", name: "Deer", keywords: ["animal", "nature", "horns", "venison"] },
4707
+ { emoji: "\u{1F9AC}", name: "Bison", keywords: ["ox"] },
4708
+ { emoji: "\u{1F42E}", name: "Cow Face", keywords: ["beef", "ox", "animal", "nature"] },
4709
+ { emoji: "\u{1F402}", name: "Ox", keywords: ["animal", "cow", "beef"] },
4710
+ { emoji: "\u{1F403}", name: "Water Buffalo", keywords: ["animal", "nature", "ox", "cow"] },
4711
+ { emoji: "\u{1F404}", name: "Cow", keywords: ["cow2", "beef", "ox", "animal"] },
4712
+ { emoji: "\u{1F437}", name: "Pig Face", keywords: ["animal", "oink", "nature"] },
4713
+ { emoji: "\u{1F416}", name: "Pig", keywords: ["pig2", "animal", "nature"] },
4714
+ { emoji: "\u{1F417}", name: "Boar", keywords: ["animal", "nature"] },
4715
+ { emoji: "\u{1F43D}", name: "Pig Nose", keywords: ["animal", "oink"] },
4716
+ { emoji: "\u{1F40F}", name: "Ram", keywords: ["animal", "sheep", "nature"] },
4717
+ { emoji: "\u{1F411}", name: "Ewe", keywords: ["sheep", "animal", "nature", "wool"] },
4718
+ { emoji: "\u{1F410}", name: "Goat", keywords: ["animal", "nature"] },
4719
+ { emoji: "\u{1F42A}", name: "Camel", keywords: ["dromedary", "animal", "hot", "desert"] },
4720
+ { emoji: "\u{1F42B}", name: "Bactrian Camel", keywords: ["two", "hump", "animal", "nature"] }
4721
+ ]
4722
+ },
4723
+ {
4724
+ id: "foods",
4725
+ label: "Food & drink",
4726
+ emojis: [
4727
+ { emoji: "\u{1F347}", name: "Grapes", keywords: ["fruit", "food", "wine"] },
4728
+ { emoji: "\u{1F348}", name: "Melon", keywords: ["fruit", "nature", "food"] },
4729
+ { emoji: "\u{1F349}", name: "Watermelon", keywords: ["fruit", "food", "picnic", "summer"] },
4730
+ { emoji: "\u{1F34A}", name: "Tangerine", keywords: ["food", "fruit", "nature", "orange"] },
4731
+ { emoji: "\u{1F34B}", name: "Lemon", keywords: ["fruit", "nature"] },
4732
+ { emoji: "\u{1F34C}", name: "Banana", keywords: ["fruit", "food", "monkey"] },
4733
+ { emoji: "\u{1F34D}", name: "Pineapple", keywords: ["fruit", "nature", "food"] },
4734
+ { emoji: "\u{1F96D}", name: "Mango", keywords: ["fruit", "food", "tropical"] },
4735
+ { emoji: "\u{1F34E}", name: "Red Apple", keywords: ["fruit", "mac", "school"] },
4736
+ { emoji: "\u{1F34F}", name: "Green Apple", keywords: ["fruit", "nature"] },
4737
+ { emoji: "\u{1F350}", name: "Pear", keywords: ["fruit", "nature", "food"] },
4738
+ { emoji: "\u{1F351}", name: "Peach", keywords: ["fruit", "nature", "food"] },
4739
+ { emoji: "\u{1F352}", name: "Cherries", keywords: ["food", "fruit"] },
4740
+ { emoji: "\u{1F353}", name: "Strawberry", keywords: ["fruit", "food", "nature"] },
4741
+ { emoji: "\u{1FAD0}", name: "Blueberries", keywords: ["fruit"] },
4742
+ { emoji: "\u{1F95D}", name: "Kiwifruit", keywords: ["kiwi", "fruit", "food"] },
4743
+ { emoji: "\u{1F345}", name: "Tomato", keywords: ["fruit", "vegetable", "nature", "food"] },
4744
+ { emoji: "\u{1FAD2}", name: "Olive", keywords: ["fruit"] },
4745
+ { emoji: "\u{1F965}", name: "Coconut", keywords: ["fruit", "nature", "food", "palm"] },
4746
+ { emoji: "\u{1F951}", name: "Avocado", keywords: ["fruit", "food"] },
4747
+ { emoji: "\u{1F346}", name: "Eggplant", keywords: ["vegetable", "nature", "food", "aubergine"] },
4748
+ { emoji: "\u{1F954}", name: "Potato", keywords: ["food", "tuber", "vegatable", "starch"] },
4749
+ { emoji: "\u{1F955}", name: "Carrot", keywords: ["vegetable", "food", "orange"] },
4750
+ { emoji: "\u{1F33D}", name: "Ear of Corn", keywords: ["food", "vegetable", "plant"] },
4751
+ { emoji: "\u{1F336}\uFE0F", name: "Hot Pepper", keywords: ["food", "spicy", "chilli", "chili"] },
4752
+ { emoji: "\u{1FAD1}", name: "Bell Pepper", keywords: ["fruit", "plant"] },
4753
+ { emoji: "\u{1F952}", name: "Cucumber", keywords: ["fruit", "food", "pickle"] },
4754
+ { emoji: "\u{1F96C}", name: "Leafy Green", keywords: ["food", "vegetable", "plant", "bok"] },
4755
+ { emoji: "\u{1F966}", name: "Broccoli", keywords: ["fruit", "food", "vegetable"] },
4756
+ { emoji: "\u{1F9C4}", name: "Garlic", keywords: ["food", "spice", "cook"] },
4757
+ { emoji: "\u{1F9C5}", name: "Onion", keywords: ["cook", "food", "spice"] },
4758
+ { emoji: "\u{1F95C}", name: "Peanuts", keywords: ["food", "nut"] },
4759
+ { emoji: "\u{1FAD8}", name: "Beans", keywords: ["food"] },
4760
+ { emoji: "\u{1F330}", name: "Chestnut", keywords: ["food", "squirrel"] },
4761
+ { emoji: "\u{1FADA}", name: "Ginger Root", keywords: ["spice", "yellow", "cooking", "gingerbread"] },
4762
+ { emoji: "\u{1FADB}", name: "Pea Pod", keywords: ["cozy", "green"] },
4763
+ { emoji: "\u{1F35E}", name: "Bread", keywords: ["food", "wheat", "breakfast", "toast"] },
4764
+ { emoji: "\u{1F950}", name: "Croissant", keywords: ["food", "bread", "french"] },
4765
+ { emoji: "\u{1F956}", name: "Baguette Bread", keywords: ["food", "french", "france", "bakery"] },
4766
+ { emoji: "\u{1FAD3}", name: "Flatbread", keywords: ["flour", "food", "bakery"] }
4767
+ ]
4768
+ },
4769
+ {
4770
+ id: "activity",
4771
+ label: "Activity",
4772
+ emojis: [
4773
+ { emoji: "\u{1F383}", name: "Jack-O-Lantern", keywords: ["jack", "o", "lantern", "halloween"] },
4774
+ { emoji: "\u{1F384}", name: "Christmas Tree", keywords: ["festival", "vacation", "december", "xmas"] },
4775
+ { emoji: "\u{1F386}", name: "Fireworks", keywords: ["photo", "festival", "carnival", "congratulations"] },
4776
+ { emoji: "\u{1F387}", name: "Sparkler", keywords: ["stars", "night", "shine"] },
4777
+ { emoji: "\u{1F9E8}", name: "Firecracker", keywords: ["dynamite", "boom", "explode", "explosion"] },
4778
+ { emoji: "\u2728", name: "Sparkles", keywords: ["stars", "shine", "shiny", "cool"] },
4779
+ { emoji: "\u{1F388}", name: "Balloon", keywords: ["party", "celebration", "birthday", "circus"] },
4780
+ { emoji: "\u{1F389}", name: "Party Popper", keywords: ["tada", "congratulations", "birthday", "magic"] },
4781
+ { emoji: "\u{1F38A}", name: "Confetti Ball", keywords: ["festival", "party", "birthday", "circus"] },
4782
+ { emoji: "\u{1F38B}", name: "Tanabata Tree", keywords: ["plant", "nature", "branch", "summer"] },
4783
+ { emoji: "\u{1F38D}", name: "Pine Decoration", keywords: ["bamboo", "japanese", "plant", "nature"] },
4784
+ { emoji: "\u{1F38E}", name: "Japanese Dolls", keywords: ["toy", "kimono"] },
4785
+ { emoji: "\u{1F38F}", name: "Carp Streamer", keywords: ["flags", "fish", "japanese", "koinobori"] },
4786
+ { emoji: "\u{1F390}", name: "Wind Chime", keywords: ["nature", "ding", "spring", "bell"] },
4787
+ { emoji: "\u{1F391}", name: "Moon Viewing Ceremony", keywords: ["rice", "scene", "photo", "japan"] },
4788
+ { emoji: "\u{1F9E7}", name: "Red Envelope", keywords: ["gift"] },
4789
+ { emoji: "\u{1F380}", name: "Ribbon", keywords: ["decoration", "pink", "girl", "bowtie"] },
4790
+ { emoji: "\u{1F381}", name: "Wrapped Gift", keywords: ["present", "birthday", "christmas", "xmas"] },
4791
+ { emoji: "\u{1F397}\uFE0F", name: "Reminder Ribbon", keywords: ["sports", "cause", "support", "awareness"] },
4792
+ { emoji: "\u{1F39F}\uFE0F", name: "Admission Tickets", keywords: ["sports", "concert", "entrance"] },
4793
+ { emoji: "\u{1F3AB}", name: "Ticket", keywords: ["event", "concert", "pass"] },
4794
+ { emoji: "\u{1F396}\uFE0F", name: "Military Medal", keywords: ["award", "winning", "army"] },
4795
+ { emoji: "\u{1F3C6}", name: "Trophy", keywords: ["win", "award", "contest", "place"] },
4796
+ { emoji: "\u{1F3C5}", name: "Sports Medal", keywords: ["award", "winning"] },
4797
+ { emoji: "\u{1F947}", name: "1st Place Medal", keywords: ["first", "award", "winning"] },
4798
+ { emoji: "\u{1F948}", name: "2nd Place Medal", keywords: ["second", "award"] },
4799
+ { emoji: "\u{1F949}", name: "3rd Place Medal", keywords: ["third", "award"] },
4800
+ { emoji: "\u26BD", name: "Soccer Ball", keywords: ["sports", "football"] },
4801
+ { emoji: "\u26BE", name: "Baseball", keywords: ["sports", "balls"] },
4802
+ { emoji: "\u{1F94E}", name: "Softball", keywords: ["sports", "balls"] },
4803
+ { emoji: "\u{1F3C0}", name: "Basketball", keywords: ["sports", "balls", "NBA"] },
4804
+ { emoji: "\u{1F3D0}", name: "Volleyball", keywords: ["sports", "balls"] }
4805
+ ]
4806
+ },
4807
+ {
4808
+ id: "places",
4809
+ label: "Travel & places",
4810
+ emojis: [
4811
+ { emoji: "\u{1F30D}", name: "Earth Globe Europe-Africa", keywords: ["africa", "showing", "europe", "world"] },
4812
+ { emoji: "\u{1F30E}", name: "Earth Globe Americas", keywords: ["showing", "world", "USA", "international"] },
4813
+ { emoji: "\u{1F30F}", name: "Earth Globe Asia-Australia", keywords: ["asia", "showing", "australia", "world"] },
4814
+ { emoji: "\u{1F310}", name: "Globe with Meridians", keywords: ["earth", "international", "world", "internet"] },
4815
+ { emoji: "\u{1F5FA}\uFE0F", name: "World Map", keywords: ["location", "direction"] },
4816
+ { emoji: "\u{1F5FE}", name: "Map of Japan", keywords: ["nation", "country", "japanese", "asia"] },
4817
+ { emoji: "\u{1F9ED}", name: "Compass", keywords: ["magnetic", "navigation", "orienteering"] },
4818
+ { emoji: "\u{1F3D4}\uFE0F", name: "Snow-Capped Mountain", keywords: ["snow", "capped", "photo", "nature"] },
4819
+ { emoji: "\u26F0\uFE0F", name: "Mountain", keywords: ["photo", "nature", "environment"] },
4820
+ { emoji: "\u{1F30B}", name: "Volcano", keywords: ["photo", "nature", "disaster"] },
4821
+ { emoji: "\u{1F5FB}", name: "Mount Fuji", keywords: ["photo", "mountain", "nature", "japanese"] },
4822
+ { emoji: "\u{1F3D5}\uFE0F", name: "Camping", keywords: ["photo", "outdoors", "tent"] },
4823
+ { emoji: "\u{1F3D6}\uFE0F", name: "Beach with Umbrella", keywords: ["weather", "summer", "sunny", "sand"] },
4824
+ { emoji: "\u{1F3DC}\uFE0F", name: "Desert", keywords: ["photo", "warm", "saharah"] },
4825
+ { emoji: "\u{1F3DD}\uFE0F", name: "Desert Island", keywords: ["photo", "tropical", "mojito"] },
4826
+ { emoji: "\u{1F3DE}\uFE0F", name: "National Park", keywords: ["photo", "environment", "nature"] },
4827
+ { emoji: "\u{1F3DF}\uFE0F", name: "Stadium", keywords: ["photo", "place", "sports", "concert"] },
4828
+ { emoji: "\u{1F3DB}\uFE0F", name: "Classical Building", keywords: ["art", "culture", "history"] },
4829
+ { emoji: "\u{1F3D7}\uFE0F", name: "Building Construction", keywords: ["wip", "working", "progress"] },
4830
+ { emoji: "\u{1F9F1}", name: "Brick", keywords: ["bricks"] },
4831
+ { emoji: "\u{1FAA8}", name: "Rock", keywords: ["stone"] },
4832
+ { emoji: "\u{1FAB5}", name: "Wood", keywords: ["nature", "timber", "trunk"] },
4833
+ { emoji: "\u{1F6D6}", name: "Hut", keywords: ["house", "structure"] },
4834
+ { emoji: "\u{1F3D8}\uFE0F", name: "Houses", keywords: ["house", "buildings", "photo"] },
4835
+ { emoji: "\u{1F3DA}\uFE0F", name: "Derelict House", keywords: ["building", "abandon", "evict", "broken"] },
4836
+ { emoji: "\u{1F3E0}", name: "House", keywords: ["building", "home"] },
4837
+ { emoji: "\u{1F3E1}", name: "House with Garden", keywords: ["home", "plant", "nature"] },
4838
+ { emoji: "\u{1F3E2}", name: "Office Building", keywords: ["bureau", "work"] },
4839
+ { emoji: "\u{1F3E3}", name: "Japanese Post Office", keywords: ["building", "envelope", "communication"] },
4840
+ { emoji: "\u{1F3E4}", name: "Post Office", keywords: ["european", "building", "email"] },
4841
+ { emoji: "\u{1F3E5}", name: "Hospital", keywords: ["building", "health", "surgery", "doctor"] },
4842
+ { emoji: "\u{1F3E6}", name: "Bank", keywords: ["building", "money", "sales", "cash"] }
4843
+ ]
4844
+ },
4845
+ {
4846
+ id: "objects",
4847
+ label: "Objects",
4848
+ emojis: [
4849
+ { emoji: "\u{1F453}", name: "Glasses", keywords: ["eyeglasses", "fashion", "accessories", "eyesight"] },
4850
+ { emoji: "\u{1F576}\uFE0F", name: "Sunglasses", keywords: ["dark", "face", "cool", "accessories"] },
4851
+ { emoji: "\u{1F97D}", name: "Goggles", keywords: ["eyes", "protection", "safety"] },
4852
+ { emoji: "\u{1F97C}", name: "Lab Coat", keywords: ["doctor", "experiment", "scientist", "chemist"] },
4853
+ { emoji: "\u{1F9BA}", name: "Safety Vest", keywords: ["protection"] },
4854
+ { emoji: "\u{1F454}", name: "Necktie", keywords: ["shirt", "suitup", "formal", "fashion"] },
4855
+ { emoji: "\u{1F455}", name: "T-Shirt", keywords: ["shirt", "tshirt", "t", "fashion"] },
4856
+ { emoji: "\u{1F456}", name: "Jeans", keywords: ["fashion", "shopping"] },
4857
+ { emoji: "\u{1F9E3}", name: "Scarf", keywords: ["neck", "winter", "clothes"] },
4858
+ { emoji: "\u{1F9E4}", name: "Gloves", keywords: ["hands", "winter", "clothes"] },
4859
+ { emoji: "\u{1F9E5}", name: "Coat", keywords: ["jacket"] },
4860
+ { emoji: "\u{1F9E6}", name: "Socks", keywords: ["stockings", "clothes"] },
4861
+ { emoji: "\u{1F457}", name: "Dress", keywords: ["clothes", "fashion", "shopping"] },
4862
+ { emoji: "\u{1F458}", name: "Kimono", keywords: ["dress", "fashion", "women", "female"] },
4863
+ { emoji: "\u{1F97B}", name: "Sari", keywords: ["dress"] },
4864
+ { emoji: "\u{1FA71}", name: "One-Piece Swimsuit", keywords: ["one", "piece", "fashion"] },
4865
+ { emoji: "\u{1FA72}", name: "Briefs", keywords: ["clothing"] },
4866
+ { emoji: "\u{1FA73}", name: "Shorts", keywords: ["clothing"] },
4867
+ { emoji: "\u{1F459}", name: "Bikini", keywords: ["swimming", "female", "woman", "girl"] },
4868
+ { emoji: "\u{1F45A}", name: "Womans Clothes", keywords: ["woman", "s", "fashion", "shopping"] },
4869
+ { emoji: "\u{1FAAD}", name: "Folding Hand Fan", keywords: ["flamenco", "hot"] },
4870
+ { emoji: "\u{1F45B}", name: "Purse", keywords: ["fashion", "accessories", "money", "sales"] },
4871
+ { emoji: "\u{1F45C}", name: "Handbag", keywords: ["fashion", "accessory", "accessories", "shopping"] },
4872
+ { emoji: "\u{1F45D}", name: "Pouch", keywords: ["clutch", "bag", "accessories", "shopping"] },
4873
+ { emoji: "\u{1F6CD}\uFE0F", name: "Shopping Bags", keywords: ["mall", "buy", "purchase"] },
4874
+ { emoji: "\u{1F392}", name: "Backpack", keywords: ["school", "satchel", "student", "education"] },
4875
+ { emoji: "\u{1FA74}", name: "Thong Sandal", keywords: ["footwear", "summer"] },
4876
+ { emoji: "\u{1F45E}", name: "Mans Shoe", keywords: ["man", "s", "fashion", "male"] },
4877
+ { emoji: "\u{1F45F}", name: "Running Shoe", keywords: ["athletic", "shoes", "sports", "sneakers"] },
4878
+ { emoji: "\u{1F97E}", name: "Hiking Boot", keywords: ["backpacking", "camping"] },
4879
+ { emoji: "\u{1F97F}", name: "Flat Shoe", keywords: ["womans", "ballet", "slip", "on"] },
4880
+ { emoji: "\u{1F460}", name: "High-Heeled Shoe", keywords: ["high", "heel", "heeled", "fashion"] },
4881
+ { emoji: "\u{1F461}", name: "Womans Sandal", keywords: ["woman", "s", "shoes", "fashion"] },
4882
+ { emoji: "\u{1FA70}", name: "Ballet Shoes", keywords: ["dance"] },
4883
+ { emoji: "\u{1F462}", name: "Womans Boots", keywords: ["boot", "woman", "s", "shoes"] },
4884
+ { emoji: "\u{1FAAE}", name: "Hair Pick", keywords: ["afro", "comb"] },
4885
+ { emoji: "\u{1F451}", name: "Crown", keywords: ["king", "kod", "leader", "royalty"] },
4886
+ { emoji: "\u{1F452}", name: "Womans Hat", keywords: ["woman", "s", "fashion", "accessories"] },
4887
+ { emoji: "\u{1F3A9}", name: "Top Hat", keywords: ["tophat", "magic", "gentleman", "classy"] },
4888
+ { emoji: "\u{1F393}", name: "Graduation Cap", keywords: ["mortar", "board", "school", "college"] },
4889
+ { emoji: "\u{1F9E2}", name: "Billed Cap", keywords: ["baseball"] },
4890
+ { emoji: "\u{1FA96}", name: "Military Helmet", keywords: ["army", "protection"] },
4891
+ { emoji: "\u26D1\uFE0F", name: "Rescue Worker\u2019s Helmet", keywords: ["with", "white", "cross", "worker"] },
4892
+ { emoji: "\u{1F4FF}", name: "Prayer Beads", keywords: ["dhikr", "religious"] }
4893
+ ]
4894
+ },
4895
+ {
4896
+ id: "symbols",
4897
+ label: "Symbols",
4898
+ emojis: [
4899
+ { emoji: "\u{1F3E7}", name: "Atm Sign", keywords: ["money", "sales", "cash", "blue"] },
4900
+ { emoji: "\u{1F6AE}", name: "Litter in Bin Sign", keywords: ["put", "its", "place", "blue"] },
4901
+ { emoji: "\u{1F6B0}", name: "Potable Water", keywords: ["blue", "square", "liquid", "restroom"] },
4902
+ { emoji: "\u267F", name: "Wheelchair Symbol", keywords: ["blue", "square", "disabled", "accessibility"] },
4903
+ { emoji: "\u{1F6B9}", name: "Men\u2019s Room", keywords: ["mens", "men", "s", "toilet"] },
4904
+ { emoji: "\u{1F6BA}", name: "Women\u2019s Room", keywords: ["womens", "women", "s", "purple"] },
4905
+ { emoji: "\u{1F6BB}", name: "Restroom", keywords: ["blue", "square", "toilet", "refresh"] },
4906
+ { emoji: "\u{1F6BC}", name: "Baby Symbol", keywords: ["orange", "square", "child"] },
4907
+ { emoji: "\u{1F6BE}", name: "Water Closet", keywords: ["wc", "toilet", "restroom", "blue"] },
4908
+ { emoji: "\u{1F6C2}", name: "Passport Control", keywords: ["custom", "blue", "square"] },
4909
+ { emoji: "\u{1F6C3}", name: "Customs", keywords: ["passport", "border", "blue", "square"] },
4910
+ { emoji: "\u{1F6C4}", name: "Baggage Claim", keywords: ["blue", "square", "airport", "transport"] },
4911
+ { emoji: "\u{1F6C5}", name: "Left Luggage", keywords: ["blue", "square", "travel"] },
4912
+ { emoji: "\u26A0\uFE0F", name: "Warning", keywords: ["exclamation", "wip", "alert", "error"] },
4913
+ { emoji: "\u{1F6B8}", name: "Children Crossing", keywords: ["school", "warning", "danger", "sign"] },
4914
+ { emoji: "\u26D4", name: "No Entry", keywords: ["limit", "security", "privacy", "bad"] },
4915
+ { emoji: "\u{1F6AB}", name: "Prohibited", keywords: ["no", "entry", "sign", "forbid"] },
4916
+ { emoji: "\u{1F6B3}", name: "No Bicycles", keywords: ["cyclist", "prohibited", "circle"] },
4917
+ { emoji: "\u{1F6AD}", name: "No Smoking", keywords: ["cigarette", "blue", "square", "smell"] },
4918
+ { emoji: "\u{1F6AF}", name: "No Littering", keywords: ["do", "not", "litter", "trash"] },
4919
+ { emoji: "\u{1F6B1}", name: "Non-Potable Water", keywords: ["non", "potable", "drink", "faucet"] },
4920
+ { emoji: "\u{1F6B7}", name: "No Pedestrians", keywords: ["rules", "crossing", "walking", "circle"] },
4921
+ { emoji: "\u{1F4F5}", name: "No Mobile Phones", keywords: ["iphone", "mute", "circle"] },
4922
+ { emoji: "\u{1F51E}", name: "No One Under Eighteen", keywords: ["underage", "18", "drink", "pub"] },
4923
+ { emoji: "\u2622\uFE0F", name: "Radioactive", keywords: ["sign", "nuclear", "danger"] },
4924
+ { emoji: "\u2623\uFE0F", name: "Biohazard", keywords: ["sign", "danger"] },
4925
+ { emoji: "\u2B06\uFE0F", name: "Up Arrow", keywords: ["blue", "square", "continue", "top"] },
4926
+ { emoji: "\u2197\uFE0F", name: "Up-Right Arrow", keywords: ["upper", "right", "up", "blue"] },
4927
+ { emoji: "\u27A1\uFE0F", name: "Right Arrow", keywords: ["blue", "square", "next"] },
4928
+ { emoji: "\u2198\uFE0F", name: "South East Arrow", keywords: ["lower", "right", "down", "blue"] },
4929
+ { emoji: "\u2B07\uFE0F", name: "Down Arrow", keywords: ["blue", "square", "direction", "bottom"] },
4930
+ { emoji: "\u2199\uFE0F", name: "Down-Left Arrow", keywords: ["lower", "left", "down", "blue"] }
4931
+ ]
4932
+ },
4933
+ {
4934
+ id: "flags",
4935
+ label: "Flags",
4936
+ emojis: [
4937
+ { emoji: "\u{1F3C1}", name: "Chequered Flag", keywords: ["checkered", "contest", "finishline", "race"] },
4938
+ { emoji: "\u{1F1E8}\u{1F1F3}", name: "China Flag", keywords: ["cn", "chinese", "prc", "country"] },
4939
+ { emoji: "\u{1F38C}", name: "Crossed Flags", keywords: ["japanese", "nation", "country", "border"] },
4940
+ { emoji: "\u{1F1E9}\u{1F1EA}", name: "Germany Flag", keywords: ["de", "german", "nation", "country"] },
4941
+ { emoji: "\u{1F1EA}\u{1F1F8}", name: "Spain Flag", keywords: ["es", "nation", "country", "banner"] },
4942
+ { emoji: "\u{1F1E6}\u{1F1E8}", name: "Ascension Island Flag", keywords: ["ac"] },
4943
+ { emoji: "\u{1F1E6}\u{1F1E9}", name: "Andorra Flag", keywords: ["ad", "nation", "country", "banner"] },
4944
+ { emoji: "\u{1F1E6}\u{1F1EA}", name: "United Arab Emirates Flag", keywords: ["ae", "nation", "country", "banner"] },
4945
+ { emoji: "\u{1F1E6}\u{1F1EB}", name: "Afghanistan Flag", keywords: ["af", "nation", "country", "banner"] },
4946
+ { emoji: "\u{1F1E6}\u{1F1EC}", name: "Antigua & Barbuda Flag", keywords: ["ag", "nation", "country", "banner"] },
4947
+ { emoji: "\u{1F1E6}\u{1F1EE}", name: "Anguilla Flag", keywords: ["ai", "nation", "country", "banner"] },
4948
+ { emoji: "\u{1F1E6}\u{1F1F1}", name: "Albania Flag", keywords: ["al", "nation", "country", "banner"] }
4949
+ ]
4950
+ }
4951
+ ];
4952
+ function matches(def, query) {
4953
+ if (def.name.toLowerCase().includes(query)) return true;
4954
+ return def.keywords.some((k2) => k2.toLowerCase().includes(query));
4955
+ }
4956
+ var EmojiPicker = forwardRef(
4957
+ function EmojiPicker2({
4958
+ onSelect,
4959
+ groups = EMOJI_GROUPS,
4960
+ columns = 8,
4961
+ searchable = true,
4962
+ searchPlaceholder = "Search emoji",
4963
+ emptyText = "No emoji found",
4964
+ className,
4965
+ ...props
4966
+ }, ref) {
4967
+ const [query, setQuery] = useState("");
4968
+ const q = query.trim().toLowerCase();
4969
+ const results = useMemo(() => {
4970
+ if (!q) return null;
4971
+ return groups.flatMap((g) => g.emojis).filter((d) => matches(d, q));
4972
+ }, [groups, q]);
4973
+ const gridStyle = {
4974
+ gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`
4975
+ };
4976
+ const cell = (def) => /* @__PURE__ */ jsx(
4977
+ "button",
4978
+ {
4979
+ type: "button",
4980
+ className: "klun-emoji-picker__cell",
4981
+ title: def.name,
4982
+ "aria-label": def.name,
4983
+ onClick: () => onSelect(def.emoji, def),
4984
+ children: def.emoji
4985
+ },
4986
+ def.emoji
4987
+ );
4988
+ return /* @__PURE__ */ jsxs("div", { ref, className: cx("klun-emoji-picker", className), ...props, children: [
4989
+ searchable && /* @__PURE__ */ jsxs("label", { className: "klun-emoji-picker__search", children: [
4990
+ /* @__PURE__ */ jsx("i", { className: "ri-search-line", "aria-hidden": "true" }),
4991
+ /* @__PURE__ */ jsx(
4992
+ "input",
4993
+ {
4994
+ value: query,
4995
+ placeholder: searchPlaceholder,
4996
+ "aria-label": searchPlaceholder,
4997
+ onChange: (e) => setQuery(e.target.value)
4998
+ }
4999
+ )
5000
+ ] }),
5001
+ /* @__PURE__ */ jsx("div", { className: "klun-emoji-picker__body", children: results ? results.length ? /* @__PURE__ */ jsx("div", { className: "klun-emoji-picker__grid", style: gridStyle, children: results.map(cell) }) : /* @__PURE__ */ jsx("div", { className: "klun-emoji-picker__empty", children: emptyText }) : groups.map((group) => /* @__PURE__ */ jsxs("div", { className: "klun-emoji-picker__group", children: [
5002
+ /* @__PURE__ */ jsx("div", { className: "klun-emoji-picker__group-title", children: group.label }),
5003
+ /* @__PURE__ */ jsx("div", { className: "klun-emoji-picker__grid", style: gridStyle, children: group.emojis.map(cell) })
5004
+ ] }, group.id)) })
5005
+ ] });
5006
+ }
5007
+ );
5008
+ EmojiPicker.displayName = "EmojiPicker";
5009
+ var QUICK_EMOJIS = ["\u{1F44D}", "\u{1F44C}", "\u2764\uFE0F", "\u2705", "\u{1F389}", "\u{1F615}", "\u{1F680}", "\u{1F440}"];
5010
+ var QuickEmojiPicker = forwardRef(
5011
+ function QuickEmojiPicker2({ onSelect, emojis = QUICK_EMOJIS, align = "left", label = "Add reaction", className }, ref) {
5012
+ return /* @__PURE__ */ jsx(
5013
+ Popover,
5014
+ {
5015
+ align,
5016
+ width: 304,
5017
+ trigger: /* @__PURE__ */ jsx(
5018
+ "button",
5019
+ {
5020
+ ref,
5021
+ type: "button",
5022
+ className: cx("klun-quick-emoji-picker", className),
5023
+ "aria-label": label,
5024
+ title: label,
5025
+ children: /* @__PURE__ */ jsx("i", { className: "ri-emotion-add-line", "aria-hidden": "true" })
5026
+ }
5027
+ ),
5028
+ children: (close) => (
5029
+ // Mounted per open, so the quick/full state resets like the original.
5030
+ /* @__PURE__ */ jsx(
5031
+ QuickPanel,
5032
+ {
5033
+ emojis,
5034
+ onSelect: (emoji) => {
5035
+ onSelect(emoji);
5036
+ close();
5037
+ }
5038
+ }
5039
+ )
5040
+ )
5041
+ }
5042
+ );
5043
+ }
5044
+ );
5045
+ QuickEmojiPicker.displayName = "QuickEmojiPicker";
5046
+ function QuickPanel({
5047
+ emojis,
5048
+ onSelect
5049
+ }) {
5050
+ const [expanded, setExpanded] = useState(false);
5051
+ if (expanded) {
5052
+ return /* @__PURE__ */ jsx(EmojiPicker, { onSelect: (emoji) => onSelect(emoji) });
5053
+ }
5054
+ return /* @__PURE__ */ jsxs("div", { className: "klun-quick-emoji-picker__panel", children: [
5055
+ /* @__PURE__ */ jsx("div", { className: "klun-quick-emoji-picker__row", children: emojis.map((emoji) => /* @__PURE__ */ jsx(
5056
+ "button",
5057
+ {
5058
+ type: "button",
5059
+ className: "klun-quick-emoji-picker__cell",
5060
+ onClick: () => onSelect(emoji),
5061
+ children: emoji
5062
+ },
5063
+ emoji
5064
+ )) }),
5065
+ /* @__PURE__ */ jsx(
5066
+ "button",
5067
+ {
5068
+ type: "button",
5069
+ className: "klun-quick-emoji-picker__more",
5070
+ onClick: () => setExpanded(true),
5071
+ children: "More emojis\u2026"
5072
+ }
5073
+ )
5074
+ ] });
5075
+ }
5076
+ var GAP = 6;
5077
+ var MARGIN = 8;
5078
+ var Tooltip = forwardRef(function Tooltip2({ content, side = "top", delay = 200, disabled = false, children, style, className }, ref) {
5079
+ const [open, setOpen] = useState(false);
5080
+ const [pos, setPos] = useState(null);
5081
+ const timer = useRef(null);
5082
+ const innerRef = useRef(null);
5083
+ const bubbleRef = useRef(null);
5084
+ const show = () => {
5085
+ if (disabled || !content) return;
5086
+ if (timer.current) clearTimeout(timer.current);
5087
+ timer.current = setTimeout(() => setOpen(true), delay);
5088
+ };
5089
+ const hide = () => {
5090
+ if (timer.current) clearTimeout(timer.current);
5091
+ setOpen(false);
5092
+ };
5093
+ useEffect(() => () => {
5094
+ if (timer.current) clearTimeout(timer.current);
5095
+ }, []);
5096
+ useLayoutEffect(() => {
5097
+ if (!open) {
5098
+ setPos(null);
5099
+ return;
5100
+ }
5101
+ const place2 = () => {
5102
+ const trigger = innerRef.current;
5103
+ const bubble = bubbleRef.current;
5104
+ if (!trigger || !bubble) return;
5105
+ const tr = trigger.getBoundingClientRect();
5106
+ const bw = bubble.offsetWidth;
5107
+ const bh = bubble.offsetHeight;
5108
+ const vw = document.documentElement.clientWidth;
5109
+ const vh = document.documentElement.clientHeight;
5110
+ let top;
5111
+ let left;
5112
+ switch (side) {
5113
+ case "bottom":
5114
+ top = tr.bottom + GAP;
5115
+ left = tr.left + tr.width / 2 - bw / 2;
5116
+ break;
5117
+ case "left":
5118
+ left = tr.left - bw - GAP;
5119
+ top = tr.top + tr.height / 2 - bh / 2;
5120
+ break;
5121
+ case "right":
5122
+ left = tr.right + GAP;
5123
+ top = tr.top + tr.height / 2 - bh / 2;
5124
+ break;
5125
+ default:
5126
+ top = tr.top - bh - GAP;
5127
+ left = tr.left + tr.width / 2 - bw / 2;
5128
+ }
5129
+ left = Math.max(MARGIN, Math.min(left, vw - bw - MARGIN));
5130
+ top = Math.max(MARGIN, Math.min(top, vh - bh - MARGIN));
5131
+ setPos({ top, left });
5132
+ };
5133
+ place2();
5134
+ window.addEventListener("scroll", place2, true);
5135
+ window.addEventListener("resize", place2);
5136
+ return () => {
5137
+ window.removeEventListener("scroll", place2, true);
5138
+ window.removeEventListener("resize", place2);
5139
+ };
5140
+ }, [open, side, content]);
5141
+ const setRefs = (node) => {
5142
+ innerRef.current = node;
5143
+ if (typeof ref === "function") ref(node);
5144
+ else if (ref) ref.current = node;
5145
+ };
5146
+ const bubbleStyle = {
5147
+ ...style,
5148
+ position: "fixed",
5149
+ top: pos ? pos.top : 0,
5150
+ left: pos ? pos.left : 0,
5151
+ right: "auto",
5152
+ bottom: "auto",
5153
+ transform: "none",
5154
+ visibility: pos ? "visible" : "hidden"
5155
+ };
5156
+ return /* @__PURE__ */ jsxs(
5157
+ "span",
5158
+ {
5159
+ ref: setRefs,
5160
+ className: cx("klun-tooltip", className),
5161
+ onMouseEnter: show,
5162
+ onMouseLeave: hide,
5163
+ onFocus: show,
5164
+ onBlur: hide,
5165
+ children: [
5166
+ children,
5167
+ open && content && typeof document !== "undefined" ? createPortal(
5168
+ /* @__PURE__ */ jsx(
5169
+ "span",
5170
+ {
5171
+ ref: bubbleRef,
5172
+ role: "tooltip",
5173
+ className: cx("klun-tooltip__bubble", `klun-tooltip__bubble--${side}`),
5174
+ style: bubbleStyle,
5175
+ children: content
5176
+ }
5177
+ ),
5178
+ document.body
5179
+ ) : null
5180
+ ]
5181
+ }
5182
+ );
5183
+ });
5184
+ Tooltip.displayName = "Tooltip";
5185
+ function groupReactions(items2, currentActorId) {
5186
+ const map = /* @__PURE__ */ new Map();
5187
+ for (const item of items2) {
5188
+ let group = map.get(item.emoji);
5189
+ if (!group) {
5190
+ group = { emoji: item.emoji, count: 0, reacted: false, actors: [] };
5191
+ map.set(item.emoji, group);
5192
+ }
5193
+ group.count += 1;
5194
+ group.actors.push(item.actorName ?? item.actorId);
5195
+ if (item.actorId === currentActorId) group.reacted = true;
5196
+ }
5197
+ return Array.from(map.values());
5198
+ }
5199
+ var ReactionBar = forwardRef(
5200
+ function ReactionBar2({ reactions, onToggle, showPicker = true, pickerEmojis, className, ...props }, ref) {
5201
+ return /* @__PURE__ */ jsxs("div", { ref, className: cx("klun-reaction-bar", className), ...props, children: [
5202
+ reactions.map((reaction) => /* @__PURE__ */ jsx(
5203
+ Tooltip,
5204
+ {
5205
+ side: "top",
5206
+ disabled: reaction.actors.length === 0,
5207
+ content: reaction.actors.join(", "),
5208
+ children: /* @__PURE__ */ jsxs(
5209
+ "button",
5210
+ {
5211
+ type: "button",
5212
+ className: "klun-reaction",
5213
+ "data-reacted": reaction.reacted ? "" : void 0,
5214
+ "aria-pressed": reaction.reacted,
5215
+ "aria-label": `${reaction.emoji} ${reaction.count}`,
5216
+ onClick: () => onToggle?.(reaction.emoji),
5217
+ children: [
5218
+ /* @__PURE__ */ jsx("span", { className: "klun-reaction__emoji", children: reaction.emoji }),
5219
+ /* @__PURE__ */ jsx("span", { className: "klun-reaction__count", children: reaction.count })
5220
+ ]
5221
+ }
5222
+ )
5223
+ },
5224
+ reaction.emoji
5225
+ )),
5226
+ showPicker && /* @__PURE__ */ jsx(QuickEmojiPicker, { emojis: pickerEmojis, onSelect: (emoji) => onToggle?.(emoji) })
5227
+ ] });
5228
+ }
5229
+ );
5230
+ ReactionBar.displayName = "ReactionBar";
5231
+ var ShimmerText = forwardRef(
5232
+ function ShimmerText2({ children, active = true, className, ...props }, ref) {
5233
+ return /* @__PURE__ */ jsxs(
5234
+ "span",
5235
+ {
5236
+ ref,
5237
+ className: cx("klun-shimmer-text", !active && "klun-shimmer-text--static", className),
5238
+ ...props,
5239
+ children: [
5240
+ children,
5241
+ active && /* @__PURE__ */ jsx("span", { className: "klun-shimmer-text__window", "aria-hidden": "true", children: /* @__PURE__ */ jsx("span", { className: "klun-shimmer-text__copy", children }) })
5242
+ ]
5243
+ }
5244
+ );
5245
+ }
5246
+ );
5247
+ ShimmerText.displayName = "ShimmerText";
5248
+ var StatCard = forwardRef(function StatCard2({ label, value, delta, trend = "up", icon, tint = "feature", className, ...props }, ref) {
5249
+ const up = trend === "up";
5250
+ return /* @__PURE__ */ jsxs("div", { ref, className: cx("klun-stat-card", `klun-stat-card--${tint}`, className), ...props, children: [
5251
+ /* @__PURE__ */ jsxs("div", { className: "klun-stat-card__head", children: [
5252
+ /* @__PURE__ */ jsx("span", { className: "klun-stat-card__icon", children: icon }),
5253
+ delta != null ? /* @__PURE__ */ jsxs(
5254
+ "span",
5255
+ {
5256
+ className: cx(
4015
5257
  "klun-stat-card__delta",
4016
5258
  up ? "klun-stat-card__delta--up" : "klun-stat-card__delta--down"
4017
5259
  ),
@@ -4451,7 +5693,8 @@ function SortIcon({ dir }) {
4451
5693
  function Check({
4452
5694
  checked,
4453
5695
  indeterminate,
4454
- onChange
5696
+ onChange,
5697
+ label
4455
5698
  }) {
4456
5699
  const active = checked || indeterminate;
4457
5700
  return /* @__PURE__ */ jsx(
@@ -4459,6 +5702,7 @@ function Check({
4459
5702
  {
4460
5703
  type: "button",
4461
5704
  role: "checkbox",
5705
+ "aria-label": label,
4462
5706
  "aria-checked": indeterminate ? "mixed" : checked,
4463
5707
  onClick: (e) => {
4464
5708
  e.stopPropagation();
@@ -4631,7 +5875,15 @@ function TableHead({
4631
5875
  leftFixed.length && "klun-table__cell--fixed klun-table__cell--fixed-head"
4632
5876
  ),
4633
5877
  style: leftFixed.length ? { left: expandCol ? 44 : 0 } : void 0,
4634
- children: selectionType === "radio" ? null : /* @__PURE__ */ jsx(Check, { checked: allOn, indeterminate: someOn, onChange: toggleAll })
5878
+ children: selectionType === "radio" ? null : /* @__PURE__ */ jsx(
5879
+ Check,
5880
+ {
5881
+ checked: allOn,
5882
+ indeterminate: someOn,
5883
+ onChange: toggleAll,
5884
+ label: table.selectAll ?? "Select all rows"
5885
+ }
5886
+ )
4635
5887
  }
4636
5888
  ) : null,
4637
5889
  visibleColumns.map((c, ci) => {
@@ -4877,6 +6129,7 @@ function RowBase({
4877
6129
  {
4878
6130
  type: "button",
4879
6131
  role: "radio",
6132
+ "aria-label": table.selectRow ?? "Select row",
4880
6133
  "aria-checked": on,
4881
6134
  onClick: (e) => {
4882
6135
  e.stopPropagation();
@@ -4885,7 +6138,14 @@ function RowBase({
4885
6138
  className: cx("klun-table__radio", on && "klun-table__radio--on"),
4886
6139
  children: on ? /* @__PURE__ */ jsx("span", { className: "klun-table__radio-dot" }) : null
4887
6140
  }
4888
- ) : /* @__PURE__ */ jsx(Check, { checked: on, onChange: () => onSelectRow?.(id) })
6141
+ ) : /* @__PURE__ */ jsx(
6142
+ Check,
6143
+ {
6144
+ checked: on,
6145
+ onChange: () => onSelectRow?.(id),
6146
+ label: table.selectRow ?? "Select row"
6147
+ }
6148
+ )
4889
6149
  }
4890
6150
  ) : null,
4891
6151
  visibleColumns.map((c, ci) => {
@@ -5728,115 +6988,6 @@ var Timeline = forwardRef(function Timeline2({ items: items2 = [], mode = "left"
5728
6988
  }) });
5729
6989
  });
5730
6990
  Timeline.displayName = "Timeline";
5731
- var GAP = 6;
5732
- var MARGIN = 8;
5733
- var Tooltip = forwardRef(function Tooltip2({ content, side = "top", delay = 200, disabled = false, children, style, className }, ref) {
5734
- const [open, setOpen] = useState(false);
5735
- const [pos, setPos] = useState(null);
5736
- const timer = useRef(null);
5737
- const innerRef = useRef(null);
5738
- const bubbleRef = useRef(null);
5739
- const show = () => {
5740
- if (disabled || !content) return;
5741
- if (timer.current) clearTimeout(timer.current);
5742
- timer.current = setTimeout(() => setOpen(true), delay);
5743
- };
5744
- const hide = () => {
5745
- if (timer.current) clearTimeout(timer.current);
5746
- setOpen(false);
5747
- };
5748
- useEffect(() => () => {
5749
- if (timer.current) clearTimeout(timer.current);
5750
- }, []);
5751
- useLayoutEffect(() => {
5752
- if (!open) {
5753
- setPos(null);
5754
- return;
5755
- }
5756
- const place2 = () => {
5757
- const trigger = innerRef.current;
5758
- const bubble = bubbleRef.current;
5759
- if (!trigger || !bubble) return;
5760
- const tr = trigger.getBoundingClientRect();
5761
- const bw = bubble.offsetWidth;
5762
- const bh = bubble.offsetHeight;
5763
- const vw = document.documentElement.clientWidth;
5764
- const vh = document.documentElement.clientHeight;
5765
- let top;
5766
- let left;
5767
- switch (side) {
5768
- case "bottom":
5769
- top = tr.bottom + GAP;
5770
- left = tr.left + tr.width / 2 - bw / 2;
5771
- break;
5772
- case "left":
5773
- left = tr.left - bw - GAP;
5774
- top = tr.top + tr.height / 2 - bh / 2;
5775
- break;
5776
- case "right":
5777
- left = tr.right + GAP;
5778
- top = tr.top + tr.height / 2 - bh / 2;
5779
- break;
5780
- default:
5781
- top = tr.top - bh - GAP;
5782
- left = tr.left + tr.width / 2 - bw / 2;
5783
- }
5784
- left = Math.max(MARGIN, Math.min(left, vw - bw - MARGIN));
5785
- top = Math.max(MARGIN, Math.min(top, vh - bh - MARGIN));
5786
- setPos({ top, left });
5787
- };
5788
- place2();
5789
- window.addEventListener("scroll", place2, true);
5790
- window.addEventListener("resize", place2);
5791
- return () => {
5792
- window.removeEventListener("scroll", place2, true);
5793
- window.removeEventListener("resize", place2);
5794
- };
5795
- }, [open, side, content]);
5796
- const setRefs = (node) => {
5797
- innerRef.current = node;
5798
- if (typeof ref === "function") ref(node);
5799
- else if (ref) ref.current = node;
5800
- };
5801
- const bubbleStyle = {
5802
- ...style,
5803
- position: "fixed",
5804
- top: pos ? pos.top : 0,
5805
- left: pos ? pos.left : 0,
5806
- right: "auto",
5807
- bottom: "auto",
5808
- transform: "none",
5809
- visibility: pos ? "visible" : "hidden"
5810
- };
5811
- return /* @__PURE__ */ jsxs(
5812
- "span",
5813
- {
5814
- ref: setRefs,
5815
- className: cx("klun-tooltip", className),
5816
- onMouseEnter: show,
5817
- onMouseLeave: hide,
5818
- onFocus: show,
5819
- onBlur: hide,
5820
- children: [
5821
- children,
5822
- open && content && typeof document !== "undefined" ? createPortal(
5823
- /* @__PURE__ */ jsx(
5824
- "span",
5825
- {
5826
- ref: bubbleRef,
5827
- role: "tooltip",
5828
- className: cx("klun-tooltip__bubble", `klun-tooltip__bubble--${side}`),
5829
- style: bubbleStyle,
5830
- children: content
5831
- }
5832
- ),
5833
- document.body
5834
- ) : null
5835
- ]
5836
- }
5837
- );
5838
- });
5839
- Tooltip.displayName = "Tooltip";
5840
6991
  function flatten(nodes, level, parentExpanded, expanded, out) {
5841
6992
  for (const n of nodes) {
5842
6993
  const isExpanded = expanded.includes(n.key);
@@ -5967,6 +7118,72 @@ var Tree = forwardRef(function Tree2({
5967
7118
  );
5968
7119
  });
5969
7120
  Tree.displayName = "Tree";
7121
+ function VirtualTable({
7122
+ rows = [],
7123
+ columns = [],
7124
+ rowHeight = 40,
7125
+ height = 420,
7126
+ getRowId,
7127
+ onRowClick,
7128
+ overscan = 8,
7129
+ zebra = true,
7130
+ empty,
7131
+ className,
7132
+ style,
7133
+ ...props
7134
+ }) {
7135
+ const [scrollTop, setScrollTop] = useState(0);
7136
+ const total = rows.length;
7137
+ const template = useMemo(
7138
+ () => columns.map((c) => c.width ? `${c.width}px` : "minmax(0, 1fr)").join(" "),
7139
+ [columns]
7140
+ );
7141
+ const bodyHeight = Math.max(0, height - 36);
7142
+ const start = Math.max(0, Math.floor(scrollTop / rowHeight) - overscan);
7143
+ const end = Math.min(total, Math.ceil((scrollTop + bodyHeight) / rowHeight) + overscan);
7144
+ const slice = rows.slice(start, end);
7145
+ return /* @__PURE__ */ jsxs("div", { ref: void 0, className: cx("klun-vtable", className), style: { ...style, height }, ...props, children: [
7146
+ /* @__PURE__ */ jsx("div", { className: "klun-vtable__head", style: { gridTemplateColumns: template }, children: columns.map((c) => /* @__PURE__ */ jsx("div", { className: cx("klun-vtable__th", c.align && `klun-vtable__th--${c.align}`), children: c.header }, c.key)) }),
7147
+ /* @__PURE__ */ jsx(
7148
+ "div",
7149
+ {
7150
+ className: "klun-vtable__body",
7151
+ onScroll: (e) => setScrollTop(e.target.scrollTop),
7152
+ style: { maxHeight: bodyHeight },
7153
+ children: total === 0 ? /* @__PURE__ */ jsx("div", { className: "klun-vtable__empty", children: empty ?? "No rows" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
7154
+ /* @__PURE__ */ jsx("div", { style: { height: start * rowHeight } }),
7155
+ slice.map((row, i) => {
7156
+ const index = start + i;
7157
+ const id = getRowId?.(row, index) ?? index;
7158
+ return /* @__PURE__ */ jsx(
7159
+ "div",
7160
+ {
7161
+ role: onRowClick ? "button" : void 0,
7162
+ tabIndex: onRowClick ? 0 : void 0,
7163
+ onClick: () => onRowClick?.(row, index),
7164
+ className: cx(
7165
+ "klun-vtable__row",
7166
+ zebra && index % 2 === 1 && "klun-vtable__row--zebra",
7167
+ onRowClick && "klun-vtable__row--clickable"
7168
+ ),
7169
+ style: { gridTemplateColumns: template, height: rowHeight },
7170
+ children: columns.map((c) => /* @__PURE__ */ jsx("div", { className: cx("klun-vtable__td", c.align && `klun-vtable__td--${c.align}`), children: c.render ? c.render(row, index) : String(row[c.key] ?? "") }, c.key))
7171
+ },
7172
+ id
7173
+ );
7174
+ }),
7175
+ /* @__PURE__ */ jsx("div", { style: { height: Math.max(0, (total - end) * rowHeight) } })
7176
+ ] })
7177
+ }
7178
+ ),
7179
+ /* @__PURE__ */ jsxs("div", { className: "klun-vtable__foot", children: [
7180
+ total.toLocaleString("en-US"),
7181
+ " rows \xB7 rendering ",
7182
+ slice.length
7183
+ ] })
7184
+ ] });
7185
+ }
7186
+ VirtualTable.displayName = "VirtualTable";
5970
7187
  var Accordion = forwardRef(function Accordion2({ items: items2 = [], defaultOpen, multiple = false, className, ...props }, ref) {
5971
7188
  const [open, setOpen] = useState(() => defaultOpen != null ? [defaultOpen] : []);
5972
7189
  const toggle = (id) => setOpen((cur) => {
@@ -6095,6 +7312,74 @@ var Banner = forwardRef(function Banner2({ status = "information", variant = "fi
6095
7312
  );
6096
7313
  });
6097
7314
  Banner.displayName = "Banner";
7315
+ var BorderBeam = forwardRef(
7316
+ function BorderBeam2({ className, ...props }, ref) {
7317
+ return /* @__PURE__ */ jsx(
7318
+ "span",
7319
+ {
7320
+ ref,
7321
+ "aria-hidden": "true",
7322
+ className: cx("klun-border-beam", className),
7323
+ ...props,
7324
+ children: /* @__PURE__ */ jsx("span", { className: "klun-border-beam__light" })
7325
+ }
7326
+ );
7327
+ }
7328
+ );
7329
+ BorderBeam.displayName = "BorderBeam";
7330
+ var RESOURCE_NOUN = {
7331
+ agent: "agent",
7332
+ skill: "skill",
7333
+ comment: "comment",
7334
+ runtime: "runtime",
7335
+ workspace: "workspace"
7336
+ };
7337
+ function getCopy(reason, noun, ownerName) {
7338
+ switch (reason) {
7339
+ case "not_authenticated":
7340
+ return `Sign in to edit this ${noun}.`;
7341
+ case "not_member":
7342
+ return `Join this workspace to edit this ${noun}.`;
7343
+ case "not_owner_role":
7344
+ return `View only \u2014 only the workspace owner can manage this ${noun}.`;
7345
+ case "not_admin_role":
7346
+ return `View only \u2014 only workspace owners and admins can manage this ${noun}.`;
7347
+ case "not_resource_owner":
7348
+ if (ownerName) {
7349
+ return `View only \u2014 only ${ownerName} and workspace admins can edit this ${noun}.`;
7350
+ }
7351
+ return `View only \u2014 only the ${noun} owner and workspace admins can edit this ${noun}.`;
7352
+ case "last_owner":
7353
+ return `A workspace must keep at least one owner \u2014 promote another member first.`;
7354
+ case "private_visibility":
7355
+ if (ownerName) {
7356
+ return `Personal ${noun} \u2014 only ${ownerName} and workspace admins can use this.`;
7357
+ }
7358
+ return `Personal ${noun} \u2014 only the owner and workspace admins can use this.`;
7359
+ case "allowed":
7360
+ case "unknown":
7361
+ return "";
7362
+ }
7363
+ }
7364
+ var CapabilityBanner = forwardRef(
7365
+ function CapabilityBanner2({ reason, resource, ownerName, message, className, ...props }, ref) {
7366
+ if (reason === "allowed" || reason === "unknown") return null;
7367
+ return /* @__PURE__ */ jsxs(
7368
+ "div",
7369
+ {
7370
+ ref,
7371
+ role: "status",
7372
+ className: cx("klun-capability-banner", className),
7373
+ ...props,
7374
+ children: [
7375
+ /* @__PURE__ */ jsx("i", { className: "ri-lock-line klun-capability-banner__icon", "aria-hidden": "true" }),
7376
+ /* @__PURE__ */ jsx("span", { children: message ?? getCopy(reason, RESOURCE_NOUN[resource], ownerName) })
7377
+ ]
7378
+ }
7379
+ );
7380
+ }
7381
+ );
7382
+ CapabilityBanner.displayName = "CapabilityBanner";
6098
7383
  var CircularProgress = forwardRef(
6099
7384
  function CircularProgress2({
6100
7385
  value = 0,
@@ -6152,14 +7437,250 @@ var CircularProgress = forwardRef(
6152
7437
  )
6153
7438
  ]
6154
7439
  }
6155
- ),
6156
- /* @__PURE__ */ jsx("div", { className: "klun-circular-progress__label", children: label != null ? label : `${Math.round(pct2)}%` })
6157
- ]
7440
+ ),
7441
+ /* @__PURE__ */ jsx("div", { className: "klun-circular-progress__label", children: label != null ? label : `${Math.round(pct2)}%` })
7442
+ ]
7443
+ }
7444
+ );
7445
+ }
7446
+ );
7447
+ CircularProgress.displayName = "CircularProgress";
7448
+ var SPHERE_PATHS = [
7449
+ { from: { x: -0.28, y: 0.18 }, to: { x: 1.28, y: 0.78 } },
7450
+ { from: { x: 1.28, y: 0.82 }, to: { x: -0.28, y: 0.22 } },
7451
+ { from: { x: 0.18, y: 1.24 }, to: { x: 0.82, y: -0.24 } }
7452
+ ];
7453
+ var WAVE_SPHERE_ANCHORS = [
7454
+ { x: 0.28, y: 0.08 },
7455
+ { x: 0.76, y: 0.28 },
7456
+ { x: 0.38, y: 0.5 },
7457
+ { x: 0.78, y: 0.72 },
7458
+ { x: 0.34, y: 0.93 }
7459
+ ];
7460
+ var SPHERE_PROFILES = [
7461
+ { phase: 0, radius: 1, dot: 1, speed: 1 },
7462
+ { phase: 0.2, radius: 0.94, dot: 0.96, speed: 1 },
7463
+ { phase: 0.4, radius: 1, dot: 1, speed: 1 },
7464
+ { phase: 0.6, radius: 0.94, dot: 0.96, speed: 1 },
7465
+ { phase: 0.8, radius: 0.9, dot: 0.94, speed: 1 }
7466
+ ];
7467
+ function clamp(value, min, max) {
7468
+ return Math.min(Math.max(value, min), max);
7469
+ }
7470
+ function lerp(start, end, amount) {
7471
+ return start + (end - start) * amount;
7472
+ }
7473
+ function easeInOutSine(value) {
7474
+ return 0.5 - Math.cos(value * Math.PI) * 0.5;
7475
+ }
7476
+ function smoothstep(edge0, edge1, value) {
7477
+ const t = clamp((value - edge0) / (edge1 - edge0), 0, 1);
7478
+ return t * t * (3 - 2 * t);
7479
+ }
7480
+ function getTravelOpacity(progress) {
7481
+ const fadeIn = smoothstep(0.03, 0.24, progress);
7482
+ const fadeOut = 1 - smoothstep(0.76, 0.97, progress);
7483
+ return Math.min(fadeIn, fadeOut);
7484
+ }
7485
+ function getWaveOpacity(progress) {
7486
+ const pulse = (1 - Math.cos(progress * Math.PI * 2)) * 0.5;
7487
+ return 0.16 + Math.pow(pulse, 1.28) * 0.84;
7488
+ }
7489
+ function resolveSphereRadius(radius, windowSize) {
7490
+ if (typeof radius === "number") return radius;
7491
+ const percent = Number.parseFloat(radius);
7492
+ if (Number.isNaN(percent)) return Math.max(windowSize.w, windowSize.h);
7493
+ return Math.max(windowSize.w, windowSize.h) * (percent / 100);
7494
+ }
7495
+ function resolveColor(host, value) {
7496
+ if (value === "currentColor") return getComputedStyle(host).color;
7497
+ return value;
7498
+ }
7499
+ var DotSphere = forwardRef(
7500
+ function DotSphere2({
7501
+ className,
7502
+ dotGap = 20,
7503
+ motion = "travel",
7504
+ sphereCount = 3,
7505
+ sphereRadius = 200,
7506
+ dotRadiusMax = 3,
7507
+ speed = 0.18,
7508
+ bgColor = "transparent",
7509
+ dotColor = "currentColor",
7510
+ followMouse = false,
7511
+ ...props
7512
+ }, ref) {
7513
+ const innerRef = useRef(null);
7514
+ useEffect(() => {
7515
+ const canvas = innerRef.current;
7516
+ if (!canvas) return;
7517
+ const ctx = canvas.getContext("2d");
7518
+ if (!ctx) return;
7519
+ const canvasElement = canvas;
7520
+ const context = ctx;
7521
+ const resolvedBg = bgColor === "transparent" ? null : bgColor;
7522
+ const resolvedDot = resolveColor(canvasElement, dotColor);
7523
+ const prefersReducedMotion = window.matchMedia(
7524
+ "(prefers-reduced-motion: reduce)"
7525
+ ).matches;
7526
+ const state = {
7527
+ windowSize: { w: 0, h: 0 },
7528
+ circleNumber: { x: 0, y: 0 },
7529
+ posStart: { x: 0, y: 0 },
7530
+ spheres: []
7531
+ };
7532
+ state.spheres = Array.from({ length: Math.max(1, sphereCount) }, (_, index) => {
7533
+ const path = SPHERE_PATHS[index % SPHERE_PATHS.length] ?? SPHERE_PATHS[0];
7534
+ const anchor = WAVE_SPHERE_ANCHORS[index % WAVE_SPHERE_ANCHORS.length] ?? WAVE_SPHERE_ANCHORS[0];
7535
+ const profile = SPHERE_PROFILES[index % SPHERE_PROFILES.length] ?? SPHERE_PROFILES[0];
7536
+ return {
7537
+ center: { x: 0, y: 0 },
7538
+ anchor,
7539
+ from: path.from,
7540
+ to: path.to,
7541
+ progress: profile.phase,
7542
+ radius: 0,
7543
+ radiusScale: profile.radius,
7544
+ dotRadiusMax: 0,
7545
+ dotScale: profile.dot,
7546
+ speed: speed * profile.speed,
7547
+ color: resolvedDot,
7548
+ opacity: 0
7549
+ };
7550
+ });
7551
+ function setDotParams() {
7552
+ state.circleNumber = {
7553
+ x: Math.floor(state.windowSize.w / dotGap) + 2,
7554
+ y: Math.floor(state.windowSize.h / dotGap) + 1
7555
+ };
7556
+ state.posStart = {
7557
+ x: Math.round(
7558
+ (state.windowSize.w - (state.circleNumber.x - 1) * dotGap) / 2
7559
+ ),
7560
+ y: Math.round(
7561
+ (state.windowSize.h - (state.circleNumber.y - 1) * dotGap) / 2
7562
+ )
7563
+ };
7564
+ }
7565
+ function updateSpherePosition(sphere) {
7566
+ if (motion === "wave") {
7567
+ sphere.center.x = sphere.anchor.x * state.windowSize.w;
7568
+ sphere.center.y = sphere.anchor.y * state.windowSize.h;
7569
+ sphere.opacity = getWaveOpacity(sphere.progress);
7570
+ return;
7571
+ }
7572
+ const easedProgress = easeInOutSine(sphere.progress);
7573
+ sphere.center.x = lerp(sphere.from.x, sphere.to.x, easedProgress) * state.windowSize.w;
7574
+ sphere.center.y = lerp(sphere.from.y, sphere.to.y, easedProgress) * state.windowSize.h;
7575
+ sphere.opacity = getTravelOpacity(sphere.progress);
7576
+ }
7577
+ function handleResize() {
7578
+ const bounds = canvasElement.getBoundingClientRect();
7579
+ const dpr = Math.min(window.devicePixelRatio || 1, 2);
7580
+ const width = Math.max(1, Math.floor(bounds.width));
7581
+ const height = Math.max(1, Math.floor(bounds.height));
7582
+ state.windowSize = { w: width, h: height };
7583
+ canvasElement.width = Math.floor(width * dpr);
7584
+ canvasElement.height = Math.floor(height * dpr);
7585
+ context.setTransform(dpr, 0, 0, dpr, 0, 0);
7586
+ setDotParams();
7587
+ state.spheres.forEach((sphere) => {
7588
+ sphere.radius = resolveSphereRadius(sphereRadius, state.windowSize) * sphere.radiusScale;
7589
+ sphere.dotRadiusMax = dotRadiusMax * sphere.dotScale;
7590
+ updateSpherePosition(sphere);
7591
+ });
7592
+ }
7593
+ function drawDots() {
7594
+ const { w, h } = state.windowSize;
7595
+ if (resolvedBg) {
7596
+ context.beginPath();
7597
+ context.fillStyle = resolvedBg;
7598
+ context.rect(0, 0, w, h);
7599
+ context.fill();
7600
+ context.closePath();
7601
+ } else {
7602
+ context.clearRect(0, 0, w, h);
7603
+ }
7604
+ context.globalCompositeOperation = "lighter";
7605
+ state.spheres.forEach((sphere) => {
7606
+ if (sphere.opacity <= 0.01) return;
7607
+ for (let i = 0; i < state.circleNumber.x; i++) {
7608
+ for (let j = 0; j < state.circleNumber.y; j++) {
7609
+ const gapX = j % 2 === 0 ? -dotGap / 2 : 0;
7610
+ const x = state.posStart.x + gapX + i * dotGap;
7611
+ const y = state.posStart.y + j * dotGap;
7612
+ const dx = x - sphere.center.x;
7613
+ const dy = y - sphere.center.y;
7614
+ const distance = Math.sqrt(dx * dx + dy * dy);
7615
+ if (distance >= sphere.radius) continue;
7616
+ const alpha = 1 - distance / sphere.radius;
7617
+ const radius = sphere.dotRadiusMax * alpha;
7618
+ context.save();
7619
+ context.globalAlpha = alpha * sphere.opacity;
7620
+ context.beginPath();
7621
+ context.fillStyle = sphere.color;
7622
+ context.arc(x, y, radius, 0, 2 * Math.PI, false);
7623
+ context.fill();
7624
+ context.closePath();
7625
+ context.restore();
7626
+ }
7627
+ }
7628
+ });
7629
+ context.globalCompositeOperation = "source-over";
7630
+ }
7631
+ function moveSpheres(event) {
7632
+ const bounds = event ? canvasElement.getBoundingClientRect() : null;
7633
+ state.spheres.forEach((sphere, index) => {
7634
+ if (event && followMouse && index === 0 && bounds) {
7635
+ sphere.center.x = event.clientX - bounds.left;
7636
+ sphere.center.y = event.clientY - bounds.top;
7637
+ sphere.opacity = 1;
7638
+ return;
7639
+ }
7640
+ if (followMouse && index === 0) return;
7641
+ sphere.progress = (sphere.progress + sphere.speed * (motion === "wave" ? 21e-4 : 35e-4)) % 1;
7642
+ updateSpherePosition(sphere);
7643
+ });
7644
+ }
7645
+ function render() {
7646
+ if (!prefersReducedMotion) moveSpheres(null);
7647
+ drawDots();
7648
+ }
7649
+ let animationId = 0;
7650
+ function draw() {
7651
+ animationId = window.requestAnimationFrame(draw);
7652
+ render();
7653
+ }
7654
+ function handleMouseMove(event) {
7655
+ if (followMouse) moveSpheres(event);
7656
+ }
7657
+ const resizeObserver = new ResizeObserver(handleResize);
7658
+ resizeObserver.observe(canvasElement);
7659
+ handleResize();
7660
+ render();
7661
+ if (!prefersReducedMotion) draw();
7662
+ if (followMouse) window.addEventListener("mousemove", handleMouseMove);
7663
+ return () => {
7664
+ resizeObserver.disconnect();
7665
+ window.removeEventListener("mousemove", handleMouseMove);
7666
+ window.cancelAnimationFrame(animationId);
7667
+ };
7668
+ }, [dotGap, motion, sphereCount, sphereRadius, dotRadiusMax, speed, bgColor, dotColor, followMouse]);
7669
+ return /* @__PURE__ */ jsx(
7670
+ "canvas",
7671
+ {
7672
+ ref: (node) => {
7673
+ innerRef.current = node;
7674
+ if (typeof ref === "function") ref(node);
7675
+ else if (ref) ref.current = node;
7676
+ },
7677
+ className: cx("klun-dot-sphere", className),
7678
+ ...props
6158
7679
  }
6159
7680
  );
6160
7681
  }
6161
7682
  );
6162
- CircularProgress.displayName = "CircularProgress";
7683
+ DotSphere.displayName = "DotSphere";
6163
7684
  var EmptyState = forwardRef(function EmptyState2({ icon, title, description, action, className, ...props }, ref) {
6164
7685
  return /* @__PURE__ */ jsxs("div", { ref, className: cx("klun-empty-state", className), ...props, children: [
6165
7686
  icon ? /* @__PURE__ */ jsx("div", { className: "klun-empty-state__icon", children: icon }) : null,
@@ -6282,6 +7803,33 @@ function useMessage(defaults = {}) {
6282
7803
  return [api, holder];
6283
7804
  }
6284
7805
  Message.useMessage = useMessage;
7806
+ function NavigationProgress({
7807
+ active,
7808
+ position = "fixed",
7809
+ top = 0,
7810
+ className,
7811
+ style
7812
+ }) {
7813
+ const [renderSweep, setRenderSweep] = useState(false);
7814
+ useEffect(() => {
7815
+ if (active) setRenderSweep(true);
7816
+ }, [active]);
7817
+ return /* @__PURE__ */ jsx(
7818
+ "div",
7819
+ {
7820
+ "aria-hidden": "true",
7821
+ role: "presentation",
7822
+ "data-active": active ? "true" : "false",
7823
+ "data-position": position,
7824
+ className: cx("klun-navigation-progress", className),
7825
+ style: { top, ...style },
7826
+ onTransitionEnd: (event) => {
7827
+ if (!active) setRenderSweep(false);
7828
+ },
7829
+ children: renderSweep && /* @__PURE__ */ jsx("div", { className: "klun-navigation-progress__sweep" })
7830
+ }
7831
+ );
7832
+ }
6285
7833
  var STATUS2 = {
6286
7834
  default: { icon: "ri-notification-3-line", c: null },
6287
7835
  information: { icon: "ri-information-fill", c: "information" },
@@ -6724,6 +8272,54 @@ function ToastRow({
6724
8272
  }
6725
8273
  );
6726
8274
  }
8275
+
8276
+ // src/components/feedback/unicode-spinner-data.ts
8277
+ var UNICODE_ANIMATIONS = {
8278
+ "braille": { interval: 80, frames: ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"] },
8279
+ "braillewave": { interval: 100, frames: ["\u2801\u2802\u2804\u2840", "\u2802\u2804\u2840\u2880", "\u2804\u2840\u2880\u2820", "\u2840\u2880\u2820\u2810", "\u2880\u2820\u2810\u2808", "\u2820\u2810\u2808\u2801", "\u2810\u2808\u2801\u2802", "\u2808\u2801\u2802\u2804"] },
8280
+ "dna": { interval: 80, frames: ["\u280B\u2809\u2819\u281A", "\u2809\u2819\u281A\u2812", "\u2819\u281A\u2812\u2802", "\u281A\u2812\u2802\u2802", "\u2812\u2802\u2802\u2812", "\u2802\u2802\u2812\u2832", "\u2802\u2812\u2832\u2834", "\u2812\u2832\u2834\u2824", "\u2832\u2834\u2824\u2804", "\u2834\u2824\u2804\u280B", "\u2824\u2804\u280B\u2809", "\u2804\u280B\u2809\u2819"] },
8281
+ "scan": { interval: 70, frames: ["\u2800\u2800\u2800\u2800", "\u2847\u2800\u2800\u2800", "\u28FF\u2800\u2800\u2800", "\u28B8\u2847\u2800\u2800", "\u2800\u28FF\u2800\u2800", "\u2800\u28B8\u2847\u2800", "\u2800\u2800\u28FF\u2800", "\u2800\u2800\u28B8\u2847", "\u2800\u2800\u2800\u28FF", "\u2800\u2800\u2800\u28B8"] },
8282
+ "rain": { interval: 100, frames: ["\u2881\u2802\u2814\u2808", "\u2802\u280C\u2860\u2810", "\u2804\u2850\u2880\u2821", "\u2848\u2820\u2800\u2882", "\u2810\u2880\u2801\u2804", "\u2820\u2801\u280A\u2840", "\u2881\u2802\u2814\u2808", "\u2802\u280C\u2860\u2810", "\u2804\u2850\u2880\u2821", "\u2848\u2820\u2800\u2882", "\u2810\u2880\u2801\u2804", "\u2820\u2801\u280A\u2840"] },
8283
+ "scanline": { interval: 120, frames: ["\u2809\u2809\u2809", "\u2813\u2813\u2813", "\u2826\u2826\u2826", "\u28C4\u28C4\u28C4", "\u2826\u2826\u2826", "\u2813\u2813\u2813"] },
8284
+ "pulse": { interval: 180, frames: ["\u2800\u2836\u2800", "\u2830\u28FF\u2806", "\u28BE\u28C9\u2877", "\u28CF\u2800\u28F9", "\u2841\u2800\u2888"] },
8285
+ "snake": { interval: 80, frames: ["\u28C1\u2840", "\u28C9\u2800", "\u2849\u2801", "\u2809\u2809", "\u2808\u2819", "\u2800\u281B", "\u2810\u281A", "\u2812\u2812", "\u2816\u2802", "\u2836\u2800", "\u2826\u2804", "\u2824\u2824", "\u2820\u28A4", "\u2800\u28E4", "\u2880\u28E0", "\u28C0\u28C0"] },
8286
+ "sparkle": { interval: 150, frames: ["\u2861\u280A\u2894\u2821", "\u280A\u2870\u2861\u2858", "\u2894\u2885\u2808\u28A2", "\u2841\u2882\u2806\u284D", "\u2894\u2828\u2891\u2890", "\u2828\u2851\u2860\u280A"] },
8287
+ "cascade": { interval: 60, frames: ["\u2800\u2800\u2800\u2800", "\u2800\u2800\u2800\u2800", "\u2801\u2800\u2800\u2800", "\u280B\u2800\u2800\u2800", "\u281E\u2801\u2800\u2800", "\u2874\u280B\u2800\u2800", "\u28E0\u281E\u2801\u2800", "\u2880\u2874\u280B\u2800", "\u2800\u28E0\u281E\u2801", "\u2800\u2880\u2874\u280B", "\u2800\u2800\u28E0\u281E", "\u2800\u2800\u2880\u2874", "\u2800\u2800\u2800\u28E0", "\u2800\u2800\u2800\u2880"] },
8288
+ "columns": { interval: 60, frames: ["\u2840\u2800\u2800", "\u2844\u2800\u2800", "\u2846\u2800\u2800", "\u2847\u2800\u2800", "\u28C7\u2800\u2800", "\u28E7\u2800\u2800", "\u28F7\u2800\u2800", "\u28FF\u2800\u2800", "\u28FF\u2840\u2800", "\u28FF\u2844\u2800", "\u28FF\u2846\u2800", "\u28FF\u2847\u2800", "\u28FF\u28C7\u2800", "\u28FF\u28E7\u2800", "\u28FF\u28F7\u2800", "\u28FF\u28FF\u2800", "\u28FF\u28FF\u2840", "\u28FF\u28FF\u2844", "\u28FF\u28FF\u2846", "\u28FF\u28FF\u2847", "\u28FF\u28FF\u28C7", "\u28FF\u28FF\u28E7", "\u28FF\u28FF\u28F7", "\u28FF\u28FF\u28FF", "\u28FF\u28FF\u28FF", "\u2800\u2800\u2800"] },
8289
+ "orbit": { interval: 100, frames: ["\u2803", "\u2809", "\u2818", "\u2830", "\u28A0", "\u28C0", "\u2844", "\u2806"] },
8290
+ "breathe": { interval: 100, frames: ["\u2800", "\u2802", "\u280C", "\u2851", "\u2895", "\u289D", "\u28EB", "\u28DF", "\u28FF", "\u28DF", "\u28EB", "\u289D", "\u2895", "\u2851", "\u280C", "\u2802", "\u2800"] },
8291
+ "waverows": { interval: 90, frames: ["\u2816\u2809\u2809\u2811", "\u2860\u2816\u2809\u2809", "\u28E0\u2860\u2816\u2809", "\u28C4\u28E0\u2860\u2816", "\u2822\u28C4\u28E0\u2860", "\u2819\u2822\u28C4\u28E0", "\u2809\u2819\u2822\u28C4", "\u280A\u2809\u2819\u2822", "\u281C\u280A\u2809\u2819", "\u2864\u281C\u280A\u2809", "\u28C0\u2864\u281C\u280A", "\u28A4\u28C0\u2864\u281C", "\u2823\u28A4\u28C0\u2864", "\u2811\u2823\u28A4\u28C0", "\u2809\u2811\u2823\u28A4", "\u280B\u2809\u2811\u2823"] },
8292
+ "checkerboard": { interval: 250, frames: ["\u2895\u2895\u2895", "\u286A\u286A\u286A", "\u288A\u2814\u2861", "\u2861\u288A\u2814"] },
8293
+ "helix": { interval: 80, frames: ["\u288C\u28C9\u288E\u28C9", "\u28C9\u2871\u28C9\u2871", "\u28C9\u288E\u28C9\u288E", "\u2871\u28C9\u2871\u28C9", "\u288E\u28C9\u288E\u28C9", "\u28C9\u2871\u28C9\u2871", "\u28C9\u288E\u28C9\u288E", "\u2871\u28C9\u2871\u28C9", "\u288E\u28C9\u288E\u28C9", "\u28C9\u2871\u28C9\u2871", "\u28C9\u288E\u28C9\u288E", "\u2871\u28C9\u2871\u28C9", "\u288E\u28C9\u288E\u28C9", "\u28C9\u2871\u28C9\u2871", "\u28C9\u288E\u28C9\u288E", "\u2871\u28C9\u2871\u28C9"] },
8294
+ "fillsweep": { interval: 100, frames: ["\u28C0\u28C0", "\u28E4\u28E4", "\u28F6\u28F6", "\u28FF\u28FF", "\u28FF\u28FF", "\u28FF\u28FF", "\u28F6\u28F6", "\u28E4\u28E4", "\u28C0\u28C0", "\u2800\u2800", "\u2800\u2800"] },
8295
+ "diagswipe": { interval: 60, frames: ["\u2801\u2800", "\u280B\u2800", "\u281F\u2801", "\u287F\u280B", "\u28FF\u281F", "\u28FF\u287F", "\u28FF\u28FF", "\u28FF\u28FF", "\u28FE\u28FF", "\u28F4\u28FF", "\u28E0\u28FE", "\u2880\u28F4", "\u2800\u28E0", "\u2800\u2880", "\u2800\u2800", "\u2800\u2800"] }
8296
+ };
8297
+ var UnicodeSpinner = forwardRef(
8298
+ function UnicodeSpinner2({ name = "braille", paused = false, className, ...props }, ref) {
8299
+ const spec = UNICODE_ANIMATIONS[name];
8300
+ const [frame, setFrame] = useState(0);
8301
+ useEffect(() => {
8302
+ if (paused) return;
8303
+ setFrame(0);
8304
+ const timer = setInterval(
8305
+ () => setFrame((f) => (f + 1) % spec.frames.length),
8306
+ spec.interval
8307
+ );
8308
+ return () => clearInterval(timer);
8309
+ }, [name, paused, spec]);
8310
+ return /* @__PURE__ */ jsx(
8311
+ "span",
8312
+ {
8313
+ ref,
8314
+ className: cx("klun-unicode-spinner", className),
8315
+ "aria-hidden": "true",
8316
+ ...props,
8317
+ children: spec.frames[frame]
8318
+ }
8319
+ );
8320
+ }
8321
+ );
8322
+ UnicodeSpinner.displayName = "UnicodeSpinner";
6727
8323
  var AutoComplete = forwardRef(
6728
8324
  function AutoComplete2({
6729
8325
  options = [],
@@ -7810,6 +9406,69 @@ var CompactSelectForInput = forwardRef(function CompactSelectForInput2({
7810
9406
  );
7811
9407
  });
7812
9408
  CompactSelectForInput.displayName = "CompactSelectForInput";
9409
+ var CounterInput = forwardRef(function CounterInput2({
9410
+ value = 0,
9411
+ min = -Infinity,
9412
+ max = Infinity,
9413
+ step = 1,
9414
+ size: sizeProp,
9415
+ onChange,
9416
+ disabled = false,
9417
+ suffix,
9418
+ className,
9419
+ ...props
9420
+ }, ref) {
9421
+ const size = useSize(sizeProp);
9422
+ const { counterInput } = useLocale();
9423
+ const decreaseLabel = counterInput?.decrease ?? "Decrease";
9424
+ const increaseLabel = counterInput?.increase ?? "Increase";
9425
+ const set = (next) => {
9426
+ const v = Math.max(min, Math.min(max, next));
9427
+ if (v !== value) onChange?.(v);
9428
+ };
9429
+ return /* @__PURE__ */ jsxs(
9430
+ "div",
9431
+ {
9432
+ ref,
9433
+ className: cx(
9434
+ "klun-counter-input",
9435
+ `klun-counter-input--${size}`,
9436
+ disabled && "klun-counter-input--disabled",
9437
+ className
9438
+ ),
9439
+ ...props,
9440
+ children: [
9441
+ /* @__PURE__ */ jsx(
9442
+ "button",
9443
+ {
9444
+ type: "button",
9445
+ className: "klun-counter-input__step",
9446
+ "aria-label": decreaseLabel,
9447
+ disabled: disabled || value <= min,
9448
+ onClick: () => set(value - step),
9449
+ children: /* @__PURE__ */ jsx("i", { className: "ri-subtract-line" })
9450
+ }
9451
+ ),
9452
+ /* @__PURE__ */ jsxs("span", { className: "klun-counter-input__value", children: [
9453
+ value,
9454
+ suffix ? /* @__PURE__ */ jsx("span", { className: "klun-counter-input__suffix", children: suffix }) : null
9455
+ ] }),
9456
+ /* @__PURE__ */ jsx(
9457
+ "button",
9458
+ {
9459
+ type: "button",
9460
+ className: "klun-counter-input__step",
9461
+ "aria-label": increaseLabel,
9462
+ disabled: disabled || value >= max,
9463
+ onClick: () => set(value + step),
9464
+ children: /* @__PURE__ */ jsx("i", { className: "ri-add-line" })
9465
+ }
9466
+ )
9467
+ ]
9468
+ }
9469
+ );
9470
+ });
9471
+ CounterInput.displayName = "CounterInput";
7813
9472
  var CRON_PRESETS = [
7814
9473
  { label: "Every 5 minutes", value: "*/5 * * * *" },
7815
9474
  { label: "Every 15 minutes", value: "*/15 * * * *" },
@@ -7953,64 +9612,6 @@ var CronEditor = forwardRef(function CronEditor2({
7953
9612
  );
7954
9613
  });
7955
9614
  CronEditor.displayName = "CronEditor";
7956
- var CounterInput = forwardRef(function CounterInput2({
7957
- value = 0,
7958
- min = -Infinity,
7959
- max = Infinity,
7960
- step = 1,
7961
- size: sizeProp,
7962
- onChange,
7963
- disabled = false,
7964
- suffix,
7965
- className,
7966
- ...props
7967
- }, ref) {
7968
- const size = useSize(sizeProp);
7969
- const set = (next) => {
7970
- const v = Math.max(min, Math.min(max, next));
7971
- if (v !== value) onChange?.(v);
7972
- };
7973
- return /* @__PURE__ */ jsxs(
7974
- "div",
7975
- {
7976
- ref,
7977
- className: cx(
7978
- "klun-counter-input",
7979
- `klun-counter-input--${size}`,
7980
- disabled && "klun-counter-input--disabled",
7981
- className
7982
- ),
7983
- ...props,
7984
- children: [
7985
- /* @__PURE__ */ jsx(
7986
- "button",
7987
- {
7988
- type: "button",
7989
- className: "klun-counter-input__step",
7990
- disabled: disabled || value <= min,
7991
- onClick: () => set(value - step),
7992
- children: /* @__PURE__ */ jsx("i", { className: "ri-subtract-line" })
7993
- }
7994
- ),
7995
- /* @__PURE__ */ jsxs("span", { className: "klun-counter-input__value", children: [
7996
- value,
7997
- suffix ? /* @__PURE__ */ jsx("span", { className: "klun-counter-input__suffix", children: suffix }) : null
7998
- ] }),
7999
- /* @__PURE__ */ jsx(
8000
- "button",
8001
- {
8002
- type: "button",
8003
- className: "klun-counter-input__step",
8004
- disabled: disabled || value >= max,
8005
- onClick: () => set(value + step),
8006
- children: /* @__PURE__ */ jsx("i", { className: "ri-add-line" })
8007
- }
8008
- )
8009
- ]
8010
- }
8011
- );
8012
- });
8013
- CounterInput.displayName = "CounterInput";
8014
9615
  var Input = forwardRef(function Input2({
8015
9616
  size: sizeProp,
8016
9617
  leadingIcon,
@@ -8086,107 +9687,25 @@ var Input = forwardRef(function Input2({
8086
9687
  }
8087
9688
  ),
8088
9689
  showClear ? /* @__PURE__ */ jsx(
8089
- "button",
8090
- {
8091
- type: "button",
8092
- className: "klun-input__clear",
8093
- onClick: clear,
8094
- "aria-label": input.clear,
8095
- children: /* @__PURE__ */ jsx("i", { className: "ri-close-circle-fill" })
8096
- }
8097
- ) : null,
8098
- trailingNode,
8099
- loading ? /* @__PURE__ */ jsx("span", { className: "klun-input__spinner", "aria-hidden": true, children: /* @__PURE__ */ jsxs("svg", { className: "klun-input__spinner-svg", width: 18, height: 18, viewBox: "0 0 20 20", fill: "none", children: [
8100
- /* @__PURE__ */ jsx("circle", { cx: "10", cy: "10", r: "7", stroke: "currentColor", strokeOpacity: "0.25", strokeWidth: "2.5" }),
8101
- /* @__PURE__ */ jsx("circle", { cx: "10", cy: "10", r: "7", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeDasharray: "44", strokeDashoffset: "31" })
8102
- ] }) }) : trailingIcon ? /* @__PURE__ */ jsx("span", { className: "klun-input__icon", children: trailingIcon }) : null
8103
- ]
8104
- }
8105
- );
8106
- });
8107
- Input.displayName = "Input";
8108
- var Popover = forwardRef(function Popover2({ trigger, children, align = "left", width = 280, className, ...props }, ref) {
8109
- const [open, setOpen] = useState(false);
8110
- const wrapRef = useRef(null);
8111
- const panelRef = useRef(null);
8112
- const portal = typeof document !== "undefined";
8113
- const [pos, setPos] = useState(null);
8114
- useEffect(() => {
8115
- if (!open) return;
8116
- const h = (e) => {
8117
- const t = e.target;
8118
- if (wrapRef.current?.contains(t)) return;
8119
- if (panelRef.current?.contains(t)) return;
8120
- setOpen(false);
8121
- };
8122
- document.addEventListener("mousedown", h);
8123
- return () => document.removeEventListener("mousedown", h);
8124
- }, [open]);
8125
- useLayoutEffect(() => {
8126
- if (!open || !portal) return;
8127
- const anchor = wrapRef.current;
8128
- const panel2 = panelRef.current;
8129
- if (!anchor || !panel2) return;
8130
- const place2 = () => {
8131
- const a = anchor.getBoundingClientRect();
8132
- const gap = 8;
8133
- const margin = 8;
8134
- const pw = panel2.offsetWidth || width;
8135
- const ph = panel2.offsetHeight;
8136
- const below = window.innerHeight - a.bottom;
8137
- const openUp = below < ph + gap && a.top > below;
8138
- const top = openUp ? Math.max(margin, a.top - gap - ph) : Math.min(a.bottom + gap, window.innerHeight - margin - ph);
8139
- let left = align === "right" ? a.right - pw : a.left;
8140
- if (left + pw > window.innerWidth - margin) left = window.innerWidth - margin - pw;
8141
- if (left < margin) left = margin;
8142
- setPos({ left, top });
8143
- };
8144
- place2();
8145
- window.addEventListener("scroll", place2, true);
8146
- window.addEventListener("resize", place2);
8147
- return () => {
8148
- window.removeEventListener("scroll", place2, true);
8149
- window.removeEventListener("resize", place2);
8150
- };
8151
- }, [open, portal, align, width]);
8152
- useEffect(() => {
8153
- if (!open) setPos(null);
8154
- }, [open]);
8155
- const setRefs = (node) => {
8156
- wrapRef.current = node;
8157
- if (typeof ref === "function") ref(node);
8158
- else if (ref) ref.current = node;
8159
- };
8160
- const content = typeof children === "function" ? children(() => setOpen(false)) : children;
8161
- const portalStyle = {
8162
- position: "fixed",
8163
- left: pos?.left ?? 0,
8164
- top: pos?.top ?? 0,
8165
- width,
8166
- zIndex: 1150,
8167
- // keep hidden until measured to avoid a flash at the top-left corner
8168
- visibility: pos ? "visible" : "hidden"
8169
- };
8170
- const panel = open ? /* @__PURE__ */ jsx(
8171
- "div",
8172
- {
8173
- ref: panelRef,
8174
- role: "dialog",
8175
- className: cx(
8176
- "klun-popover__panel",
8177
- // in-flow fallback keeps the legacy left/right anchoring classes
8178
- !portal && `klun-popover__panel--${align}`
8179
- ),
8180
- style: portal ? portalStyle : { width },
8181
- children: content
9690
+ "button",
9691
+ {
9692
+ type: "button",
9693
+ className: "klun-input__clear",
9694
+ onClick: clear,
9695
+ "aria-label": input.clear,
9696
+ children: /* @__PURE__ */ jsx("i", { className: "ri-close-circle-fill" })
9697
+ }
9698
+ ) : null,
9699
+ trailingNode,
9700
+ loading ? /* @__PURE__ */ jsx("span", { className: "klun-input__spinner", "aria-hidden": true, children: /* @__PURE__ */ jsxs("svg", { className: "klun-input__spinner-svg", width: 18, height: 18, viewBox: "0 0 20 20", fill: "none", children: [
9701
+ /* @__PURE__ */ jsx("circle", { cx: "10", cy: "10", r: "7", stroke: "currentColor", strokeOpacity: "0.25", strokeWidth: "2.5" }),
9702
+ /* @__PURE__ */ jsx("circle", { cx: "10", cy: "10", r: "7", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeDasharray: "44", strokeDashoffset: "31" })
9703
+ ] }) }) : trailingIcon ? /* @__PURE__ */ jsx("span", { className: "klun-input__icon", children: trailingIcon }) : null
9704
+ ]
8182
9705
  }
8183
- ) : null;
8184
- return /* @__PURE__ */ jsxs("span", { ref: setRefs, className: cx("klun-popover", className), ...props, children: [
8185
- /* @__PURE__ */ jsx("span", { className: "klun-popover__trigger", onClick: () => setOpen((o) => !o), children: trigger }),
8186
- portal ? panel && createPortal(panel, document.body) : panel
8187
- ] });
9706
+ );
8188
9707
  });
8189
- Popover.displayName = "Popover";
9708
+ Input.displayName = "Input";
8190
9709
  var same = (a, b) => !!a && !!b && a.toDateString() === b.toDateString();
8191
9710
  var DEFAULT_FORMAT = {
8192
9711
  year: "numeric",
@@ -8687,6 +10206,56 @@ var DigitInput = forwardRef(function DigitInput2({ length = 4, value = "", onCha
8687
10206
  );
8688
10207
  });
8689
10208
  DigitInput.displayName = "DigitInput";
10209
+ var FieldGroup = forwardRef(
10210
+ function FieldGroup2({ className, ...props }, ref) {
10211
+ return /* @__PURE__ */ jsx("div", { ref, className: cx("klun-field-group", className), ...props });
10212
+ }
10213
+ );
10214
+ FieldGroup.displayName = "FieldGroup";
10215
+ var Field = forwardRef(function Field2({ orientation = "vertical", invalid = false, className, ...props }, ref) {
10216
+ return /* @__PURE__ */ jsx(
10217
+ "div",
10218
+ {
10219
+ ref,
10220
+ role: "group",
10221
+ "data-orientation": orientation,
10222
+ "data-invalid": invalid ? "" : void 0,
10223
+ className: cx("klun-field", `klun-field--${orientation}`, className),
10224
+ ...props
10225
+ }
10226
+ );
10227
+ });
10228
+ Field.displayName = "Field";
10229
+ var FieldContent = forwardRef(
10230
+ function FieldContent2({ className, ...props }, ref) {
10231
+ return /* @__PURE__ */ jsx("div", { ref, className: cx("klun-field__content", className), ...props });
10232
+ }
10233
+ );
10234
+ FieldContent.displayName = "FieldContent";
10235
+ var FieldLabel = forwardRef(
10236
+ function FieldLabel2({ className, ...props }, ref) {
10237
+ return /* @__PURE__ */ jsx("label", { ref, className: cx("klun-field__label", className), ...props });
10238
+ }
10239
+ );
10240
+ FieldLabel.displayName = "FieldLabel";
10241
+ var FieldTitle = forwardRef(
10242
+ function FieldTitle2({ className, ...props }, ref) {
10243
+ return /* @__PURE__ */ jsx("div", { ref, className: cx("klun-field__title", className), ...props });
10244
+ }
10245
+ );
10246
+ FieldTitle.displayName = "FieldTitle";
10247
+ var FieldDescription = forwardRef(
10248
+ function FieldDescription2({ className, ...props }, ref) {
10249
+ return /* @__PURE__ */ jsx("div", { ref, className: cx("klun-field__description", className), ...props });
10250
+ }
10251
+ );
10252
+ FieldDescription.displayName = "FieldDescription";
10253
+ var FieldError = forwardRef(
10254
+ function FieldError2({ className, role = "alert", ...props }, ref) {
10255
+ return /* @__PURE__ */ jsx("div", { ref, role, className: cx("klun-field__error", className), ...props });
10256
+ }
10257
+ );
10258
+ FieldError.displayName = "FieldError";
8690
10259
  var FileUpload = forwardRef(function FileUpload2({ onFiles, hint = "SVG, PNG, JPG or GIF (max. 8MB)", className, ...props }, ref) {
8691
10260
  const { fileUpload } = useLocale();
8692
10261
  const [over, setOver] = useState(false);
@@ -9099,6 +10668,388 @@ var Hint = forwardRef(function Hint2({ children, error = false, icon, className,
9099
10668
  );
9100
10669
  });
9101
10670
  Hint.displayName = "Hint";
10671
+ var Modal = forwardRef(function Modal2({
10672
+ open,
10673
+ onClose,
10674
+ onOk,
10675
+ onCancel,
10676
+ title,
10677
+ description,
10678
+ icon,
10679
+ footer,
10680
+ okText,
10681
+ cancelText,
10682
+ confirmLoading = false,
10683
+ okType = "primary",
10684
+ okButtonProps,
10685
+ cancelButtonProps,
10686
+ width = 520,
10687
+ centered = false,
10688
+ closable = true,
10689
+ maskClosable,
10690
+ closeOnBackdrop,
10691
+ keyboard = true,
10692
+ mask = true,
10693
+ draggable = false,
10694
+ maximizable = false,
10695
+ defaultMaximized = false,
10696
+ zIndex,
10697
+ afterClose,
10698
+ afterOpenChange,
10699
+ children,
10700
+ className,
10701
+ style,
10702
+ ...props
10703
+ }, ref) {
10704
+ const { modal } = useLocale();
10705
+ const cancel = onCancel ?? onClose;
10706
+ const backdropClosable = maskClosable ?? closeOnBackdrop ?? true;
10707
+ const [pos, setPos] = useState({ x: 0, y: 0 });
10708
+ const [maximized, setMaximized] = useState(defaultMaximized);
10709
+ const drag = useRef(null);
10710
+ const prevOpen = useRef(open);
10711
+ useEffect(() => {
10712
+ if (!open) return;
10713
+ const onKey = (e) => {
10714
+ if (e.key === "Escape" && keyboard && closable) cancel?.();
10715
+ };
10716
+ document.addEventListener("keydown", onKey);
10717
+ const prevOverflow = document.body.style.overflow;
10718
+ document.body.style.overflow = "hidden";
10719
+ return () => {
10720
+ document.removeEventListener("keydown", onKey);
10721
+ document.body.style.overflow = prevOverflow;
10722
+ };
10723
+ }, [open, keyboard, closable, cancel]);
10724
+ useEffect(() => {
10725
+ if (open) {
10726
+ setPos({ x: 0, y: 0 });
10727
+ setMaximized(defaultMaximized);
10728
+ }
10729
+ if (prevOpen.current !== open) {
10730
+ afterOpenChange?.(open);
10731
+ if (!open) afterClose?.();
10732
+ prevOpen.current = open;
10733
+ }
10734
+ }, [open]);
10735
+ if (!open) return null;
10736
+ const startDrag = (e) => {
10737
+ if (!draggable || maximized) return;
10738
+ if (e.target.closest(".klun-modal__actions")) return;
10739
+ drag.current = { sx: e.clientX, sy: e.clientY, ox: pos.x, oy: pos.y };
10740
+ const move = (ev) => {
10741
+ if (!drag.current) return;
10742
+ setPos({
10743
+ x: drag.current.ox + (ev.clientX - drag.current.sx),
10744
+ y: drag.current.oy + (ev.clientY - drag.current.sy)
10745
+ });
10746
+ };
10747
+ const up = () => {
10748
+ drag.current = null;
10749
+ document.removeEventListener("mousemove", move);
10750
+ document.removeEventListener("mouseup", up);
10751
+ };
10752
+ document.addEventListener("mousemove", move);
10753
+ document.addEventListener("mouseup", up);
10754
+ };
10755
+ const defaultFooter = /* @__PURE__ */ jsxs(Fragment, { children: [
10756
+ /* @__PURE__ */ jsx(Button, { variant: "neutral", appearance: "stroke", disabled: confirmLoading, onClick: cancel, ...cancelButtonProps, children: cancelText ?? "Cancel" }),
10757
+ /* @__PURE__ */ jsx(Button, { variant: okType === "danger" ? "error" : "primary", loading: confirmLoading, onClick: onOk, ...okButtonProps, children: okText ?? "OK" })
10758
+ ] });
10759
+ const resolvedFooter = footer === null ? null : footer !== void 0 ? footer : defaultFooter;
10760
+ const dialogStyle = maximized ? { ...style } : {
10761
+ width,
10762
+ ...pos.x || pos.y ? { transform: `translate(${pos.x}px, ${pos.y}px)` } : null,
10763
+ ...style
10764
+ };
10765
+ return /* @__PURE__ */ jsx(
10766
+ "div",
10767
+ {
10768
+ className: cx("klun-modal", centered && "klun-modal--centered", !mask && "klun-modal--no-mask"),
10769
+ style: zIndex != null ? { zIndex } : void 0,
10770
+ onClick: backdropClosable && closable ? cancel : void 0,
10771
+ children: /* @__PURE__ */ jsxs(
10772
+ "div",
10773
+ {
10774
+ ref,
10775
+ role: "dialog",
10776
+ "aria-modal": "true",
10777
+ onClick: (e) => e.stopPropagation(),
10778
+ className: cx("klun-modal__dialog", maximized && "klun-modal__dialog--maximized", className),
10779
+ style: dialogStyle,
10780
+ ...props,
10781
+ children: [
10782
+ title || icon ? /* @__PURE__ */ jsxs(
10783
+ "div",
10784
+ {
10785
+ className: cx("klun-modal__header", draggable && !maximized && "klun-modal__header--draggable"),
10786
+ onMouseDown: startDrag,
10787
+ children: [
10788
+ icon ? /* @__PURE__ */ jsx("div", { className: "klun-modal__icon", children: icon }) : null,
10789
+ /* @__PURE__ */ jsxs("div", { className: "klun-modal__heading", children: [
10790
+ title ? /* @__PURE__ */ jsx("div", { className: "klun-modal__title", children: title }) : null,
10791
+ description ? /* @__PURE__ */ jsx("div", { className: "klun-modal__description", children: description }) : null
10792
+ ] }),
10793
+ maximizable || closable ? /* @__PURE__ */ jsxs("div", { className: "klun-modal__actions", children: [
10794
+ maximizable ? /* @__PURE__ */ jsx(
10795
+ "button",
10796
+ {
10797
+ type: "button",
10798
+ onClick: () => setMaximized((m) => !m),
10799
+ "aria-label": maximized ? modal.restore ?? "Restore" : modal.maximize ?? "Maximize",
10800
+ className: "klun-modal__action",
10801
+ children: maximized ? /* @__PURE__ */ jsx("svg", { width: "18", height: "18", viewBox: "0 0 20 20", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M8 4v4H4M12 16v-4h4M12 4v4h4M8 16v-4H4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) : /* @__PURE__ */ jsx("svg", { width: "18", height: "18", viewBox: "0 0 20 20", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M4 8V4h4M16 12v4h-4M16 8V4h-4M4 12v4h4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
10802
+ }
10803
+ ) : null,
10804
+ closable ? /* @__PURE__ */ jsx("button", { type: "button", onClick: cancel, "aria-label": modal.close, className: "klun-modal__action klun-modal__close", children: /* @__PURE__ */ jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M5 5l10 10M15 5L5 15", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) }) : null
10805
+ ] }) : null
10806
+ ]
10807
+ }
10808
+ ) : null,
10809
+ children ? /* @__PURE__ */ jsx("div", { className: "klun-modal__body", children }) : null,
10810
+ resolvedFooter ? /* @__PURE__ */ jsx("div", { className: cx("klun-modal__footer", !children && "klun-modal__footer--flush"), children: resolvedFooter }) : null
10811
+ ]
10812
+ }
10813
+ )
10814
+ }
10815
+ );
10816
+ });
10817
+ Modal.displayName = "Modal";
10818
+ var ICON_GROUPS = [
10819
+ {
10820
+ label: "\u5E38\u7528",
10821
+ icons: [
10822
+ "ri-home-4-line",
10823
+ "ri-dashboard-3-line",
10824
+ "ri-settings-3-line",
10825
+ "ri-user-line",
10826
+ "ri-team-line",
10827
+ "ri-file-list-3-line",
10828
+ "ri-calendar-line",
10829
+ "ri-notification-3-line",
10830
+ "ri-search-line",
10831
+ "ri-star-line",
10832
+ "ri-heart-line",
10833
+ "ri-bookmark-line",
10834
+ "ri-link",
10835
+ "ri-share-line",
10836
+ "ri-download-line",
10837
+ "ri-upload-line",
10838
+ "ri-eye-line",
10839
+ "ri-lock-line",
10840
+ "ri-mail-line",
10841
+ "ri-message-2-line"
10842
+ ]
10843
+ },
10844
+ {
10845
+ label: "\u65B9\u5411",
10846
+ icons: [
10847
+ "ri-arrow-up-line",
10848
+ "ri-arrow-down-line",
10849
+ "ri-arrow-left-line",
10850
+ "ri-arrow-right-line",
10851
+ "ri-arrow-up-s-line",
10852
+ "ri-arrow-down-s-line",
10853
+ "ri-arrow-left-s-line",
10854
+ "ri-arrow-right-s-line",
10855
+ "ri-arrow-up-circle-line",
10856
+ "ri-arrow-down-circle-line",
10857
+ "ri-arrow-left-circle-line",
10858
+ "ri-arrow-right-circle-line",
10859
+ "ri-corner-down-right-line",
10860
+ "ri-refresh-line",
10861
+ "ri-loop-left-line",
10862
+ "ri-loop-right-line",
10863
+ "ri-login-circle-line",
10864
+ "ri-logout-circle-line",
10865
+ "ri-menu-line",
10866
+ "ri-more-2-line"
10867
+ ]
10868
+ },
10869
+ {
10870
+ label: "\u7F16\u8F91",
10871
+ icons: [
10872
+ "ri-add-line",
10873
+ "ri-subtract-line",
10874
+ "ri-close-line",
10875
+ "ri-check-line",
10876
+ "ri-edit-line",
10877
+ "ri-delete-bin-line",
10878
+ "ri-file-copy-line",
10879
+ "ri-clipboard-line",
10880
+ "ri-save-line",
10881
+ "ri-magic-line",
10882
+ "ri-filter-line",
10883
+ "ri-drag-move-2-line",
10884
+ "ri-eye-off-line",
10885
+ "ri-eye-2-line",
10886
+ "ri-text",
10887
+ "ri-font-size",
10888
+ "ri-bold",
10889
+ "ri-italic",
10890
+ "ri-underline",
10891
+ "ri-strikethrough"
10892
+ ]
10893
+ },
10894
+ {
10895
+ label: "\u6570\u636E",
10896
+ icons: [
10897
+ "ri-bar-chart-2-line",
10898
+ "ri-line-chart-line",
10899
+ "ri-pie-chart-2-line",
10900
+ "ri-stock-line",
10901
+ "ri-funds-line",
10902
+ "ri-table-line",
10903
+ "ri-list-check",
10904
+ "ri-node-tree",
10905
+ "ri-flow-chart",
10906
+ "ri-percent-line",
10907
+ "ri-calculator-line",
10908
+ "ri-database-2-line",
10909
+ "ri-server-line",
10910
+ "ri-hard-drive-2-line",
10911
+ "ri-cloud-line",
10912
+ "ri-cpu-line",
10913
+ "ri-code-s-slash-line",
10914
+ "ri-terminal-box-line",
10915
+ "ri-history-line",
10916
+ "ri-fingerprint-line"
10917
+ ]
10918
+ },
10919
+ {
10920
+ label: "\u6587\u4EF6",
10921
+ icons: [
10922
+ "ri-file-line",
10923
+ "ri-file-text-line",
10924
+ "ri-folder-line",
10925
+ "ri-folder-open-line",
10926
+ "ri-archive-line",
10927
+ "ri-inbox-line",
10928
+ "ri-image-line",
10929
+ "ri-gallery-line",
10930
+ "ri-video-line",
10931
+ "ri-music-2-line",
10932
+ "ri-movie-line",
10933
+ "ri-camera-line",
10934
+ "ri-file-pdf-line",
10935
+ "ri-file-word-line",
10936
+ "ri-file-excel-line",
10937
+ "ri-file-ppt-line",
10938
+ "ri-printer-line",
10939
+ "ri-attachment-2",
10940
+ "ri-archive-drawer-line",
10941
+ "ri-book-open-line"
10942
+ ]
10943
+ }
10944
+ ];
10945
+ var IconPicker = forwardRef(function IconPicker2({
10946
+ value,
10947
+ onChange,
10948
+ groups = ICON_GROUPS,
10949
+ dialogTitle = "\u9009\u62E9\u56FE\u6807",
10950
+ placeholder = "\u641C\u7D22\u56FE\u6807\u540D\u79F0",
10951
+ renderIcon,
10952
+ renderEmpty,
10953
+ width = 520,
10954
+ emptyText = "\u672A\u9009\u62E9\u56FE\u6807",
10955
+ disabled,
10956
+ className,
10957
+ ...props
10958
+ }, ref) {
10959
+ const [open, setOpen] = useState(false);
10960
+ const [tab, setTab] = useState(groups[0]?.label ?? "");
10961
+ const [keyword, setKeyword] = useState("");
10962
+ const icon = renderIcon ?? ((name) => /* @__PURE__ */ jsx("i", { className: name }));
10963
+ const empty = renderEmpty ?? (() => /* @__PURE__ */ jsx("i", { className: "ri-add-line" }));
10964
+ const activeTab = groups.some((g) => g.label === tab) ? tab : groups[0]?.label ?? "";
10965
+ const list = useMemo(() => {
10966
+ const q = keyword.trim().toLowerCase();
10967
+ if (q) return groups.flatMap((g) => g.icons).filter((name) => name.toLowerCase().includes(q));
10968
+ return groups.find((g) => g.label === activeTab)?.icons ?? [];
10969
+ }, [groups, keyword, activeTab]);
10970
+ const commit = (next) => {
10971
+ onChange?.(next);
10972
+ setOpen(false);
10973
+ };
10974
+ return /* @__PURE__ */ jsxs("div", { ref, className: cx("klun-icon-picker", className), ...props, children: [
10975
+ /* @__PURE__ */ jsx(
10976
+ "button",
10977
+ {
10978
+ type: "button",
10979
+ className: "klun-icon-picker__trigger",
10980
+ disabled,
10981
+ "aria-haspopup": "dialog",
10982
+ "aria-label": "\u9009\u62E9\u56FE\u6807",
10983
+ title: value,
10984
+ onClick: () => {
10985
+ setKeyword("");
10986
+ setOpen(true);
10987
+ },
10988
+ children: value ? icon(value) : empty()
10989
+ }
10990
+ ),
10991
+ /* @__PURE__ */ jsxs(
10992
+ Modal,
10993
+ {
10994
+ open,
10995
+ onClose: () => setOpen(false),
10996
+ title: dialogTitle,
10997
+ width,
10998
+ footer: null,
10999
+ className: "klun-icon-picker__dialog",
11000
+ children: [
11001
+ /* @__PURE__ */ jsxs("label", { className: "klun-icon-picker__search", children: [
11002
+ /* @__PURE__ */ jsx("i", { className: "ri-search-line" }),
11003
+ /* @__PURE__ */ jsx(
11004
+ "input",
11005
+ {
11006
+ value: keyword,
11007
+ placeholder,
11008
+ "aria-label": placeholder,
11009
+ onChange: (e) => setKeyword(e.target.value)
11010
+ }
11011
+ )
11012
+ ] }),
11013
+ /* @__PURE__ */ jsx("div", { className: "klun-icon-picker__tabs", role: "tablist", children: groups.map((group) => /* @__PURE__ */ jsx(
11014
+ "button",
11015
+ {
11016
+ type: "button",
11017
+ role: "tab",
11018
+ "aria-selected": !keyword && activeTab === group.label,
11019
+ className: cx("klun-icon-picker__tab", !keyword && activeTab === group.label && "is-active"),
11020
+ onClick: () => {
11021
+ setTab(group.label);
11022
+ setKeyword("");
11023
+ },
11024
+ children: group.label
11025
+ },
11026
+ group.label
11027
+ )) }),
11028
+ list.length ? /* @__PURE__ */ jsx("div", { className: "klun-icon-picker__grid", children: list.map((name) => /* @__PURE__ */ jsx(
11029
+ "button",
11030
+ {
11031
+ type: "button",
11032
+ className: cx("klun-icon-picker__cell", value === name && "is-selected"),
11033
+ "aria-pressed": value === name,
11034
+ title: name,
11035
+ "aria-label": name,
11036
+ onClick: () => commit(name),
11037
+ children: icon(name)
11038
+ },
11039
+ name
11040
+ )) }) : /* @__PURE__ */ jsx("div", { className: "klun-icon-picker__empty", children: "\u6CA1\u6709\u5339\u914D\u7684\u56FE\u6807" }),
11041
+ /* @__PURE__ */ jsxs("div", { className: "klun-icon-picker__foot", children: [
11042
+ /* @__PURE__ */ jsxs("span", { className: "klun-icon-picker__current", children: [
11043
+ value ? icon(value) : empty(),
11044
+ /* @__PURE__ */ jsx("span", { className: "klun-icon-picker__name", children: value || emptyText })
11045
+ ] }),
11046
+ /* @__PURE__ */ jsx("span", { className: "klun-icon-picker__actions", children: value ? /* @__PURE__ */ jsx("button", { type: "button", className: "klun-icon-picker__btn", onClick: () => commit(""), children: "\u6E05\u9664" }) : null })
11047
+ ] })
11048
+ ]
11049
+ }
11050
+ )
11051
+ ] });
11052
+ });
9102
11053
  var ImageUpload = forwardRef(function ImageUpload2({
9103
11054
  src,
9104
11055
  shape = "circle",
@@ -9306,7 +11257,77 @@ var InlineSelect = forwardRef(
9306
11257
  );
9307
11258
  }
9308
11259
  );
9309
- InlineSelect.displayName = "InlineSelect";
11260
+ InlineSelect.displayName = "InlineSelect";
11261
+ var InputGroup = forwardRef(
11262
+ function InputGroup2({ className, ...props }, ref) {
11263
+ return /* @__PURE__ */ jsx(
11264
+ "div",
11265
+ {
11266
+ ref,
11267
+ role: "group",
11268
+ className: cx("klun-input-group", className),
11269
+ ...props
11270
+ }
11271
+ );
11272
+ }
11273
+ );
11274
+ InputGroup.displayName = "InputGroup";
11275
+ var InputGroupInput = forwardRef(
11276
+ function InputGroupInput2({ className, ...props }, ref) {
11277
+ return /* @__PURE__ */ jsx("input", { ref, className: cx("klun-input-group__input", className), ...props });
11278
+ }
11279
+ );
11280
+ InputGroupInput.displayName = "InputGroupInput";
11281
+ var InputGroupAddon = forwardRef(
11282
+ function InputGroupAddon2({ align = "inline-start", className, onMouseDown, onClick, ...props }, ref) {
11283
+ return /* @__PURE__ */ jsx(
11284
+ "div",
11285
+ {
11286
+ ref,
11287
+ role: "group",
11288
+ "data-align": align,
11289
+ className: cx("klun-input-group__addon", className),
11290
+ onMouseDown: (event) => {
11291
+ if (!event.target.closest("button")) event.preventDefault();
11292
+ onMouseDown?.(event);
11293
+ },
11294
+ onClick: (event) => {
11295
+ if (!event.target.closest("button")) {
11296
+ event.currentTarget.parentElement?.querySelector("input")?.focus();
11297
+ }
11298
+ onClick?.(event);
11299
+ },
11300
+ ...props
11301
+ }
11302
+ );
11303
+ }
11304
+ );
11305
+ InputGroupAddon.displayName = "InputGroupAddon";
11306
+ var InputGroupText = forwardRef(
11307
+ function InputGroupText2({ className, ...props }, ref) {
11308
+ return /* @__PURE__ */ jsx("span", { ref, className: cx("klun-input-group__text", className), ...props });
11309
+ }
11310
+ );
11311
+ InputGroupText.displayName = "InputGroupText";
11312
+ var InputGroupButton = forwardRef(
11313
+ function InputGroupButton2({ size = "xsmall", variant = "ghost", type = "button", className, ...props }, ref) {
11314
+ return /* @__PURE__ */ jsx(
11315
+ "button",
11316
+ {
11317
+ ref,
11318
+ type,
11319
+ className: cx(
11320
+ "klun-input-group__button",
11321
+ `klun-input-group__button--${size}`,
11322
+ `klun-input-group__button--${variant}`,
11323
+ className
11324
+ ),
11325
+ ...props
11326
+ }
11327
+ );
11328
+ }
11329
+ );
11330
+ InputGroupButton.displayName = "InputGroupButton";
9310
11331
  var Label = forwardRef(function Label2({ children, required = false, sublabel, disabled = false, className, ...props }, ref) {
9311
11332
  return /* @__PURE__ */ jsxs(
9312
11333
  "label",
@@ -9773,11 +11794,15 @@ var Select = forwardRef(function Select2({
9773
11794
  value,
9774
11795
  defaultValue,
9775
11796
  onChange,
11797
+ "aria-label": ariaLabel,
11798
+ "aria-labelledby": ariaLabelledBy,
9776
11799
  ...props
9777
11800
  }, ref) {
9778
11801
  const size = useSize(sizeProp);
9779
11802
  const rootRef = useRef(null);
9780
11803
  const s = useSelect({ children, value, defaultValue, disabled: disabled || loading, rootRef });
11804
+ const { select: selectLocale } = useLocale();
11805
+ const fallbackLabel = !ariaLabel && !ariaLabelledBy && !s.selected && !placeholder ? selectLocale?.placeholder ?? "Select" : void 0;
9781
11806
  return /* @__PURE__ */ jsxs("div", { ref: rootRef, className: cx("klun-select", `klun-select--${size}`, className), style, children: [
9782
11807
  /* @__PURE__ */ jsxs(
9783
11808
  "button",
@@ -9789,6 +11814,8 @@ var Select = forwardRef(function Select2({
9789
11814
  onKeyDown: s.triggerKeyDown,
9790
11815
  "aria-haspopup": "listbox",
9791
11816
  "aria-expanded": s.open,
11817
+ "aria-label": ariaLabel ?? fallbackLabel,
11818
+ "aria-labelledby": ariaLabelledBy,
9792
11819
  "data-open": s.open || void 0,
9793
11820
  className: cx(
9794
11821
  "klun-select__trigger",
@@ -11120,40 +13147,537 @@ var SegmentedControl = forwardRef(
11120
13147
  }) });
11121
13148
  }
11122
13149
  );
11123
- SegmentedControl.displayName = "SegmentedControl";
11124
- var StepIndicator = forwardRef(function StepIndicator2({ steps = [], current = 0, className, ...props }, ref) {
11125
- return /* @__PURE__ */ jsx("div", { ref, className: cx("klun-step-indicator", className), ...props, children: steps.map((s, i) => {
11126
- const done = i < current;
11127
- const active = i === current;
11128
- const state = done ? "complete" : active ? "active" : "upcoming";
11129
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [
11130
- /* @__PURE__ */ jsxs("div", { className: "klun-step-indicator__step", "data-state": state, children: [
11131
- /* @__PURE__ */ jsx("div", { className: "klun-step-indicator__marker", children: done ? /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx(
11132
- "path",
11133
- {
11134
- d: "M3.5 8.5l3 3 6-6.5",
11135
- stroke: "currentColor",
11136
- strokeWidth: "2",
11137
- strokeLinecap: "round",
11138
- strokeLinejoin: "round"
11139
- }
11140
- ) }) : i + 1 }),
11141
- /* @__PURE__ */ jsxs("div", { className: "klun-step-indicator__text", children: [
11142
- /* @__PURE__ */ jsx("div", { className: "klun-step-indicator__label", children: s.label }),
11143
- s.description ? /* @__PURE__ */ jsx("div", { className: "klun-step-indicator__description", children: s.description }) : null
11144
- ] })
11145
- ] }),
11146
- i < steps.length - 1 ? /* @__PURE__ */ jsx(
11147
- "div",
11148
- {
11149
- className: "klun-step-indicator__connector",
11150
- "data-state": i < current ? "complete" : "upcoming"
11151
- }
11152
- ) : null
11153
- ] }, i);
11154
- }) });
11155
- });
11156
- StepIndicator.displayName = "StepIndicator";
13150
+ SegmentedControl.displayName = "SegmentedControl";
13151
+ function RailButton({
13152
+ item,
13153
+ active,
13154
+ onClick
13155
+ }) {
13156
+ return /* @__PURE__ */ jsx(
13157
+ "button",
13158
+ {
13159
+ type: "button",
13160
+ className: "klun-sidebar__nav-button",
13161
+ "data-active": active ? "" : void 0,
13162
+ disabled: item.disabled,
13163
+ "aria-current": active ? "page" : void 0,
13164
+ "aria-label": item.label,
13165
+ title: item.label,
13166
+ onClick,
13167
+ children: item.icon
13168
+ }
13169
+ );
13170
+ }
13171
+ function MenuItemRow({
13172
+ item,
13173
+ expanded,
13174
+ collapsed,
13175
+ active,
13176
+ onToggle,
13177
+ onSelect
13178
+ }) {
13179
+ const isExpander = Array.isArray(item.children) && item.children.length > 0;
13180
+ const title = collapsed && typeof item.label === "string" ? item.label : void 0;
13181
+ return /* @__PURE__ */ jsxs(
13182
+ "button",
13183
+ {
13184
+ type: "button",
13185
+ className: "klun-sidebar__item",
13186
+ "data-active": active ? "" : void 0,
13187
+ disabled: item.disabled,
13188
+ "aria-expanded": isExpander ? expanded : void 0,
13189
+ "aria-current": !isExpander && active ? "page" : void 0,
13190
+ title,
13191
+ onClick: () => {
13192
+ if (isExpander) onToggle();
13193
+ else onSelect?.(item.key, item);
13194
+ },
13195
+ children: [
13196
+ item.icon != null && /* @__PURE__ */ jsx("span", { className: "klun-sidebar__item-icon", children: item.icon }),
13197
+ /* @__PURE__ */ jsx("span", { className: "klun-sidebar__item-label", children: item.label }),
13198
+ isExpander && /* @__PURE__ */ jsx("span", { className: "klun-sidebar__item-caret", "aria-hidden": "true", children: /* @__PURE__ */ jsx("i", { className: "ri-arrow-down-s-line" }) })
13199
+ ]
13200
+ }
13201
+ );
13202
+ }
13203
+ function SubMenuItemRow({
13204
+ item,
13205
+ onSelect
13206
+ }) {
13207
+ return /* @__PURE__ */ jsx("div", { className: "klun-sidebar__sub-item", children: /* @__PURE__ */ jsx(
13208
+ "button",
13209
+ {
13210
+ type: "button",
13211
+ className: "klun-sidebar__sub-button",
13212
+ disabled: item.disabled,
13213
+ onClick: () => onSelect?.(item.key, item),
13214
+ children: item.label
13215
+ }
13216
+ ) });
13217
+ }
13218
+ var Sidebar = forwardRef(function Sidebar2({
13219
+ brand,
13220
+ navItems = [],
13221
+ footNavItems = [],
13222
+ avatar,
13223
+ navKey,
13224
+ defaultNavKey,
13225
+ onNavChange,
13226
+ title,
13227
+ sections = [],
13228
+ selectedKey,
13229
+ onSelect,
13230
+ defaultExpandedKeys = [],
13231
+ searchable = false,
13232
+ searchPlaceholder = "Search",
13233
+ onSearch,
13234
+ collapsed: collapsedProp,
13235
+ defaultCollapsed = false,
13236
+ onCollapse,
13237
+ className,
13238
+ ...props
13239
+ }, ref) {
13240
+ const [internalNav, setInternalNav] = useState(defaultNavKey);
13241
+ const [internalCollapsed, setInternalCollapsed] = useState(defaultCollapsed);
13242
+ const [expandedKeys, setExpandedKeys] = useState(
13243
+ () => new Set(defaultExpandedKeys)
13244
+ );
13245
+ const [query, setQuery] = useState("");
13246
+ const navControlled = navKey !== void 0;
13247
+ const activeNav = navControlled ? navKey : internalNav;
13248
+ const collapseControlled = collapsedProp !== void 0;
13249
+ const collapsed = collapseControlled ? collapsedProp : internalCollapsed;
13250
+ const selectNav = (key) => {
13251
+ if (!navControlled) setInternalNav(key);
13252
+ onNavChange?.(key);
13253
+ };
13254
+ const toggleCollapse = () => {
13255
+ const next = !collapsed;
13256
+ if (!collapseControlled) setInternalCollapsed(next);
13257
+ onCollapse?.(next);
13258
+ };
13259
+ const toggleExpanded = (key) => {
13260
+ setExpandedKeys((prev) => {
13261
+ const next = new Set(prev);
13262
+ if (next.has(key)) next.delete(key);
13263
+ else next.add(key);
13264
+ return next;
13265
+ });
13266
+ };
13267
+ return /* @__PURE__ */ jsxs(
13268
+ "div",
13269
+ {
13270
+ ref,
13271
+ className: cx("klun-sidebar", collapsed && "klun-sidebar--collapsed", className),
13272
+ "data-collapsed": collapsed ? "" : void 0,
13273
+ ...props,
13274
+ children: [
13275
+ /* @__PURE__ */ jsxs("nav", { className: "klun-sidebar__rail", "aria-label": "Primary", children: [
13276
+ brand != null && /* @__PURE__ */ jsx("div", { className: "klun-sidebar__brand", children: brand }),
13277
+ /* @__PURE__ */ jsx("div", { className: "klun-sidebar__nav", children: navItems.map((item) => /* @__PURE__ */ jsx(
13278
+ RailButton,
13279
+ {
13280
+ item,
13281
+ active: item.key === activeNav,
13282
+ onClick: () => selectNav(item.key)
13283
+ },
13284
+ item.key
13285
+ )) }),
13286
+ /* @__PURE__ */ jsxs("div", { className: "klun-sidebar__rail-foot", children: [
13287
+ footNavItems.map((item) => /* @__PURE__ */ jsx(
13288
+ RailButton,
13289
+ {
13290
+ item,
13291
+ active: item.key === activeNav,
13292
+ onClick: () => selectNav(item.key)
13293
+ },
13294
+ item.key
13295
+ )),
13296
+ avatar != null && /* @__PURE__ */ jsx("div", { className: "klun-sidebar__avatar", children: avatar })
13297
+ ] })
13298
+ ] }),
13299
+ /* @__PURE__ */ jsxs("div", { className: "klun-sidebar__panel", children: [
13300
+ /* @__PURE__ */ jsxs("div", { className: "klun-sidebar__panel-head", children: [
13301
+ !collapsed && /* @__PURE__ */ jsx("div", { className: "klun-sidebar__panel-title", children: title }),
13302
+ /* @__PURE__ */ jsx(
13303
+ "button",
13304
+ {
13305
+ type: "button",
13306
+ className: "klun-sidebar__collapse",
13307
+ "aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
13308
+ "aria-expanded": !collapsed,
13309
+ onClick: toggleCollapse,
13310
+ children: /* @__PURE__ */ jsx("i", { className: "ri-arrow-left-s-line", "aria-hidden": "true" })
13311
+ }
13312
+ )
13313
+ ] }),
13314
+ searchable && /* @__PURE__ */ jsxs("div", { className: "klun-sidebar__search", children: [
13315
+ /* @__PURE__ */ jsx("span", { className: "klun-sidebar__search-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx("i", { className: "ri-search-line" }) }),
13316
+ /* @__PURE__ */ jsx(
13317
+ "input",
13318
+ {
13319
+ type: "text",
13320
+ className: "klun-sidebar__search-input",
13321
+ placeholder: searchPlaceholder,
13322
+ value: query,
13323
+ tabIndex: collapsed ? -1 : 0,
13324
+ onChange: (e) => {
13325
+ setQuery(e.target.value);
13326
+ onSearch?.(e.target.value);
13327
+ }
13328
+ }
13329
+ )
13330
+ ] }),
13331
+ /* @__PURE__ */ jsx("div", { className: "klun-sidebar__scroll", children: sections.map((section, sectionIndex) => /* @__PURE__ */ jsxs("div", { className: "klun-sidebar__section", children: [
13332
+ section.title != null && /* @__PURE__ */ jsx("div", { className: "klun-sidebar__section-title", children: section.title }),
13333
+ section.items.map((item) => {
13334
+ const isExpander = Array.isArray(item.children) && item.children.length > 0;
13335
+ const expanded = expandedKeys.has(item.key);
13336
+ return /* @__PURE__ */ jsxs("div", { className: "klun-sidebar__group", children: [
13337
+ /* @__PURE__ */ jsx(
13338
+ MenuItemRow,
13339
+ {
13340
+ item,
13341
+ expanded,
13342
+ collapsed,
13343
+ active: item.key === selectedKey,
13344
+ onToggle: () => toggleExpanded(item.key),
13345
+ onSelect
13346
+ }
13347
+ ),
13348
+ isExpander && expanded && !collapsed && /* @__PURE__ */ jsx("div", { className: "klun-sidebar__sub", children: item.children.map((child) => /* @__PURE__ */ jsx(SubMenuItemRow, { item: child, onSelect }, child.key)) })
13349
+ ] }, item.key);
13350
+ })
13351
+ ] }, sectionIndex)) })
13352
+ ] })
13353
+ ]
13354
+ }
13355
+ );
13356
+ });
13357
+ Sidebar.displayName = "Sidebar";
13358
+ var StepIndicator = forwardRef(function StepIndicator2({ steps = [], current = 0, className, ...props }, ref) {
13359
+ return /* @__PURE__ */ jsx("div", { ref, className: cx("klun-step-indicator", className), ...props, children: steps.map((s, i) => {
13360
+ const done = i < current;
13361
+ const active = i === current;
13362
+ const state = done ? "complete" : active ? "active" : "upcoming";
13363
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
13364
+ /* @__PURE__ */ jsxs("div", { className: "klun-step-indicator__step", "data-state": state, children: [
13365
+ /* @__PURE__ */ jsx("div", { className: "klun-step-indicator__marker", children: done ? /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx(
13366
+ "path",
13367
+ {
13368
+ d: "M3.5 8.5l3 3 6-6.5",
13369
+ stroke: "currentColor",
13370
+ strokeWidth: "2",
13371
+ strokeLinecap: "round",
13372
+ strokeLinejoin: "round"
13373
+ }
13374
+ ) }) : i + 1 }),
13375
+ /* @__PURE__ */ jsxs("div", { className: "klun-step-indicator__text", children: [
13376
+ /* @__PURE__ */ jsx("div", { className: "klun-step-indicator__label", children: s.label }),
13377
+ s.description ? /* @__PURE__ */ jsx("div", { className: "klun-step-indicator__description", children: s.description }) : null
13378
+ ] })
13379
+ ] }),
13380
+ i < steps.length - 1 ? /* @__PURE__ */ jsx(
13381
+ "div",
13382
+ {
13383
+ className: "klun-step-indicator__connector",
13384
+ "data-state": i < current ? "complete" : "upcoming"
13385
+ }
13386
+ ) : null
13387
+ ] }, i);
13388
+ }) });
13389
+ });
13390
+ StepIndicator.displayName = "StepIndicator";
13391
+ var StepperContext = createContext(void 0);
13392
+ var StepItemContext = createContext(void 0);
13393
+ function useStepper() {
13394
+ const ctx = useContext(StepperContext);
13395
+ if (!ctx) throw new Error("useStepper must be used within a <Stepper>");
13396
+ return ctx;
13397
+ }
13398
+ function useStepItem() {
13399
+ const ctx = useContext(StepItemContext);
13400
+ if (!ctx) throw new Error("useStepItem must be used within a <StepperItem>");
13401
+ return ctx;
13402
+ }
13403
+ var Stepper = forwardRef(function Stepper2({
13404
+ defaultValue = 1,
13405
+ value,
13406
+ onValueChange,
13407
+ orientation = "horizontal",
13408
+ className,
13409
+ children,
13410
+ indicators = {},
13411
+ ...props
13412
+ }, ref) {
13413
+ const [internalStep, setInternalStep] = useState(defaultValue);
13414
+ const [triggerNodes, setTriggerNodes] = useState([]);
13415
+ const registerTrigger = useCallback((node) => {
13416
+ setTriggerNodes((prev) => prev.includes(node) ? prev : [...prev, node]);
13417
+ }, []);
13418
+ const unregisterTrigger = useCallback((node) => {
13419
+ setTriggerNodes((prev) => prev.filter((n) => n !== node));
13420
+ }, []);
13421
+ const activeStep = value ?? internalStep;
13422
+ const setActiveStep = useCallback(
13423
+ (step) => {
13424
+ if (value === void 0) setInternalStep(step);
13425
+ onValueChange?.(step);
13426
+ },
13427
+ [value, onValueChange]
13428
+ );
13429
+ const focusTrigger = (index) => {
13430
+ triggerNodes[index]?.focus();
13431
+ };
13432
+ const focusNext = (currentIndex) => focusTrigger((currentIndex + 1) % triggerNodes.length);
13433
+ const focusPrev = (currentIndex) => focusTrigger((currentIndex - 1 + triggerNodes.length) % triggerNodes.length);
13434
+ const contextValue = useMemo(
13435
+ () => ({
13436
+ activeStep,
13437
+ setActiveStep,
13438
+ orientation,
13439
+ registerTrigger,
13440
+ unregisterTrigger,
13441
+ triggerNodes,
13442
+ focusNext,
13443
+ focusPrev,
13444
+ focusFirst: () => focusTrigger(0),
13445
+ focusLast: () => focusTrigger(triggerNodes.length - 1),
13446
+ indicators
13447
+ }),
13448
+ // eslint-disable-next-line react-hooks/exhaustive-deps
13449
+ [activeStep, setActiveStep, orientation, registerTrigger, unregisterTrigger, triggerNodes, indicators]
13450
+ );
13451
+ return /* @__PURE__ */ jsx(StepperContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
13452
+ "div",
13453
+ {
13454
+ ref,
13455
+ role: "tablist",
13456
+ "aria-orientation": orientation,
13457
+ "data-orientation": orientation,
13458
+ className: cx("klun-stepper", className),
13459
+ ...props,
13460
+ children
13461
+ }
13462
+ ) });
13463
+ });
13464
+ Stepper.displayName = "Stepper";
13465
+ var StepperNav = forwardRef(
13466
+ function StepperNav2({ className, ...props }, ref) {
13467
+ const { orientation, activeStep } = useStepper();
13468
+ return /* @__PURE__ */ jsx(
13469
+ "nav",
13470
+ {
13471
+ ref,
13472
+ "data-orientation": orientation,
13473
+ "data-state": activeStep,
13474
+ className: cx("klun-stepper__nav", className),
13475
+ ...props
13476
+ }
13477
+ );
13478
+ }
13479
+ );
13480
+ StepperNav.displayName = "StepperNav";
13481
+ var StepperItem = forwardRef(
13482
+ function StepperItem2({ step, completed = false, disabled = false, loading = false, className, children, ...props }, ref) {
13483
+ const { activeStep } = useStepper();
13484
+ const state = completed || step < activeStep ? "completed" : activeStep === step ? "active" : "inactive";
13485
+ const isLoading = loading && step === activeStep;
13486
+ return /* @__PURE__ */ jsx(StepItemContext.Provider, { value: { step, state, isDisabled: disabled, isLoading }, children: /* @__PURE__ */ jsx(
13487
+ "div",
13488
+ {
13489
+ ref,
13490
+ "data-state": state,
13491
+ "data-loading": isLoading ? "" : void 0,
13492
+ className: cx("klun-stepper__item", className),
13493
+ ...props,
13494
+ children
13495
+ }
13496
+ ) });
13497
+ }
13498
+ );
13499
+ StepperItem.displayName = "StepperItem";
13500
+ var StepperTrigger = forwardRef(
13501
+ function StepperTrigger2({ className, children, tabIndex, onKeyDown, ...props }, ref) {
13502
+ const { step, state, isDisabled, isLoading } = useStepItem();
13503
+ const {
13504
+ activeStep,
13505
+ setActiveStep,
13506
+ registerTrigger,
13507
+ unregisterTrigger,
13508
+ triggerNodes,
13509
+ focusNext,
13510
+ focusPrev,
13511
+ focusFirst,
13512
+ focusLast
13513
+ } = useStepper();
13514
+ const buttonRef = useRef(null);
13515
+ useEffect(() => {
13516
+ const node = buttonRef.current;
13517
+ if (!node) return;
13518
+ registerTrigger(node);
13519
+ return () => unregisterTrigger(node);
13520
+ }, [registerTrigger, unregisterTrigger]);
13521
+ const isSelected = activeStep === step;
13522
+ return /* @__PURE__ */ jsx(
13523
+ "button",
13524
+ {
13525
+ ref: (node) => {
13526
+ buttonRef.current = node;
13527
+ if (typeof ref === "function") ref(node);
13528
+ else if (ref) ref.current = node;
13529
+ },
13530
+ type: "button",
13531
+ role: "tab",
13532
+ id: `stepper-tab-${step}`,
13533
+ "aria-selected": isSelected,
13534
+ "aria-controls": `stepper-panel-${step}`,
13535
+ tabIndex: typeof tabIndex === "number" ? tabIndex : isSelected ? 0 : -1,
13536
+ "data-state": state,
13537
+ "data-loading": isLoading ? "" : void 0,
13538
+ disabled: isDisabled,
13539
+ className: cx("klun-stepper__trigger", className),
13540
+ onClick: () => setActiveStep(step),
13541
+ onKeyDown: (event) => {
13542
+ switch (event.key) {
13543
+ case "ArrowRight":
13544
+ case "ArrowDown": {
13545
+ event.preventDefault();
13546
+ const index = triggerNodes.findIndex((n) => n === buttonRef.current);
13547
+ if (index !== -1) focusNext(index);
13548
+ break;
13549
+ }
13550
+ case "ArrowLeft":
13551
+ case "ArrowUp": {
13552
+ event.preventDefault();
13553
+ const index = triggerNodes.findIndex((n) => n === buttonRef.current);
13554
+ if (index !== -1) focusPrev(index);
13555
+ break;
13556
+ }
13557
+ case "Home":
13558
+ event.preventDefault();
13559
+ focusFirst();
13560
+ break;
13561
+ case "End":
13562
+ event.preventDefault();
13563
+ focusLast();
13564
+ break;
13565
+ case "Enter":
13566
+ case " ":
13567
+ event.preventDefault();
13568
+ setActiveStep(step);
13569
+ break;
13570
+ }
13571
+ onKeyDown?.(event);
13572
+ },
13573
+ ...props,
13574
+ children
13575
+ }
13576
+ );
13577
+ }
13578
+ );
13579
+ StepperTrigger.displayName = "StepperTrigger";
13580
+ var StepperIndicator = forwardRef(
13581
+ function StepperIndicator2({ className, children, ...props }, ref) {
13582
+ const { state, isLoading } = useStepItem();
13583
+ const { indicators } = useStepper();
13584
+ const custom = isLoading && indicators.loading || state === "completed" && indicators.completed || state === "active" && indicators.active || state === "inactive" && indicators.inactive;
13585
+ return /* @__PURE__ */ jsx(
13586
+ "div",
13587
+ {
13588
+ ref,
13589
+ "data-state": state,
13590
+ "data-loading": isLoading ? "" : void 0,
13591
+ className: cx("klun-stepper__indicator", className),
13592
+ ...props,
13593
+ children: custom ?? children
13594
+ }
13595
+ );
13596
+ }
13597
+ );
13598
+ StepperIndicator.displayName = "StepperIndicator";
13599
+ var StepperSeparator = forwardRef(
13600
+ function StepperSeparator2({ className, ...props }, ref) {
13601
+ const { state } = useStepItem();
13602
+ return /* @__PURE__ */ jsx(
13603
+ "div",
13604
+ {
13605
+ ref,
13606
+ "aria-hidden": "true",
13607
+ "data-state": state,
13608
+ className: cx("klun-stepper__separator", className),
13609
+ ...props
13610
+ }
13611
+ );
13612
+ }
13613
+ );
13614
+ StepperSeparator.displayName = "StepperSeparator";
13615
+ var StepperTitle = forwardRef(
13616
+ function StepperTitle2({ className, ...props }, ref) {
13617
+ const { state } = useStepItem();
13618
+ return /* @__PURE__ */ jsx(
13619
+ "h3",
13620
+ {
13621
+ ref,
13622
+ "data-state": state,
13623
+ className: cx("klun-stepper__title", className),
13624
+ ...props
13625
+ }
13626
+ );
13627
+ }
13628
+ );
13629
+ StepperTitle.displayName = "StepperTitle";
13630
+ var StepperDescription = forwardRef(
13631
+ function StepperDescription2({ className, ...props }, ref) {
13632
+ const { state } = useStepItem();
13633
+ return /* @__PURE__ */ jsx(
13634
+ "div",
13635
+ {
13636
+ ref,
13637
+ "data-state": state,
13638
+ className: cx("klun-stepper__description", className),
13639
+ ...props
13640
+ }
13641
+ );
13642
+ }
13643
+ );
13644
+ StepperDescription.displayName = "StepperDescription";
13645
+ var StepperPanel = forwardRef(
13646
+ function StepperPanel2({ className, ...props }, ref) {
13647
+ const { activeStep } = useStepper();
13648
+ return /* @__PURE__ */ jsx(
13649
+ "div",
13650
+ {
13651
+ ref,
13652
+ "data-state": activeStep,
13653
+ className: cx("klun-stepper__panel", className),
13654
+ ...props
13655
+ }
13656
+ );
13657
+ }
13658
+ );
13659
+ StepperPanel.displayName = "StepperPanel";
13660
+ var StepperContent = forwardRef(
13661
+ function StepperContent2({ value, forceMount = false, className, children, ...props }, ref) {
13662
+ const { activeStep } = useStepper();
13663
+ const isActive = value === activeStep;
13664
+ if (!forceMount && !isActive) return null;
13665
+ return /* @__PURE__ */ jsx(
13666
+ "div",
13667
+ {
13668
+ ref,
13669
+ id: `stepper-panel-${value}`,
13670
+ role: "tabpanel",
13671
+ "aria-labelledby": `stepper-tab-${value}`,
13672
+ hidden: forceMount && !isActive,
13673
+ className: cx("klun-stepper__content", className),
13674
+ ...props,
13675
+ children
13676
+ }
13677
+ );
13678
+ }
13679
+ );
13680
+ StepperContent.displayName = "StepperContent";
11157
13681
  var Wizard = forwardRef(function Wizard2({
11158
13682
  steps = [],
11159
13683
  current,
@@ -11779,6 +14303,208 @@ var Dropdown = forwardRef(function Dropdown2({ trigger, items: items2 = [], alig
11779
14303
  ] });
11780
14304
  });
11781
14305
  Dropdown.displayName = "Dropdown";
14306
+ var GAP4 = 6;
14307
+ var MARGIN2 = 8;
14308
+ var HoverCardContext = createContext(null);
14309
+ function useHoverCard() {
14310
+ const ctx = useContext(HoverCardContext);
14311
+ if (!ctx) throw new Error("HoverCard parts must be used inside <HoverCard>");
14312
+ return ctx;
14313
+ }
14314
+ function HoverCard({
14315
+ children,
14316
+ openDelay = 300,
14317
+ closeDelay = 150,
14318
+ open: openProp,
14319
+ onOpenChange
14320
+ }) {
14321
+ const [internalOpen, setInternalOpen] = useState(false);
14322
+ const controlled = openProp !== void 0;
14323
+ const open = controlled ? openProp : internalOpen;
14324
+ const triggerRef = useRef(null);
14325
+ const openTimer = useRef(null);
14326
+ const closeTimer = useRef(null);
14327
+ const clearTimers = () => {
14328
+ if (openTimer.current) clearTimeout(openTimer.current);
14329
+ if (closeTimer.current) clearTimeout(closeTimer.current);
14330
+ };
14331
+ const setOpen = (next) => {
14332
+ if (!controlled) setInternalOpen(next);
14333
+ onOpenChange?.(next);
14334
+ };
14335
+ const show = () => {
14336
+ clearTimers();
14337
+ if (open) return;
14338
+ openTimer.current = setTimeout(() => setOpen(true), openDelay);
14339
+ };
14340
+ const hide = () => {
14341
+ clearTimers();
14342
+ closeTimer.current = setTimeout(() => setOpen(false), closeDelay);
14343
+ };
14344
+ const cancelHide = () => {
14345
+ clearTimers();
14346
+ };
14347
+ useEffect(() => clearTimers, []);
14348
+ useEffect(() => {
14349
+ if (!open) return;
14350
+ const onKey = (event) => {
14351
+ if (event.key === "Escape") setOpen(false);
14352
+ };
14353
+ document.addEventListener("keydown", onKey);
14354
+ return () => document.removeEventListener("keydown", onKey);
14355
+ }, [open]);
14356
+ return /* @__PURE__ */ jsx(
14357
+ HoverCardContext.Provider,
14358
+ {
14359
+ value: {
14360
+ open,
14361
+ show,
14362
+ hide,
14363
+ cancelHide,
14364
+ triggerRef,
14365
+ registerTrigger: (el) => {
14366
+ triggerRef.current = el;
14367
+ }
14368
+ },
14369
+ children
14370
+ }
14371
+ );
14372
+ }
14373
+ var HoverCardTrigger = forwardRef(
14374
+ function HoverCardTrigger2({ className, onMouseEnter, onMouseLeave, onFocus, onBlur, ...props }, ref) {
14375
+ const ctx = useHoverCard();
14376
+ return /* @__PURE__ */ jsx(
14377
+ "span",
14378
+ {
14379
+ ref: (node) => {
14380
+ ctx.registerTrigger(node);
14381
+ if (typeof ref === "function") ref(node);
14382
+ else if (ref) ref.current = node;
14383
+ },
14384
+ className: cx("klun-hover-card__trigger", className),
14385
+ onMouseEnter: (event) => {
14386
+ ctx.show();
14387
+ onMouseEnter?.(event);
14388
+ },
14389
+ onMouseLeave: (event) => {
14390
+ ctx.hide();
14391
+ onMouseLeave?.(event);
14392
+ },
14393
+ onFocus: (event) => {
14394
+ ctx.show();
14395
+ onFocus?.(event);
14396
+ },
14397
+ onBlur: (event) => {
14398
+ ctx.hide();
14399
+ onBlur?.(event);
14400
+ },
14401
+ ...props
14402
+ }
14403
+ );
14404
+ }
14405
+ );
14406
+ HoverCardTrigger.displayName = "HoverCardTrigger";
14407
+ function alignAxis(start, size, panelSize, align) {
14408
+ if (align === "start") return start;
14409
+ if (align === "end") return start + size - panelSize;
14410
+ return start + size / 2 - panelSize / 2;
14411
+ }
14412
+ var HoverCardContent = forwardRef(
14413
+ function HoverCardContent2({
14414
+ side = "bottom",
14415
+ align = "center",
14416
+ className,
14417
+ onClick,
14418
+ onContextMenu,
14419
+ onAuxClick,
14420
+ onDoubleClick,
14421
+ ...props
14422
+ }, ref) {
14423
+ const ctx = useHoverCard();
14424
+ const panelRef = useRef(null);
14425
+ const [pos, setPos] = useState(null);
14426
+ useLayoutEffect(() => {
14427
+ if (!ctx.open) {
14428
+ setPos(null);
14429
+ return;
14430
+ }
14431
+ const place2 = () => {
14432
+ const trigger = ctx.triggerRef.current;
14433
+ const panel = panelRef.current;
14434
+ if (!trigger || !panel) return;
14435
+ const tr = trigger.getBoundingClientRect();
14436
+ const pw = panel.offsetWidth;
14437
+ const ph = panel.offsetHeight;
14438
+ const vw = document.documentElement.clientWidth;
14439
+ const vh = document.documentElement.clientHeight;
14440
+ let top;
14441
+ let left;
14442
+ switch (side) {
14443
+ case "top":
14444
+ top = tr.top - ph - GAP4;
14445
+ left = alignAxis(tr.left, tr.width, pw, align);
14446
+ break;
14447
+ case "left":
14448
+ left = tr.left - pw - GAP4;
14449
+ top = alignAxis(tr.top, tr.height, ph, align);
14450
+ break;
14451
+ case "right":
14452
+ left = tr.right + GAP4;
14453
+ top = alignAxis(tr.top, tr.height, ph, align);
14454
+ break;
14455
+ default:
14456
+ top = tr.bottom + GAP4;
14457
+ left = alignAxis(tr.left, tr.width, pw, align);
14458
+ }
14459
+ left = Math.max(MARGIN2, Math.min(left, vw - pw - MARGIN2));
14460
+ top = Math.max(MARGIN2, Math.min(top, vh - ph - MARGIN2));
14461
+ setPos({ top, left });
14462
+ };
14463
+ place2();
14464
+ window.addEventListener("scroll", place2, true);
14465
+ window.addEventListener("resize", place2);
14466
+ return () => {
14467
+ window.removeEventListener("scroll", place2, true);
14468
+ window.removeEventListener("resize", place2);
14469
+ };
14470
+ }, [ctx.open, side, align, ctx.triggerRef]);
14471
+ if (!ctx.open || typeof document === "undefined") return null;
14472
+ const stop = (forwarded) => (event) => {
14473
+ event.stopPropagation();
14474
+ forwarded?.(event);
14475
+ };
14476
+ const style = {
14477
+ position: "fixed",
14478
+ top: pos?.top ?? 0,
14479
+ left: pos?.left ?? 0,
14480
+ visibility: pos ? "visible" : "hidden"
14481
+ };
14482
+ return createPortal(
14483
+ /* @__PURE__ */ jsx(
14484
+ "div",
14485
+ {
14486
+ ref: (node) => {
14487
+ panelRef.current = node;
14488
+ if (typeof ref === "function") ref(node);
14489
+ else if (ref) ref.current = node;
14490
+ },
14491
+ className: cx("klun-hover-card__panel", className),
14492
+ "data-side": side,
14493
+ style,
14494
+ onMouseEnter: ctx.cancelHide,
14495
+ onMouseLeave: ctx.hide,
14496
+ onClick: stop(onClick),
14497
+ onContextMenu: stop(onContextMenu),
14498
+ onAuxClick: stop(onAuxClick),
14499
+ onDoubleClick: stop(onDoubleClick),
14500
+ ...props
14501
+ }
14502
+ ),
14503
+ document.body
14504
+ );
14505
+ }
14506
+ );
14507
+ HoverCardContent.displayName = "HoverCardContent";
11782
14508
  var MIN = 0.25;
11783
14509
  var MAX = 6;
11784
14510
  var clampScale = (s) => Math.min(MAX, Math.max(MIN, +s.toFixed(2)));
@@ -11919,155 +14645,8 @@ function ImageViewer({
11919
14645
  );
11920
14646
  }
11921
14647
  ImageViewer.displayName = "ImageViewer";
11922
- var Modal = forwardRef(function Modal2({
11923
- open,
11924
- onClose,
11925
- onOk,
11926
- onCancel,
11927
- title,
11928
- description,
11929
- icon,
11930
- footer,
11931
- okText,
11932
- cancelText,
11933
- confirmLoading = false,
11934
- okType = "primary",
11935
- okButtonProps,
11936
- cancelButtonProps,
11937
- width = 520,
11938
- centered = false,
11939
- closable = true,
11940
- maskClosable,
11941
- closeOnBackdrop,
11942
- keyboard = true,
11943
- mask = true,
11944
- draggable = false,
11945
- maximizable = false,
11946
- defaultMaximized = false,
11947
- zIndex,
11948
- afterClose,
11949
- afterOpenChange,
11950
- children,
11951
- className,
11952
- style,
11953
- ...props
11954
- }, ref) {
11955
- const { modal } = useLocale();
11956
- const cancel = onCancel ?? onClose;
11957
- const backdropClosable = maskClosable ?? closeOnBackdrop ?? true;
11958
- const [pos, setPos] = useState({ x: 0, y: 0 });
11959
- const [maximized, setMaximized] = useState(defaultMaximized);
11960
- const drag = useRef(null);
11961
- const prevOpen = useRef(open);
11962
- useEffect(() => {
11963
- if (!open) return;
11964
- const onKey = (e) => {
11965
- if (e.key === "Escape" && keyboard && closable) cancel?.();
11966
- };
11967
- document.addEventListener("keydown", onKey);
11968
- const prevOverflow = document.body.style.overflow;
11969
- document.body.style.overflow = "hidden";
11970
- return () => {
11971
- document.removeEventListener("keydown", onKey);
11972
- document.body.style.overflow = prevOverflow;
11973
- };
11974
- }, [open, keyboard, closable, cancel]);
11975
- useEffect(() => {
11976
- if (open) {
11977
- setPos({ x: 0, y: 0 });
11978
- setMaximized(defaultMaximized);
11979
- }
11980
- if (prevOpen.current !== open) {
11981
- afterOpenChange?.(open);
11982
- if (!open) afterClose?.();
11983
- prevOpen.current = open;
11984
- }
11985
- }, [open]);
11986
- if (!open) return null;
11987
- const startDrag = (e) => {
11988
- if (!draggable || maximized) return;
11989
- if (e.target.closest(".klun-modal__actions")) return;
11990
- drag.current = { sx: e.clientX, sy: e.clientY, ox: pos.x, oy: pos.y };
11991
- const move = (ev) => {
11992
- if (!drag.current) return;
11993
- setPos({
11994
- x: drag.current.ox + (ev.clientX - drag.current.sx),
11995
- y: drag.current.oy + (ev.clientY - drag.current.sy)
11996
- });
11997
- };
11998
- const up = () => {
11999
- drag.current = null;
12000
- document.removeEventListener("mousemove", move);
12001
- document.removeEventListener("mouseup", up);
12002
- };
12003
- document.addEventListener("mousemove", move);
12004
- document.addEventListener("mouseup", up);
12005
- };
12006
- const defaultFooter = /* @__PURE__ */ jsxs(Fragment, { children: [
12007
- /* @__PURE__ */ jsx(Button, { variant: "neutral", appearance: "stroke", disabled: confirmLoading, onClick: cancel, ...cancelButtonProps, children: cancelText ?? "Cancel" }),
12008
- /* @__PURE__ */ jsx(Button, { variant: okType === "danger" ? "error" : "primary", loading: confirmLoading, onClick: onOk, ...okButtonProps, children: okText ?? "OK" })
12009
- ] });
12010
- const resolvedFooter = footer === null ? null : footer !== void 0 ? footer : defaultFooter;
12011
- const dialogStyle = maximized ? { ...style } : {
12012
- width,
12013
- ...pos.x || pos.y ? { transform: `translate(${pos.x}px, ${pos.y}px)` } : null,
12014
- ...style
12015
- };
12016
- return /* @__PURE__ */ jsx(
12017
- "div",
12018
- {
12019
- className: cx("klun-modal", centered && "klun-modal--centered", !mask && "klun-modal--no-mask"),
12020
- style: zIndex != null ? { zIndex } : void 0,
12021
- onClick: backdropClosable && closable ? cancel : void 0,
12022
- children: /* @__PURE__ */ jsxs(
12023
- "div",
12024
- {
12025
- ref,
12026
- role: "dialog",
12027
- "aria-modal": "true",
12028
- onClick: (e) => e.stopPropagation(),
12029
- className: cx("klun-modal__dialog", maximized && "klun-modal__dialog--maximized", className),
12030
- style: dialogStyle,
12031
- ...props,
12032
- children: [
12033
- title || icon ? /* @__PURE__ */ jsxs(
12034
- "div",
12035
- {
12036
- className: cx("klun-modal__header", draggable && !maximized && "klun-modal__header--draggable"),
12037
- onMouseDown: startDrag,
12038
- children: [
12039
- icon ? /* @__PURE__ */ jsx("div", { className: "klun-modal__icon", children: icon }) : null,
12040
- /* @__PURE__ */ jsxs("div", { className: "klun-modal__heading", children: [
12041
- title ? /* @__PURE__ */ jsx("div", { className: "klun-modal__title", children: title }) : null,
12042
- description ? /* @__PURE__ */ jsx("div", { className: "klun-modal__description", children: description }) : null
12043
- ] }),
12044
- maximizable || closable ? /* @__PURE__ */ jsxs("div", { className: "klun-modal__actions", children: [
12045
- maximizable ? /* @__PURE__ */ jsx(
12046
- "button",
12047
- {
12048
- type: "button",
12049
- onClick: () => setMaximized((m) => !m),
12050
- "aria-label": maximized ? modal.restore ?? "Restore" : modal.maximize ?? "Maximize",
12051
- className: "klun-modal__action",
12052
- children: maximized ? /* @__PURE__ */ jsx("svg", { width: "18", height: "18", viewBox: "0 0 20 20", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M8 4v4H4M12 16v-4h4M12 4v4h4M8 16v-4H4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) : /* @__PURE__ */ jsx("svg", { width: "18", height: "18", viewBox: "0 0 20 20", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M4 8V4h4M16 12v4h-4M16 8V4h-4M4 12v4h4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
12053
- }
12054
- ) : null,
12055
- closable ? /* @__PURE__ */ jsx("button", { type: "button", onClick: cancel, "aria-label": modal.close, className: "klun-modal__action klun-modal__close", children: /* @__PURE__ */ jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M5 5l10 10M15 5L5 15", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) }) : null
12056
- ] }) : null
12057
- ]
12058
- }
12059
- ) : null,
12060
- children ? /* @__PURE__ */ jsx("div", { className: "klun-modal__body", children }) : null,
12061
- resolvedFooter ? /* @__PURE__ */ jsx("div", { className: cx("klun-modal__footer", !children && "klun-modal__footer--flush"), children: resolvedFooter }) : null
12062
- ]
12063
- }
12064
- )
12065
- }
12066
- );
12067
- });
12068
- Modal.displayName = "Modal";
12069
- var GAP4 = 8;
12070
- var MARGIN2 = 8;
14648
+ var GAP5 = 8;
14649
+ var MARGIN3 = 8;
12071
14650
  var Popconfirm = forwardRef(function Popconfirm2({
12072
14651
  title: titleProp,
12073
14652
  description,
@@ -12128,23 +14707,23 @@ var Popconfirm = forwardRef(function Popconfirm2({
12128
14707
  let left;
12129
14708
  switch (placement) {
12130
14709
  case "bottom":
12131
- top = tr.bottom + GAP4;
14710
+ top = tr.bottom + GAP5;
12132
14711
  left = tr.left + tr.width / 2 - bw / 2;
12133
14712
  break;
12134
14713
  case "left":
12135
- left = tr.left - bw - GAP4;
14714
+ left = tr.left - bw - GAP5;
12136
14715
  top = tr.top + tr.height / 2 - bh / 2;
12137
14716
  break;
12138
14717
  case "right":
12139
- left = tr.right + GAP4;
14718
+ left = tr.right + GAP5;
12140
14719
  top = tr.top + tr.height / 2 - bh / 2;
12141
14720
  break;
12142
14721
  default:
12143
- top = tr.top - bh - GAP4;
14722
+ top = tr.top - bh - GAP5;
12144
14723
  left = tr.left + tr.width / 2 - bw / 2;
12145
14724
  }
12146
- left = Math.max(MARGIN2, Math.min(left, vw - bw - MARGIN2));
12147
- top = Math.max(MARGIN2, Math.min(top, vh - bh - MARGIN2));
14725
+ left = Math.max(MARGIN3, Math.min(left, vw - bw - MARGIN3));
14726
+ top = Math.max(MARGIN3, Math.min(top, vh - bh - MARGIN3));
12148
14727
  setPos({ top, left });
12149
14728
  };
12150
14729
  place2();
@@ -12240,6 +14819,6 @@ var Popconfirm = forwardRef(function Popconfirm2({
12240
14819
  });
12241
14820
  Popconfirm.displayName = "Popconfirm";
12242
14821
 
12243
- export { Accordion, Alert, AutoComplete, Avatar, AvatarGroup, Badge, Banner, Breadcrumb, BulkAction, BulkActionBar, Button, ButtonGroup, CRON_PRESETS, 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, CronEditor, 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, Kanban, 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, RichEditor, 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, VirtualTable, Wizard, arSA, deDE, describeCron, 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 };
12244
- //# sourceMappingURL=chunk-MBL2CDDG.js.map
12245
- //# sourceMappingURL=chunk-MBL2CDDG.js.map
14822
+ export { Accordion, ActivityHeatmap, ActorAvatar, Alert, AutoComplete, Avatar, AvatarGroup, Badge, Banner, BorderBeam, Breadcrumb, BulkAction, BulkActionBar, Button, ButtonGroup, CRON_PRESETS, CapabilityBanner, CappedNumberFlow, Card, Carousel, Cascader, CharacterCounter, ChartLegend, ChartTooltip, Checkbox, CheckboxCard, CheckboxGroup, Chip, CircularProgress, CodeViewer, Col, Collapse, ColorDot, ColorPicker, ColorSlider, CommandMenu, CompactNumberFlow, CompactSelect, CompactSelectForInput, ConfigProvider, Confirm, ContentLabel, ContextMenu2 as ContextMenu, CopyButton, CounterInput, CronEditor, CrossRefBadge, CurrencyNumberFlow, DatePicker, DateRangePicker, DateTimePicker, Descriptions, DiffViewer, DigitInput, Divider, DotSphere, Drawer, Dropdown, EmojiPicker, EmptyState, ExpiryCountdown, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldTitle, FileUpload, Flex, Form, FormItem, FormList, Format, GaugeBar, Grid, Hint, HorizontalFilter, HoverCard, HoverCardContent, HoverCardTrigger, ICON_GROUPS, IconPicker, ImageUpload, ImageViewer, InlineInput, InlineSelect, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, Kanban, Kbd, KeyIcon, LIST_GRID_BOTTOM_CLEARANCE, Label, Layout, LayoutContent, LayoutFooter, LayoutHeader, LayoutSider, List, ListGrid, ListGridBody, ListGridCell, ListGridHeader, ListGridHeaderCell, ListGridRow, ListItem, LiveDot, LogViewer, Markdown, Masonry, Menu, Message, Modal, Money, NavigationProgress, Notification, NumberFlow, PageHeader, Pagination, PasswordStrength, Popconfirm, Popover, ProgressBar, QUICK_EMOJIS, QuickEmojiPicker, Radio, RadioCard, RangeSlider, Rating, ReactionBar, RelativeTime, Result, RichEditor, RichEditorToolbar, Row, SegmentedControl, SegmentedProgress, Select, SelectMenu, ShimmerText, Sidebar, Skeleton, Slider, Space, Spinner2 as Spinner, Splitter2 as Splitter, SplitterPanel, StatCard, Statistic, StatusBadge, StatusDot, StepIndicator, Stepper, StepperContent, StepperDescription, StepperIndicator, StepperItem, StepperNav, StepperPanel, StepperSeparator, StepperTitle, StepperTrigger, Switch, Table, Tabs, Tag, Textarea, TimePicker, Timeline, Toast, Toaster, Toggle, Tooltip, Tree, UnicodeSpinner, VirtualTable, Wizard, arSA, buildHeatmapGrid, deDE, describeCron, enUS, esES, fmt, formatAvatarEmoji, frFR, groupReactions, idID, itIT, jaJP, koKR, localTodayIso, locales, nlNL, parseAvatarEmoji, parsePatch, plPL, primaryColorVars, ptBR, renderMarkdown, reorderRows, resolveMove, ruRU, summarizeHeatmap, toast, trTR, useBreakpoint, useConfig, useContextMenu, useForm, useLocale, useMappedSize, useMessage, useNotification, useSize, useStepItem, useStepper, viVN, zhCN, zhTW };
14823
+ //# sourceMappingURL=chunk-WONPY7ZX.js.map
14824
+ //# sourceMappingURL=chunk-WONPY7ZX.js.map