@almadar/ui 5.29.0 → 5.31.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.
Files changed (37) hide show
  1. package/dist/avl/index.cjs +933 -6749
  2. package/dist/avl/index.js +934 -6750
  3. package/dist/components/core/atoms/Card.d.ts +2 -0
  4. package/dist/components/core/atoms/Input.d.ts +2 -0
  5. package/dist/components/core/atoms/Select.d.ts +18 -6
  6. package/dist/components/core/atoms/Spinner.d.ts +2 -0
  7. package/dist/components/core/atoms/index.d.ts +1 -1
  8. package/dist/components/core/molecules/DocumentViewer.d.ts +0 -2
  9. package/dist/components/core/molecules/Header.d.ts +0 -4
  10. package/dist/components/core/molecules/JsonTreeEditor.d.ts +3 -8
  11. package/dist/components/core/molecules/Menu.d.ts +4 -0
  12. package/dist/components/core/molecules/Navigation.d.ts +0 -2
  13. package/dist/components/core/molecules/PageHeader.d.ts +0 -2
  14. package/dist/components/core/molecules/PropertyInspector.d.ts +8 -1
  15. package/dist/components/core/molecules/index.d.ts +1 -1
  16. package/dist/components/core/organisms/index.d.ts +0 -1
  17. package/dist/components/core/templates/index.d.ts +0 -3
  18. package/dist/components/game/molecules/index.d.ts +0 -1
  19. package/dist/components/game/molecules/three/index.cjs +27 -2
  20. package/dist/components/game/molecules/three/index.js +27 -2
  21. package/dist/components/game/molecules/three/patterns.d.ts +20 -0
  22. package/dist/components/game/organisms/TraitSlot.d.ts +3 -1
  23. package/dist/components/game/organisms/types/isometric.d.ts +2 -0
  24. package/dist/components/index.cjs +1198 -1062
  25. package/dist/components/index.js +1201 -1064
  26. package/dist/docs/index.cjs +205 -172
  27. package/dist/docs/index.d.cts +4 -0
  28. package/dist/docs/index.js +146 -113
  29. package/dist/marketing/index.cjs +91 -54
  30. package/dist/marketing/index.d.cts +2 -0
  31. package/dist/marketing/index.js +58 -21
  32. package/dist/providers/index.cjs +563 -275
  33. package/dist/providers/index.js +563 -275
  34. package/dist/renderer/pattern-resolver.d.ts +2 -5
  35. package/dist/runtime/index.cjs +563 -275
  36. package/dist/runtime/index.js +563 -275
  37. package/package.json +16 -2
@@ -3212,6 +3212,8 @@ var init_Input = __esm({
3212
3212
  className,
3213
3213
  inputType,
3214
3214
  type: htmlType,
3215
+ label,
3216
+ helperText,
3215
3217
  error,
3216
3218
  leftIcon,
3217
3219
  rightIcon,
@@ -3267,82 +3269,95 @@ var init_Input = __esm({
3267
3269
  onClear?.();
3268
3270
  }
3269
3271
  };
3272
+ const wrapField = (field) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full", children: [
3273
+ label && /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-foreground mb-1", children: label }),
3274
+ field,
3275
+ (helperText || error) && /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn("mt-1 text-xs", error ? "text-error" : "text-muted-foreground"), children: error ?? helperText })
3276
+ ] });
3270
3277
  if (type === "select") {
3271
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
3272
- resolvedLeftIcon && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
3273
- /* @__PURE__ */ jsxRuntime.jsxs(
3274
- "select",
3278
+ return wrapField(
3279
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
3280
+ resolvedLeftIcon && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
3281
+ /* @__PURE__ */ jsxRuntime.jsxs(
3282
+ "select",
3283
+ {
3284
+ ref,
3285
+ value,
3286
+ onChange: handleChange,
3287
+ className: cn(baseClassName, "appearance-none pr-10", className),
3288
+ ...props,
3289
+ children: [
3290
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: t("form.selectPlaceholder", { label: "" }) }),
3291
+ options?.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.value, children: opt.label }, opt.value))
3292
+ ]
3293
+ }
3294
+ ),
3295
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default" }) })
3296
+ ] })
3297
+ );
3298
+ }
3299
+ if (type === "textarea") {
3300
+ return wrapField(
3301
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
3302
+ "textarea",
3275
3303
  {
3276
3304
  ref,
3277
3305
  value,
3278
3306
  onChange: handleChange,
3279
- className: cn(baseClassName, "appearance-none pr-10", className),
3280
- ...props,
3281
- children: [
3282
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: t("form.selectPlaceholder", { label: "" }) }),
3283
- options?.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.value, children: opt.label }, opt.value))
3284
- ]
3307
+ rows: rows2,
3308
+ className: baseClassName,
3309
+ ...props
3285
3310
  }
3286
- ),
3287
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default" }) })
3288
- ] });
3289
- }
3290
- if (type === "textarea") {
3291
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
3292
- "textarea",
3293
- {
3294
- ref,
3295
- value,
3296
- onChange: handleChange,
3297
- rows: rows2,
3298
- className: baseClassName,
3299
- ...props
3300
- }
3301
- ) });
3311
+ ) })
3312
+ );
3302
3313
  }
3303
3314
  if (type === "checkbox") {
3304
- return /* @__PURE__ */ jsxRuntime.jsx(
3305
- "input",
3306
- {
3307
- ref,
3308
- type: "checkbox",
3309
- checked: props.checked,
3310
- onChange: handleChange,
3311
- className: cn(
3312
- "h-icon-default w-icon-default rounded-sm",
3313
- "border-border",
3314
- "text-primary focus:ring-ring",
3315
- "disabled:opacity-50 disabled:cursor-not-allowed",
3316
- className
3317
- ),
3318
- ...props
3319
- }
3315
+ return wrapField(
3316
+ /* @__PURE__ */ jsxRuntime.jsx(
3317
+ "input",
3318
+ {
3319
+ ref,
3320
+ type: "checkbox",
3321
+ checked: props.checked,
3322
+ onChange: handleChange,
3323
+ className: cn(
3324
+ "h-icon-default w-icon-default rounded-sm",
3325
+ "border-border",
3326
+ "text-primary focus:ring-ring",
3327
+ "disabled:opacity-50 disabled:cursor-not-allowed",
3328
+ className
3329
+ ),
3330
+ ...props
3331
+ }
3332
+ )
3320
3333
  );
3321
3334
  }
3322
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
3323
- resolvedLeftIcon && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
3324
- /* @__PURE__ */ jsxRuntime.jsx(
3325
- "input",
3326
- {
3327
- ref,
3328
- type,
3329
- value,
3330
- onChange: handleChange,
3331
- className: baseClassName,
3332
- ...props
3333
- }
3334
- ),
3335
- showClearButton && /* @__PURE__ */ jsxRuntime.jsx(
3336
- "button",
3337
- {
3338
- type: "button",
3339
- onClick: handleClear,
3340
- className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground hover:text-foreground",
3341
- children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
3342
- }
3343
- ),
3344
- rightIcon && !showClearButton && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground", children: resolveIconNode(rightIcon, iconCls) })
3345
- ] });
3335
+ return wrapField(
3336
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
3337
+ resolvedLeftIcon && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
3338
+ /* @__PURE__ */ jsxRuntime.jsx(
3339
+ "input",
3340
+ {
3341
+ ref,
3342
+ type,
3343
+ value,
3344
+ onChange: handleChange,
3345
+ className: baseClassName,
3346
+ ...props
3347
+ }
3348
+ ),
3349
+ showClearButton && /* @__PURE__ */ jsxRuntime.jsx(
3350
+ "button",
3351
+ {
3352
+ type: "button",
3353
+ onClick: handleClear,
3354
+ className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground hover:text-foreground",
3355
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
3356
+ }
3357
+ ),
3358
+ rightIcon && !showClearButton && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground", children: resolveIconNode(rightIcon, iconCls) })
3359
+ ] })
3360
+ );
3346
3361
  }
3347
3362
  );
3348
3363
  Input.displayName = "Input";
@@ -3413,6 +3428,190 @@ var init_Textarea = __esm({
3413
3428
  Textarea.displayName = "Textarea";
3414
3429
  }
3415
3430
  });
3431
+ function flatOptions(opts, groups) {
3432
+ const flat = opts ?? [];
3433
+ const grp = (groups ?? []).flatMap((g) => g.options);
3434
+ return [...flat, ...grp];
3435
+ }
3436
+ function NativeSelect({
3437
+ className,
3438
+ options,
3439
+ groups,
3440
+ placeholder,
3441
+ error,
3442
+ onChange,
3443
+ value,
3444
+ ...props
3445
+ }) {
3446
+ const eventBus = useEventBus();
3447
+ const handleChange = (e) => {
3448
+ if (typeof onChange === "string") {
3449
+ eventBus.emit(`UI:${onChange}`, { value: e.target.value });
3450
+ } else {
3451
+ onChange?.(e.target.value);
3452
+ }
3453
+ };
3454
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
3455
+ /* @__PURE__ */ jsxRuntime.jsxs(
3456
+ "select",
3457
+ {
3458
+ onChange: handleChange,
3459
+ value,
3460
+ className: cn(
3461
+ "block w-full border-[length:var(--border-width)] shadow-sm appearance-none",
3462
+ "px-3 py-2 pr-10 text-sm text-foreground font-medium",
3463
+ "bg-card",
3464
+ "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
3465
+ "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
3466
+ error ? "border-error focus:border-error" : "border-border focus:border-primary",
3467
+ className
3468
+ ),
3469
+ ...props,
3470
+ children: [
3471
+ placeholder && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: placeholder }),
3472
+ options?.map((option) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)),
3473
+ groups?.map((group) => /* @__PURE__ */ jsxRuntime.jsx("optgroup", { label: group.label, children: group.options.map((option) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)) }, group.label))
3474
+ ]
3475
+ }
3476
+ ),
3477
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default text-foreground" }) })
3478
+ ] });
3479
+ }
3480
+ function RichSelect({
3481
+ className,
3482
+ options,
3483
+ groups,
3484
+ placeholder,
3485
+ error,
3486
+ onChange,
3487
+ value,
3488
+ multiple,
3489
+ searchable,
3490
+ clearable,
3491
+ disabled
3492
+ }) {
3493
+ const eventBus = useEventBus();
3494
+ const [open, setOpen] = React79.useState(false);
3495
+ const [search, setSearch] = React79.useState("");
3496
+ const containerRef = React79.useRef(null);
3497
+ const selected = multiple ? Array.isArray(value) ? value : value ? [value] : [] : value ? [value] : [];
3498
+ const all = flatOptions(options, groups);
3499
+ const filtered = searchable && search ? all.filter((o) => o.label.toLowerCase().includes(search.toLowerCase())) : all;
3500
+ const toggle = (optValue) => {
3501
+ let next;
3502
+ if (multiple) {
3503
+ next = selected.includes(optValue) ? selected.filter((v) => v !== optValue) : [...selected, optValue];
3504
+ } else {
3505
+ next = optValue;
3506
+ setOpen(false);
3507
+ }
3508
+ if (typeof onChange === "string") {
3509
+ eventBus.emit(`UI:${onChange}`, { value: next });
3510
+ } else {
3511
+ onChange?.(next);
3512
+ }
3513
+ };
3514
+ const clear = (e) => {
3515
+ e.stopPropagation();
3516
+ const next = multiple ? [] : "";
3517
+ if (typeof onChange === "string") {
3518
+ eventBus.emit(`UI:${onChange}`, { value: next });
3519
+ } else {
3520
+ onChange?.(next);
3521
+ }
3522
+ };
3523
+ React79.useEffect(() => {
3524
+ const handler = (e) => {
3525
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
3526
+ setOpen(false);
3527
+ setSearch("");
3528
+ }
3529
+ };
3530
+ document.addEventListener("mousedown", handler);
3531
+ return () => document.removeEventListener("mousedown", handler);
3532
+ }, []);
3533
+ const displayLabel = selected.length === 0 ? placeholder ?? "" : multiple ? `${selected.length} selected` : all.find((o) => o.value === selected[0])?.label ?? selected[0];
3534
+ const hasValue = selected.length > 0;
3535
+ const renderOptions = (opts) => opts.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
3536
+ "button",
3537
+ {
3538
+ type: "button",
3539
+ disabled: opt.disabled,
3540
+ onClick: () => !opt.disabled && toggle(opt.value),
3541
+ className: cn(
3542
+ "w-full flex items-center justify-between px-3 py-1.5 text-sm text-start",
3543
+ "hover:bg-muted transition-colors",
3544
+ "disabled:opacity-50 disabled:cursor-not-allowed",
3545
+ selected.includes(opt.value) && "text-primary font-medium"
3546
+ ),
3547
+ children: [
3548
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: opt.label }),
3549
+ selected.includes(opt.value) && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "check", className: "h-icon-default w-icon-default" })
3550
+ ]
3551
+ },
3552
+ opt.value
3553
+ ));
3554
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, className: cn("relative w-full", className), children: [
3555
+ /* @__PURE__ */ jsxRuntime.jsx(
3556
+ "button",
3557
+ {
3558
+ type: "button",
3559
+ disabled,
3560
+ onClick: () => !disabled && setOpen((o) => !o),
3561
+ className: cn(
3562
+ "block w-full border-[length:var(--border-width)] shadow-sm",
3563
+ "px-3 py-2 pr-10 text-sm text-start font-medium",
3564
+ "bg-card rounded-sm",
3565
+ "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
3566
+ "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
3567
+ error ? "border-error focus:border-error" : "border-border focus:border-primary",
3568
+ !hasValue && "text-muted-foreground"
3569
+ ),
3570
+ children: displayLabel
3571
+ }
3572
+ ),
3573
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center gap-1 pointer-events-none", children: [
3574
+ clearable && hasValue && /* @__PURE__ */ jsxRuntime.jsx(
3575
+ "button",
3576
+ {
3577
+ type: "button",
3578
+ onClick: clear,
3579
+ className: "pointer-events-auto text-muted-foreground hover:text-foreground",
3580
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
3581
+ }
3582
+ ),
3583
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default text-foreground" })
3584
+ ] }),
3585
+ open && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(
3586
+ "absolute z-50 mt-1 w-full",
3587
+ "bg-card border-[length:var(--border-width)] border-border",
3588
+ "rounded-sm shadow-elevation-popover py-1 max-h-60 overflow-y-auto"
3589
+ ), children: [
3590
+ searchable && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 pb-1 border-b border-border", children: /* @__PURE__ */ jsxRuntime.jsx(
3591
+ "input",
3592
+ {
3593
+ autoFocus: true,
3594
+ type: "text",
3595
+ value: search,
3596
+ onChange: (e) => setSearch(e.target.value),
3597
+ placeholder: "Search\u2026",
3598
+ className: cn(
3599
+ "w-full px-2 py-1 text-sm bg-transparent",
3600
+ "focus:outline-none text-foreground placeholder:text-muted-foreground"
3601
+ )
3602
+ }
3603
+ ) }),
3604
+ groups && groups.length > 0 ? groups.map((g) => {
3605
+ const groupFiltered = searchable && search ? g.options.filter((o) => o.label.toLowerCase().includes(search.toLowerCase())) : g.options;
3606
+ if (groupFiltered.length === 0) return null;
3607
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3608
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-1 text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: g.label }),
3609
+ renderOptions(groupFiltered)
3610
+ ] }, g.label);
3611
+ }) : renderOptions(filtered)
3612
+ ] })
3613
+ ] });
3614
+ }
3416
3615
  var Select;
3417
3616
  var init_Select = __esm({
3418
3617
  "components/core/atoms/Select.tsx"() {
@@ -3420,47 +3619,12 @@ var init_Select = __esm({
3420
3619
  init_Icon();
3421
3620
  init_useEventBus();
3422
3621
  Select = React79__namespace.default.forwardRef(
3423
- ({ className, options, placeholder, error, onChange, ...props }, ref) => {
3424
- const eventBus = useEventBus();
3425
- const handleChange = (e) => {
3426
- if (typeof onChange === "string") {
3427
- eventBus.emit(`UI:${onChange}`, { value: e.target.value });
3428
- } else {
3429
- onChange?.(e);
3430
- }
3431
- };
3432
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
3433
- /* @__PURE__ */ jsxRuntime.jsxs(
3434
- "select",
3435
- {
3436
- ref,
3437
- onChange: handleChange,
3438
- className: cn(
3439
- "block w-full border-[length:var(--border-width)] shadow-sm appearance-none",
3440
- "px-3 py-2 pr-10 text-sm text-foreground font-medium",
3441
- "bg-card",
3442
- "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
3443
- "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
3444
- error ? "border-error focus:border-error" : "border-border focus:border-primary",
3445
- className
3446
- ),
3447
- ...props,
3448
- children: [
3449
- placeholder && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: placeholder }),
3450
- options.map((option) => /* @__PURE__ */ jsxRuntime.jsx(
3451
- "option",
3452
- {
3453
- value: option.value,
3454
- disabled: option.disabled,
3455
- children: option.label
3456
- },
3457
- option.value
3458
- ))
3459
- ]
3460
- }
3461
- ),
3462
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default text-foreground" }) })
3463
- ] });
3622
+ (props, _ref) => {
3623
+ const { multiple, searchable, clearable } = props;
3624
+ if (multiple || searchable || clearable) {
3625
+ return /* @__PURE__ */ jsxRuntime.jsx(RichSelect, { ...props });
3626
+ }
3627
+ return /* @__PURE__ */ jsxRuntime.jsx(NativeSelect, { ...props });
3464
3628
  }
3465
3629
  );
3466
3630
  Select.displayName = "Select";
@@ -3514,11 +3678,54 @@ var init_Checkbox = __esm({
3514
3678
  Checkbox.displayName = "Checkbox";
3515
3679
  }
3516
3680
  });
3681
+ var sizeStyles3, Spinner;
3682
+ var init_Spinner = __esm({
3683
+ "components/core/atoms/Spinner.tsx"() {
3684
+ init_cn();
3685
+ init_Icon();
3686
+ sizeStyles3 = {
3687
+ xs: "h-3 w-3",
3688
+ sm: "h-icon-default w-icon-default",
3689
+ md: "h-6 w-6",
3690
+ lg: "h-8 w-8"
3691
+ };
3692
+ Spinner = React79__namespace.default.forwardRef(
3693
+ ({ className, size = "md", overlay, ...props }, ref) => {
3694
+ if (overlay) {
3695
+ return /* @__PURE__ */ jsxRuntime.jsx(
3696
+ "div",
3697
+ {
3698
+ ref,
3699
+ className: cn(
3700
+ "absolute inset-0 z-10 flex items-center justify-center",
3701
+ "bg-background/60 backdrop-blur-sm",
3702
+ className
3703
+ ),
3704
+ ...props,
3705
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "loader", className: cn("animate-spin text-foreground", sizeStyles3[size]) })
3706
+ }
3707
+ );
3708
+ }
3709
+ return /* @__PURE__ */ jsxRuntime.jsx(
3710
+ "div",
3711
+ {
3712
+ ref,
3713
+ className: cn("text-foreground", className),
3714
+ ...props,
3715
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "loader", className: cn("animate-spin", sizeStyles3[size]) })
3716
+ }
3717
+ );
3718
+ }
3719
+ );
3720
+ Spinner.displayName = "Spinner";
3721
+ }
3722
+ });
3517
3723
  var variantStyles4, paddingStyles2, shadowStyles2, lookStyles2, Card, CardHeader, CardTitle, CardContent, CardBody, CardFooter;
3518
3724
  var init_Card = __esm({
3519
3725
  "components/core/atoms/Card.tsx"() {
3520
3726
  init_cn();
3521
3727
  init_useEventBus();
3728
+ init_Spinner();
3522
3729
  variantStyles4 = {
3523
3730
  default: [
3524
3731
  "bg-card",
@@ -3583,6 +3790,7 @@ var init_Card = __esm({
3583
3790
  look = "elevated",
3584
3791
  children,
3585
3792
  action,
3793
+ loading,
3586
3794
  onClick,
3587
3795
  ...props
3588
3796
  }, ref) => {
@@ -3596,7 +3804,7 @@ var init_Card = __esm({
3596
3804
  {
3597
3805
  ref,
3598
3806
  className: cn(
3599
- "rounded-container",
3807
+ "rounded-container relative",
3600
3808
  "transition-all duration-[var(--transition-normal)]",
3601
3809
  variantStyles4[variant],
3602
3810
  paddingStyles2[padding],
@@ -3607,6 +3815,7 @@ var init_Card = __esm({
3607
3815
  onClick: handleClick,
3608
3816
  ...props,
3609
3817
  children: [
3818
+ loading && /* @__PURE__ */ jsxRuntime.jsx(Spinner, { overlay: true, size: "md" }),
3610
3819
  (title || subtitle) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-4", children: [
3611
3820
  title && /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg text-card-foreground font-bold", children: title }),
3612
3821
  subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mt-1", children: subtitle })
@@ -3648,7 +3857,7 @@ var init_Card = __esm({
3648
3857
  CardFooter.displayName = "CardFooter";
3649
3858
  }
3650
3859
  });
3651
- var variantStyles5, sizeStyles3, iconSizes, FilterPill;
3860
+ var variantStyles5, sizeStyles4, iconSizes, FilterPill;
3652
3861
  var init_FilterPill = __esm({
3653
3862
  "components/core/atoms/FilterPill.tsx"() {
3654
3863
  init_cn();
@@ -3682,7 +3891,7 @@ var init_FilterPill = __esm({
3682
3891
  "border-[length:var(--border-width-thin)] border-border"
3683
3892
  ].join(" ")
3684
3893
  };
3685
- sizeStyles3 = {
3894
+ sizeStyles4 = {
3686
3895
  sm: "px-2 py-0.5 text-xs",
3687
3896
  md: "px-2.5 py-1 text-sm",
3688
3897
  lg: "px-3 py-1.5 text-base"
@@ -3726,7 +3935,7 @@ var init_FilterPill = __esm({
3726
3935
  className: cn(
3727
3936
  "inline-flex items-center gap-1 font-bold rounded-pill",
3728
3937
  variantStyles5[variant],
3729
- sizeStyles3[size],
3938
+ sizeStyles4[size],
3730
3939
  (onClick || clickEvent) && "cursor-pointer",
3731
3940
  className
3732
3941
  ),
@@ -3758,33 +3967,6 @@ var init_FilterPill = __esm({
3758
3967
  FilterPill.displayName = "FilterPill";
3759
3968
  }
3760
3969
  });
3761
- var sizeStyles4, Spinner;
3762
- var init_Spinner = __esm({
3763
- "components/core/atoms/Spinner.tsx"() {
3764
- init_cn();
3765
- init_Icon();
3766
- sizeStyles4 = {
3767
- xs: "h-3 w-3",
3768
- sm: "h-icon-default w-icon-default",
3769
- md: "h-6 w-6",
3770
- lg: "h-8 w-8"
3771
- };
3772
- Spinner = React79__namespace.default.forwardRef(
3773
- ({ className, size = "md", ...props }, ref) => {
3774
- return /* @__PURE__ */ jsxRuntime.jsx(
3775
- "div",
3776
- {
3777
- ref,
3778
- className: cn("text-foreground", className),
3779
- ...props,
3780
- children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "loader", className: cn("animate-spin", sizeStyles4[size]) })
3781
- }
3782
- );
3783
- }
3784
- );
3785
- Spinner.displayName = "Spinner";
3786
- }
3787
- });
3788
3970
  function generateInitials(name) {
3789
3971
  const parts = name.trim().split(/\s+/);
3790
3972
  if (parts.length === 1) {
@@ -16082,30 +16264,30 @@ var init_BranchingLogicBuilder = __esm({
16082
16264
  if (!sourceQuestion?.optionValues) return [];
16083
16265
  return sourceQuestion.optionValues.map((v) => ({ value: v, label: v }));
16084
16266
  }, [sourceQuestion]);
16085
- const handleSource = (e) => {
16086
- onChange({ ...rule, sourceQuestionId: e.target.value });
16267
+ const handleSource = (v) => {
16268
+ onChange({ ...rule, sourceQuestionId: v });
16087
16269
  };
16088
- const handleOperator = (e) => {
16089
- const next = e.target.value;
16270
+ const handleOperator = (v) => {
16271
+ const next = v;
16090
16272
  const nextValue = next === "in" && !Array.isArray(rule.value) ? rule.value ? [rule.value] : [] : next !== "in" && Array.isArray(rule.value) ? rule.value[0] ?? "" : rule.value;
16091
16273
  onChange({ ...rule, operator: next, value: nextValue });
16092
16274
  };
16093
16275
  const handleScalarValue = (e) => {
16094
16276
  onChange({ ...rule, value: e.target.value });
16095
16277
  };
16096
- const handleAddChip = (e) => {
16097
- const v = e.target.value;
16098
- if (!v) return;
16278
+ const handleAddChip = (v) => {
16279
+ const val = v;
16280
+ if (!val) return;
16099
16281
  const current = Array.isArray(rule.value) ? rule.value : [];
16100
- if (current.includes(v)) return;
16101
- onChange({ ...rule, value: [...current, v] });
16282
+ if (current.includes(val)) return;
16283
+ onChange({ ...rule, value: [...current, val] });
16102
16284
  };
16103
16285
  const handleRemoveChip = (chip) => {
16104
16286
  const current = Array.isArray(rule.value) ? rule.value : [];
16105
16287
  onChange({ ...rule, value: current.filter((c) => c !== chip) });
16106
16288
  };
16107
- const handleTarget = (e) => {
16108
- onChange({ ...rule, targetQuestionId: e.target.value });
16289
+ const handleTarget = (v) => {
16290
+ onChange({ ...rule, targetQuestionId: v });
16109
16291
  };
16110
16292
  const isMulti = rule.operator === "in";
16111
16293
  const chips = Array.isArray(rule.value) ? rule.value : [];
@@ -16186,7 +16368,7 @@ var init_BranchingLogicBuilder = __esm({
16186
16368
  options: valueOptions,
16187
16369
  value: scalarValue,
16188
16370
  placeholder: t("branchingLogic.selectValue"),
16189
- onChange: (e) => onChange({ ...rule, value: e.target.value }),
16371
+ onChange: (v) => onChange({ ...rule, value: v }),
16190
16372
  disabled: readOnly
16191
16373
  }
16192
16374
  ) : /* @__PURE__ */ jsxRuntime.jsx(
@@ -18618,7 +18800,7 @@ var init_Pagination = __esm({
18618
18800
  Select,
18619
18801
  {
18620
18802
  value: String(pageSize),
18621
- onChange: (e) => handlePageSizeChange(Number(e.target.value)),
18803
+ onChange: (v) => handlePageSizeChange(Number(v)),
18622
18804
  options: pageSizeOptions.map((size) => ({
18623
18805
  value: String(size),
18624
18806
  label: String(size)
@@ -21544,7 +21726,84 @@ var init_DashboardLayout = __esm({
21544
21726
  NavLinkBottom.displayName = "NavLinkBottom";
21545
21727
  }
21546
21728
  });
21547
- var Menu;
21729
+ function computeMenuStyle(position, triggerRect) {
21730
+ const isTop = position.startsWith("top");
21731
+ const isRight = position.endsWith("right") || position.endsWith("end");
21732
+ if (isTop) {
21733
+ return {
21734
+ top: triggerRect.top - MENU_GAP,
21735
+ transform: "translateY(-100%)",
21736
+ ...isRight ? { right: window.innerWidth - triggerRect.right } : { left: triggerRect.left }
21737
+ };
21738
+ }
21739
+ return {
21740
+ top: triggerRect.bottom + MENU_GAP,
21741
+ ...isRight ? { right: window.innerWidth - triggerRect.right } : { left: triggerRect.left }
21742
+ };
21743
+ }
21744
+ function SubMenu({
21745
+ items,
21746
+ itemRef,
21747
+ direction,
21748
+ eventBus
21749
+ }) {
21750
+ const [rect, setRect] = React79.useState(null);
21751
+ React79.useEffect(() => {
21752
+ if (itemRef) {
21753
+ setRect(itemRef.getBoundingClientRect());
21754
+ }
21755
+ }, [itemRef]);
21756
+ if (!rect) return null;
21757
+ const isRtl = direction === "rtl";
21758
+ const style = {
21759
+ top: rect.top,
21760
+ ...isRtl ? { right: window.innerWidth - rect.left } : { left: rect.right }
21761
+ };
21762
+ const panel = /* @__PURE__ */ jsxRuntime.jsx(
21763
+ "div",
21764
+ {
21765
+ className: cn("fixed z-50", menuContainerStyles),
21766
+ style,
21767
+ children: items.map((item, index) => {
21768
+ const isDivider = item.id === "divider" || item.label === "divider";
21769
+ const itemId = item.id ?? `item-${item.label.toLowerCase().replace(/\s+/g, "-")}-${index}`;
21770
+ const isDanger = item.variant === "danger";
21771
+ if (isDivider) {
21772
+ return /* @__PURE__ */ jsxRuntime.jsx(Divider, { className: "my-1" }, `divider-${index}`);
21773
+ }
21774
+ return /* @__PURE__ */ jsxRuntime.jsxs(
21775
+ Box,
21776
+ {
21777
+ as: "button",
21778
+ onClick: () => {
21779
+ if (item.disabled) return;
21780
+ if (item.event) eventBus.emit(`UI:${item.event}`, { itemId, label: item.label });
21781
+ item.onClick?.();
21782
+ },
21783
+ "aria-disabled": item.disabled || void 0,
21784
+ "data-testid": item.event ? `action-${item.event}` : void 0,
21785
+ className: cn(
21786
+ "w-full flex items-center gap-3 px-4 py-2 text-start",
21787
+ "text-sm transition-colors",
21788
+ "hover:bg-muted focus:outline-none focus:bg-muted",
21789
+ "disabled:opacity-50 disabled:cursor-not-allowed",
21790
+ item.disabled && "cursor-not-allowed",
21791
+ isDanger && "text-error hover:bg-error/10"
21792
+ ),
21793
+ children: [
21794
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: item.icon, size: "sm", className: "flex-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: item.icon, size: "sm", className: "flex-shrink-0" })),
21795
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: cn("flex-1", isDanger && "text-red-600"), children: item.label }),
21796
+ item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto text-xs font-medium", children: item.badge })
21797
+ ]
21798
+ },
21799
+ itemId
21800
+ );
21801
+ })
21802
+ }
21803
+ );
21804
+ return typeof document !== "undefined" ? reactDom.createPortal(panel, document.body) : panel;
21805
+ }
21806
+ var MENU_GAP, menuContainerStyles, Menu;
21548
21807
  var init_Menu = __esm({
21549
21808
  "components/core/molecules/Menu.tsx"() {
21550
21809
  "use client";
@@ -21555,16 +21814,27 @@ var init_Menu = __esm({
21555
21814
  init_Badge();
21556
21815
  init_cn();
21557
21816
  init_useEventBus();
21817
+ MENU_GAP = 4;
21818
+ menuContainerStyles = cn(
21819
+ "bg-card",
21820
+ "border-[length:var(--border-width)] border-border",
21821
+ "shadow-elevation-popover",
21822
+ "rounded-sm",
21823
+ "min-w-0 sm:min-w-[200px] max-w-[calc(100vw-1rem)] py-1"
21824
+ );
21558
21825
  Menu = ({
21559
21826
  trigger,
21560
21827
  items,
21561
21828
  position = "bottom-left",
21562
- className
21829
+ className,
21830
+ header,
21831
+ footer
21563
21832
  }) => {
21564
21833
  const eventBus = useEventBus();
21565
- const { t, direction } = hooks.useTranslate();
21834
+ const { direction } = hooks.useTranslate();
21566
21835
  const [isOpen, setIsOpen] = React79.useState(false);
21567
21836
  const [activeSubMenu, setActiveSubMenu] = React79.useState(null);
21837
+ const [activeSubMenuRef, setActiveSubMenuRef] = React79.useState(null);
21568
21838
  const [triggerRect, setTriggerRect] = React79.useState(null);
21569
21839
  const triggerRef = React79.useRef(null);
21570
21840
  const menuRef = React79.useRef(null);
@@ -21579,13 +21849,14 @@ var init_Menu = __esm({
21579
21849
  }
21580
21850
  setIsOpen(!isOpen);
21581
21851
  setActiveSubMenu(null);
21852
+ setActiveSubMenuRef(null);
21582
21853
  };
21583
- const handleItemClick = (item) => {
21854
+ const handleItemClick = (item, itemId) => {
21584
21855
  if (item.disabled) return;
21585
21856
  if (item.subMenu && item.subMenu.length > 0) {
21586
- setActiveSubMenu(item.id ?? null);
21857
+ setActiveSubMenu(itemId);
21587
21858
  } else {
21588
- if (item.event) eventBus.emit(`UI:${item.event}`, { itemId: item.id, label: item.label });
21859
+ if (item.event) eventBus.emit(`UI:${item.event}`, { itemId, label: item.label });
21589
21860
  item.onClick?.();
21590
21861
  setIsOpen(false);
21591
21862
  }
@@ -21600,22 +21871,12 @@ var init_Menu = __esm({
21600
21871
  if (isOpen && menuRef.current && !menuRef.current.contains(e.target) && triggerRef.current && !triggerRef.current.contains(e.target)) {
21601
21872
  setIsOpen(false);
21602
21873
  setActiveSubMenu(null);
21874
+ setActiveSubMenuRef(null);
21603
21875
  }
21604
21876
  };
21605
21877
  document.addEventListener("mousedown", handleClickOutside);
21606
21878
  return () => document.removeEventListener("mousedown", handleClickOutside);
21607
21879
  }, [isOpen]);
21608
- const positionClasses = {
21609
- "top-left": "bottom-full left-0 mb-2",
21610
- "top-right": "bottom-full right-0 mb-2",
21611
- "bottom-left": "top-full left-0 mt-2",
21612
- "bottom-right": "top-full right-0 mt-2",
21613
- // Aliases for pattern compatibility
21614
- "top-start": "bottom-full left-0 mb-2",
21615
- "top-end": "bottom-full right-0 mb-2",
21616
- "bottom-start": "top-full left-0 mt-2",
21617
- "bottom-end": "top-full right-0 mt-2"
21618
- };
21619
21880
  const rtlMirror = {
21620
21881
  "top-left": "top-right",
21621
21882
  "top-right": "top-left",
@@ -21627,7 +21888,6 @@ var init_Menu = __esm({
21627
21888
  "bottom-end": "bottom-start"
21628
21889
  };
21629
21890
  const effectivePosition = direction === "rtl" ? rtlMirror[position] ?? position : position;
21630
- const subMenuSideClass = direction === "rtl" ? "right-full mr-2" : "left-full ml-2";
21631
21891
  const triggerChild = React79__namespace.default.isValidElement(trigger) ? trigger : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", as: "span", children: trigger });
21632
21892
  const triggerElement = React79__namespace.default.cloneElement(
21633
21893
  triggerChild,
@@ -21636,94 +21896,87 @@ var init_Menu = __esm({
21636
21896
  onClick: handleToggle
21637
21897
  }
21638
21898
  );
21639
- const menuContainerStyles = cn(
21640
- "bg-card",
21641
- "border-[length:var(--border-width)] border-border",
21642
- "shadow-elevation-popover",
21643
- "rounded-sm",
21644
- "min-w-0 sm:min-w-[200px] max-w-[calc(100vw-1rem)] py-1"
21645
- );
21646
- const renderMenuItem = (item, hasSubMenu, index) => {
21899
+ const renderMenuItems = (menuItems) => menuItems.map((item, index) => {
21900
+ const isDivider = item.id === "divider" || item.label === "divider";
21647
21901
  const itemId = item.id ?? `item-${item.label.toLowerCase().replace(/\s+/g, "-")}-${index}`;
21902
+ const hasSubMenu = !!(item.subMenu && item.subMenu.length > 0);
21648
21903
  const isDanger = item.variant === "danger";
21649
- return /* @__PURE__ */ jsxRuntime.jsx(
21650
- Box,
21651
- {
21652
- as: "button",
21653
- onClick: () => !item.disabled && handleItemClick({ ...item, id: itemId }),
21654
- "aria-disabled": item.disabled || void 0,
21655
- onMouseEnter: () => hasSubMenu && setActiveSubMenu(itemId),
21656
- "data-testid": item.event ? `action-${item.event}` : void 0,
21657
- className: cn(
21658
- "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
21659
- "text-sm transition-colors",
21660
- "hover:bg-muted",
21661
- "focus:outline-none focus:bg-muted",
21662
- "disabled:opacity-50 disabled:cursor-not-allowed",
21663
- item.disabled && "cursor-not-allowed",
21664
- isDanger && "text-error hover:bg-error/10"
21665
- ),
21666
- children: /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
21667
- item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: item.icon, size: "sm", className: "flex-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: item.icon, size: "sm", className: "flex-shrink-0" })),
21668
- /* @__PURE__ */ jsxRuntime.jsx(
21669
- Typography,
21670
- {
21671
- variant: "small",
21672
- className: cn("flex-1", isDanger && "text-red-600"),
21673
- children: item.label
21904
+ if (isDivider) {
21905
+ return /* @__PURE__ */ jsxRuntime.jsx(Divider, { className: "my-1" }, `divider-${index}`);
21906
+ }
21907
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { children: [
21908
+ /* @__PURE__ */ jsxRuntime.jsx(
21909
+ Box,
21910
+ {
21911
+ as: "button",
21912
+ onClick: () => handleItemClick({ ...item, id: itemId }, itemId),
21913
+ "aria-disabled": item.disabled || void 0,
21914
+ onMouseEnter: (e) => {
21915
+ if (hasSubMenu) {
21916
+ setActiveSubMenu(itemId);
21917
+ setActiveSubMenuRef(e.currentTarget);
21674
21918
  }
21919
+ },
21920
+ "data-testid": item.event ? `action-${item.event}` : void 0,
21921
+ className: cn(
21922
+ "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
21923
+ "text-sm transition-colors",
21924
+ "hover:bg-muted",
21925
+ "focus:outline-none focus:bg-muted",
21926
+ "disabled:opacity-50 disabled:cursor-not-allowed",
21927
+ item.disabled && "cursor-not-allowed",
21928
+ isDanger && "text-error hover:bg-error/10"
21675
21929
  ),
21676
- item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
21677
- hasSubMenu && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: direction === "rtl" ? "chevron-left" : "chevron-right", size: "sm", className: "flex-shrink-0" })
21678
- ] })
21679
- },
21680
- itemId
21681
- );
21682
- };
21683
- const renderMenuItems = (menuItems) => {
21684
- return menuItems.map((item, index) => {
21685
- const hasSubMenu = item.subMenu && item.subMenu.length > 0;
21686
- const isDivider = item.id === "divider" || item.label === "divider";
21687
- const itemId = item.id ?? `item-${item.label.toLowerCase().replace(/\s+/g, "-")}-${index}`;
21688
- if (isDivider) {
21689
- return /* @__PURE__ */ jsxRuntime.jsx(Divider, { className: "my-1" }, `divider-${index}`);
21690
- }
21691
- return /* @__PURE__ */ jsxRuntime.jsxs(Box, { children: [
21692
- renderMenuItem(item, !!hasSubMenu, index),
21693
- hasSubMenu && activeSubMenu === itemId && item.subMenu && /* @__PURE__ */ jsxRuntime.jsx(
21694
- Box,
21695
- {
21696
- className: cn(
21697
- "absolute top-0 z-50",
21698
- subMenuSideClass,
21699
- menuContainerStyles
21930
+ children: /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
21931
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: item.icon, size: "sm", className: "flex-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: item.icon, size: "sm", className: "flex-shrink-0" })),
21932
+ /* @__PURE__ */ jsxRuntime.jsx(
21933
+ Typography,
21934
+ {
21935
+ variant: "small",
21936
+ className: cn("flex-1", isDanger && "text-red-600"),
21937
+ children: item.label
21938
+ }
21700
21939
  ),
21701
- children: renderMenuItems(item.subMenu)
21702
- }
21703
- )
21704
- ] }, itemId);
21705
- });
21706
- };
21707
- return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "relative", children: [
21940
+ item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
21941
+ hasSubMenu && /* @__PURE__ */ jsxRuntime.jsx(
21942
+ Icon,
21943
+ {
21944
+ name: direction === "rtl" ? "chevron-left" : "chevron-right",
21945
+ size: "sm",
21946
+ className: "flex-shrink-0"
21947
+ }
21948
+ )
21949
+ ] })
21950
+ }
21951
+ ),
21952
+ hasSubMenu && activeSubMenu === itemId && item.subMenu && /* @__PURE__ */ jsxRuntime.jsx(
21953
+ SubMenu,
21954
+ {
21955
+ items: item.subMenu,
21956
+ itemRef: activeSubMenuRef,
21957
+ direction,
21958
+ eventBus
21959
+ }
21960
+ )
21961
+ ] }, itemId);
21962
+ });
21963
+ const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxRuntime.jsxs(
21964
+ "div",
21965
+ {
21966
+ ref: menuRef,
21967
+ className: cn("fixed z-50", menuContainerStyles, className),
21968
+ style: computeMenuStyle(effectivePosition, triggerRect),
21969
+ role: "menu",
21970
+ children: [
21971
+ header && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-2 border-b border-border", children: header }),
21972
+ renderMenuItems(items),
21973
+ footer && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-2 border-t border-border", children: footer })
21974
+ ]
21975
+ }
21976
+ ) : null;
21977
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
21708
21978
  triggerElement,
21709
- isOpen && triggerRect && /* @__PURE__ */ jsxRuntime.jsx(
21710
- Box,
21711
- {
21712
- ref: menuRef,
21713
- className: cn(
21714
- "absolute z-50",
21715
- menuContainerStyles,
21716
- positionClasses[effectivePosition],
21717
- className
21718
- ),
21719
- style: {
21720
- left: effectivePosition.includes("left") ? 0 : "auto",
21721
- right: effectivePosition.includes("right") ? 0 : "auto"
21722
- },
21723
- role: "menu",
21724
- children: renderMenuItems(items)
21725
- }
21726
- )
21979
+ panel && typeof document !== "undefined" ? reactDom.createPortal(panel, document.body) : panel
21727
21980
  ] });
21728
21981
  };
21729
21982
  Menu.displayName = "Menu";
@@ -23477,7 +23730,7 @@ var init_FilterGroup = __esm({
23477
23730
  Select,
23478
23731
  {
23479
23732
  value: selectedValues[filter.field] || "all",
23480
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
23733
+ onChange: (v) => handleFilterSelect(filter.field, v),
23481
23734
  options: [
23482
23735
  { value: "all", label: t("filterGroup.all") },
23483
23736
  ...filter.options?.map((opt) => ({
@@ -23560,7 +23813,7 @@ var init_FilterGroup = __esm({
23560
23813
  Select,
23561
23814
  {
23562
23815
  value: selectedValues[filter.field] || "all",
23563
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
23816
+ onChange: (v) => handleFilterSelect(filter.field, v),
23564
23817
  options: [
23565
23818
  { value: "all", label: t("filterGroup.allOf", { label: filter.label }) },
23566
23819
  ...filter.options?.map((opt) => ({
@@ -23678,7 +23931,7 @@ var init_FilterGroup = __esm({
23678
23931
  Select,
23679
23932
  {
23680
23933
  value: selectedValues[filter.field] || "all",
23681
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
23934
+ onChange: (v) => handleFilterSelect(filter.field, v),
23682
23935
  options: [
23683
23936
  { value: "all", label: t("filterGroup.all") },
23684
23937
  ...filter.options?.map((opt) => ({
@@ -27860,13 +28113,13 @@ var init_MapView = __esm({
27860
28113
  shadowSize: [41, 41]
27861
28114
  });
27862
28115
  L.Marker.prototype.options.icon = defaultIcon;
27863
- const { useEffect: useEffect72, useRef: useRef66, useCallback: useCallback112, useState: useState102 } = React79__namespace.default;
28116
+ const { useEffect: useEffect73, useRef: useRef67, useCallback: useCallback112, useState: useState103 } = React79__namespace.default;
27864
28117
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
27865
28118
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
27866
28119
  function MapUpdater({ centerLat, centerLng, zoom }) {
27867
28120
  const map = useMap();
27868
- const prevRef = useRef66({ centerLat, centerLng, zoom });
27869
- useEffect72(() => {
28121
+ const prevRef = useRef67({ centerLat, centerLng, zoom });
28122
+ useEffect73(() => {
27870
28123
  const prev = prevRef.current;
27871
28124
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
27872
28125
  map.setView([centerLat, centerLng], zoom);
@@ -27877,7 +28130,7 @@ var init_MapView = __esm({
27877
28130
  }
27878
28131
  function MapClickHandler({ onMapClick }) {
27879
28132
  const map = useMap();
27880
- useEffect72(() => {
28133
+ useEffect73(() => {
27881
28134
  if (!onMapClick) return;
27882
28135
  const handler = (e) => {
27883
28136
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -27905,7 +28158,7 @@ var init_MapView = __esm({
27905
28158
  showAttribution = true
27906
28159
  }) {
27907
28160
  const eventBus = useEventBus3();
27908
- const [clickedPosition, setClickedPosition] = useState102(null);
28161
+ const [clickedPosition, setClickedPosition] = useState103(null);
27909
28162
  const handleMapClick = useCallback112((lat, lng) => {
27910
28163
  if (showClickedPin) {
27911
28164
  setClickedPosition({ lat, lng });
@@ -33341,8 +33594,8 @@ var init_VersionDiff = __esm({
33341
33594
  return { added, removed };
33342
33595
  }, [diff]);
33343
33596
  const handleBeforeChange = React79.useCallback(
33344
- (e) => {
33345
- const id = e.target.value;
33597
+ (v) => {
33598
+ const id = v;
33346
33599
  setInternalBefore(id);
33347
33600
  onSelectBefore?.(id);
33348
33601
  if (selectBeforeEvent) eventBus.emit(`UI:${selectBeforeEvent}`, { id });
@@ -33350,8 +33603,8 @@ var init_VersionDiff = __esm({
33350
33603
  [onSelectBefore, selectBeforeEvent, eventBus]
33351
33604
  );
33352
33605
  const handleAfterChange = React79.useCallback(
33353
- (e) => {
33354
- const id = e.target.value;
33606
+ (v) => {
33607
+ const id = v;
33355
33608
  setInternalAfter(id);
33356
33609
  onSelectAfter?.(id);
33357
33610
  if (selectAfterEvent) eventBus.emit(`UI:${selectAfterEvent}`, { id });
@@ -36129,7 +36382,6 @@ var init_DocumentViewer = __esm({
36129
36382
  showPrint = false,
36130
36383
  actions,
36131
36384
  documents,
36132
- entity,
36133
36385
  isLoading = false,
36134
36386
  error,
36135
36387
  className
@@ -37978,11 +38230,11 @@ function RuleEditor({
37978
38230
  className
37979
38231
  }) {
37980
38232
  const { t } = hooks.useTranslate();
37981
- const handleWhenChange = React79.useCallback((e) => {
37982
- onChange({ ...rule, whenEvent: e.target.value });
38233
+ const handleWhenChange = React79.useCallback((v) => {
38234
+ onChange({ ...rule, whenEvent: v });
37983
38235
  }, [rule, onChange]);
37984
- const handleThenChange = React79.useCallback((e) => {
37985
- onChange({ ...rule, thenAction: e.target.value });
38236
+ const handleThenChange = React79.useCallback((v) => {
38237
+ onChange({ ...rule, thenAction: v });
37986
38238
  }, [rule, onChange]);
37987
38239
  return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: cn("items-center p-2 rounded-lg bg-muted/50 border border-border", className), gap: "sm", children: [
37988
38240
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-primary font-bold whitespace-nowrap", children: t("eventHandler.when") }),
@@ -39058,7 +39310,7 @@ var init_Form = __esm({
39058
39310
  ...commonProps,
39059
39311
  options,
39060
39312
  value: String(currentValue),
39061
- onChange: (e) => handleChange(fieldName, e.target.value),
39313
+ onChange: (v) => handleChange(fieldName, v),
39062
39314
  placeholder: field.placeholder || `Select ${label}...`
39063
39315
  }
39064
39316
  );
@@ -42944,7 +43196,7 @@ function TraitSlot({
42944
43196
  size = "md",
42945
43197
  showTooltip = true,
42946
43198
  categoryColors,
42947
- tooltipFrameUrl,
43199
+ tooltipFrameUrl = "",
42948
43200
  className,
42949
43201
  feedback,
42950
43202
  onItemDrop,
@@ -45845,18 +46097,32 @@ var init_WorldMapTemplate = __esm({
45845
46097
  }
45846
46098
  });
45847
46099
  function lazyThree(name, loader) {
45848
- const Lazy = React79__namespace.default.lazy(() => loader().then((m) => ({ default: m[name] })));
46100
+ const Lazy = React79__namespace.default.lazy(
46101
+ () => loader().then((m) => {
46102
+ const Resolved = m[name];
46103
+ if (!Resolved) {
46104
+ throw new Error(
46105
+ `[@almadar/ui] 3D component "${name}" was not found in the three subpath bundle.`
46106
+ );
46107
+ }
46108
+ return { default: Resolved };
46109
+ })
46110
+ );
45849
46111
  function ThreeWrapper(props) {
45850
46112
  return React79__namespace.default.createElement(
45851
- React79__namespace.default.Suspense,
45852
- { fallback: null },
45853
- React79__namespace.default.createElement(Lazy, props)
46113
+ ThreeBoundary,
46114
+ { name },
46115
+ React79__namespace.default.createElement(
46116
+ React79__namespace.default.Suspense,
46117
+ { fallback: null },
46118
+ React79__namespace.default.createElement(Lazy, props)
46119
+ )
45854
46120
  );
45855
46121
  }
45856
46122
  ThreeWrapper.displayName = `Lazy(${name})`;
45857
46123
  return ThreeWrapper;
45858
46124
  }
45859
- var FeatureRenderer, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
46125
+ var ThreeBoundary, FeatureRenderer, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
45860
46126
  var init_component_registry_generated = __esm({
45861
46127
  "components/core/organisms/component-registry.generated.ts"() {
45862
46128
  init_AboutPageTemplate();
@@ -46142,6 +46408,28 @@ var init_component_registry_generated = __esm({
46142
46408
  init_WorldMapBoard();
46143
46409
  init_WorldMapTemplate();
46144
46410
  init_XPBar();
46411
+ ThreeBoundary = class extends React79__namespace.default.Component {
46412
+ constructor() {
46413
+ super(...arguments);
46414
+ __publicField(this, "state", { failed: false });
46415
+ }
46416
+ static getDerivedStateFromError() {
46417
+ return { failed: true };
46418
+ }
46419
+ render() {
46420
+ if (this.state.failed) {
46421
+ return React79__namespace.default.createElement(
46422
+ "div",
46423
+ {
46424
+ "data-testid": "three-unavailable",
46425
+ style: { padding: 16, fontSize: 13, lineHeight: 1.5, opacity: 0.7 }
46426
+ },
46427
+ `3D pattern "${this.props.name}" requires three.js. Install the optional peers three + @react-three/fiber + @react-three/drei (matching the host React major) to render it.`
46428
+ );
46429
+ }
46430
+ return this.props.children;
46431
+ }
46432
+ };
46145
46433
  FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/molecules/game/three'));
46146
46434
  GameCanvas3D = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
46147
46435
  GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));