@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
@@ -3476,6 +3476,8 @@ var init_Input = __esm({
3476
3476
  className,
3477
3477
  inputType,
3478
3478
  type: htmlType,
3479
+ label,
3480
+ helperText,
3479
3481
  error,
3480
3482
  leftIcon,
3481
3483
  rightIcon,
@@ -3531,82 +3533,95 @@ var init_Input = __esm({
3531
3533
  onClear?.();
3532
3534
  }
3533
3535
  };
3536
+ const wrapField = (field) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full", children: [
3537
+ label && /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-foreground mb-1", children: label }),
3538
+ field,
3539
+ (helperText || error) && /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn("mt-1 text-xs", error ? "text-error" : "text-muted-foreground"), children: error ?? helperText })
3540
+ ] });
3534
3541
  if (type === "select") {
3535
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
3536
- 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 }),
3537
- /* @__PURE__ */ jsxRuntime.jsxs(
3538
- "select",
3542
+ return wrapField(
3543
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
3544
+ 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 }),
3545
+ /* @__PURE__ */ jsxRuntime.jsxs(
3546
+ "select",
3547
+ {
3548
+ ref,
3549
+ value,
3550
+ onChange: handleChange,
3551
+ className: cn(baseClassName, "appearance-none pr-10", className),
3552
+ ...props,
3553
+ children: [
3554
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: t("form.selectPlaceholder", { label: "" }) }),
3555
+ options?.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.value, children: opt.label }, opt.value))
3556
+ ]
3557
+ }
3558
+ ),
3559
+ /* @__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" }) })
3560
+ ] })
3561
+ );
3562
+ }
3563
+ if (type === "textarea") {
3564
+ return wrapField(
3565
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
3566
+ "textarea",
3539
3567
  {
3540
3568
  ref,
3541
3569
  value,
3542
3570
  onChange: handleChange,
3543
- className: cn(baseClassName, "appearance-none pr-10", className),
3544
- ...props,
3545
- children: [
3546
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: t("form.selectPlaceholder", { label: "" }) }),
3547
- options?.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.value, children: opt.label }, opt.value))
3548
- ]
3571
+ rows: rows2,
3572
+ className: baseClassName,
3573
+ ...props
3549
3574
  }
3550
- ),
3551
- /* @__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" }) })
3552
- ] });
3553
- }
3554
- if (type === "textarea") {
3555
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
3556
- "textarea",
3557
- {
3558
- ref,
3559
- value,
3560
- onChange: handleChange,
3561
- rows: rows2,
3562
- className: baseClassName,
3563
- ...props
3564
- }
3565
- ) });
3575
+ ) })
3576
+ );
3566
3577
  }
3567
3578
  if (type === "checkbox") {
3568
- return /* @__PURE__ */ jsxRuntime.jsx(
3569
- "input",
3570
- {
3571
- ref,
3572
- type: "checkbox",
3573
- checked: props.checked,
3574
- onChange: handleChange,
3575
- className: cn(
3576
- "h-icon-default w-icon-default rounded-sm",
3577
- "border-border",
3578
- "text-primary focus:ring-ring",
3579
- "disabled:opacity-50 disabled:cursor-not-allowed",
3580
- className
3581
- ),
3582
- ...props
3583
- }
3579
+ return wrapField(
3580
+ /* @__PURE__ */ jsxRuntime.jsx(
3581
+ "input",
3582
+ {
3583
+ ref,
3584
+ type: "checkbox",
3585
+ checked: props.checked,
3586
+ onChange: handleChange,
3587
+ className: cn(
3588
+ "h-icon-default w-icon-default rounded-sm",
3589
+ "border-border",
3590
+ "text-primary focus:ring-ring",
3591
+ "disabled:opacity-50 disabled:cursor-not-allowed",
3592
+ className
3593
+ ),
3594
+ ...props
3595
+ }
3596
+ )
3584
3597
  );
3585
3598
  }
3586
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
3587
- 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 }),
3588
- /* @__PURE__ */ jsxRuntime.jsx(
3589
- "input",
3590
- {
3591
- ref,
3592
- type,
3593
- value,
3594
- onChange: handleChange,
3595
- className: baseClassName,
3596
- ...props
3597
- }
3598
- ),
3599
- showClearButton && /* @__PURE__ */ jsxRuntime.jsx(
3600
- "button",
3601
- {
3602
- type: "button",
3603
- onClick: handleClear,
3604
- className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground hover:text-foreground",
3605
- children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
3606
- }
3607
- ),
3608
- 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) })
3609
- ] });
3599
+ return wrapField(
3600
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
3601
+ 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 }),
3602
+ /* @__PURE__ */ jsxRuntime.jsx(
3603
+ "input",
3604
+ {
3605
+ ref,
3606
+ type,
3607
+ value,
3608
+ onChange: handleChange,
3609
+ className: baseClassName,
3610
+ ...props
3611
+ }
3612
+ ),
3613
+ showClearButton && /* @__PURE__ */ jsxRuntime.jsx(
3614
+ "button",
3615
+ {
3616
+ type: "button",
3617
+ onClick: handleClear,
3618
+ className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground hover:text-foreground",
3619
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
3620
+ }
3621
+ ),
3622
+ 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) })
3623
+ ] })
3624
+ );
3610
3625
  }
3611
3626
  );
3612
3627
  Input.displayName = "Input";
@@ -3677,6 +3692,190 @@ var init_Textarea = __esm({
3677
3692
  Textarea.displayName = "Textarea";
3678
3693
  }
3679
3694
  });
3695
+ function flatOptions(opts, groups) {
3696
+ const flat = opts ?? [];
3697
+ const grp = (groups ?? []).flatMap((g) => g.options);
3698
+ return [...flat, ...grp];
3699
+ }
3700
+ function NativeSelect({
3701
+ className,
3702
+ options,
3703
+ groups,
3704
+ placeholder,
3705
+ error,
3706
+ onChange,
3707
+ value,
3708
+ ...props
3709
+ }) {
3710
+ const eventBus = useEventBus();
3711
+ const handleChange = (e) => {
3712
+ if (typeof onChange === "string") {
3713
+ eventBus.emit(`UI:${onChange}`, { value: e.target.value });
3714
+ } else {
3715
+ onChange?.(e.target.value);
3716
+ }
3717
+ };
3718
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
3719
+ /* @__PURE__ */ jsxRuntime.jsxs(
3720
+ "select",
3721
+ {
3722
+ onChange: handleChange,
3723
+ value,
3724
+ className: cn(
3725
+ "block w-full border-[length:var(--border-width)] shadow-sm appearance-none",
3726
+ "px-3 py-2 pr-10 text-sm text-foreground font-medium",
3727
+ "bg-card",
3728
+ "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
3729
+ "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
3730
+ error ? "border-error focus:border-error" : "border-border focus:border-primary",
3731
+ className
3732
+ ),
3733
+ ...props,
3734
+ children: [
3735
+ placeholder && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: placeholder }),
3736
+ options?.map((option) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)),
3737
+ 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))
3738
+ ]
3739
+ }
3740
+ ),
3741
+ /* @__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" }) })
3742
+ ] });
3743
+ }
3744
+ function RichSelect({
3745
+ className,
3746
+ options,
3747
+ groups,
3748
+ placeholder,
3749
+ error,
3750
+ onChange,
3751
+ value,
3752
+ multiple,
3753
+ searchable,
3754
+ clearable,
3755
+ disabled
3756
+ }) {
3757
+ const eventBus = useEventBus();
3758
+ const [open, setOpen] = React80.useState(false);
3759
+ const [search, setSearch] = React80.useState("");
3760
+ const containerRef = React80.useRef(null);
3761
+ const selected = multiple ? Array.isArray(value) ? value : value ? [value] : [] : value ? [value] : [];
3762
+ const all = flatOptions(options, groups);
3763
+ const filtered = searchable && search ? all.filter((o) => o.label.toLowerCase().includes(search.toLowerCase())) : all;
3764
+ const toggle = (optValue) => {
3765
+ let next;
3766
+ if (multiple) {
3767
+ next = selected.includes(optValue) ? selected.filter((v) => v !== optValue) : [...selected, optValue];
3768
+ } else {
3769
+ next = optValue;
3770
+ setOpen(false);
3771
+ }
3772
+ if (typeof onChange === "string") {
3773
+ eventBus.emit(`UI:${onChange}`, { value: next });
3774
+ } else {
3775
+ onChange?.(next);
3776
+ }
3777
+ };
3778
+ const clear = (e) => {
3779
+ e.stopPropagation();
3780
+ const next = multiple ? [] : "";
3781
+ if (typeof onChange === "string") {
3782
+ eventBus.emit(`UI:${onChange}`, { value: next });
3783
+ } else {
3784
+ onChange?.(next);
3785
+ }
3786
+ };
3787
+ React80.useEffect(() => {
3788
+ const handler = (e) => {
3789
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
3790
+ setOpen(false);
3791
+ setSearch("");
3792
+ }
3793
+ };
3794
+ document.addEventListener("mousedown", handler);
3795
+ return () => document.removeEventListener("mousedown", handler);
3796
+ }, []);
3797
+ const displayLabel = selected.length === 0 ? placeholder ?? "" : multiple ? `${selected.length} selected` : all.find((o) => o.value === selected[0])?.label ?? selected[0];
3798
+ const hasValue = selected.length > 0;
3799
+ const renderOptions = (opts) => opts.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
3800
+ "button",
3801
+ {
3802
+ type: "button",
3803
+ disabled: opt.disabled,
3804
+ onClick: () => !opt.disabled && toggle(opt.value),
3805
+ className: cn(
3806
+ "w-full flex items-center justify-between px-3 py-1.5 text-sm text-start",
3807
+ "hover:bg-muted transition-colors",
3808
+ "disabled:opacity-50 disabled:cursor-not-allowed",
3809
+ selected.includes(opt.value) && "text-primary font-medium"
3810
+ ),
3811
+ children: [
3812
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: opt.label }),
3813
+ selected.includes(opt.value) && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "check", className: "h-icon-default w-icon-default" })
3814
+ ]
3815
+ },
3816
+ opt.value
3817
+ ));
3818
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, className: cn("relative w-full", className), children: [
3819
+ /* @__PURE__ */ jsxRuntime.jsx(
3820
+ "button",
3821
+ {
3822
+ type: "button",
3823
+ disabled,
3824
+ onClick: () => !disabled && setOpen((o) => !o),
3825
+ className: cn(
3826
+ "block w-full border-[length:var(--border-width)] shadow-sm",
3827
+ "px-3 py-2 pr-10 text-sm text-start font-medium",
3828
+ "bg-card rounded-sm",
3829
+ "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
3830
+ "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
3831
+ error ? "border-error focus:border-error" : "border-border focus:border-primary",
3832
+ !hasValue && "text-muted-foreground"
3833
+ ),
3834
+ children: displayLabel
3835
+ }
3836
+ ),
3837
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center gap-1 pointer-events-none", children: [
3838
+ clearable && hasValue && /* @__PURE__ */ jsxRuntime.jsx(
3839
+ "button",
3840
+ {
3841
+ type: "button",
3842
+ onClick: clear,
3843
+ className: "pointer-events-auto text-muted-foreground hover:text-foreground",
3844
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
3845
+ }
3846
+ ),
3847
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default text-foreground" })
3848
+ ] }),
3849
+ open && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(
3850
+ "absolute z-50 mt-1 w-full",
3851
+ "bg-card border-[length:var(--border-width)] border-border",
3852
+ "rounded-sm shadow-elevation-popover py-1 max-h-60 overflow-y-auto"
3853
+ ), children: [
3854
+ searchable && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 pb-1 border-b border-border", children: /* @__PURE__ */ jsxRuntime.jsx(
3855
+ "input",
3856
+ {
3857
+ autoFocus: true,
3858
+ type: "text",
3859
+ value: search,
3860
+ onChange: (e) => setSearch(e.target.value),
3861
+ placeholder: "Search\u2026",
3862
+ className: cn(
3863
+ "w-full px-2 py-1 text-sm bg-transparent",
3864
+ "focus:outline-none text-foreground placeholder:text-muted-foreground"
3865
+ )
3866
+ }
3867
+ ) }),
3868
+ groups && groups.length > 0 ? groups.map((g) => {
3869
+ const groupFiltered = searchable && search ? g.options.filter((o) => o.label.toLowerCase().includes(search.toLowerCase())) : g.options;
3870
+ if (groupFiltered.length === 0) return null;
3871
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3872
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-1 text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: g.label }),
3873
+ renderOptions(groupFiltered)
3874
+ ] }, g.label);
3875
+ }) : renderOptions(filtered)
3876
+ ] })
3877
+ ] });
3878
+ }
3680
3879
  var Select;
3681
3880
  var init_Select = __esm({
3682
3881
  "components/core/atoms/Select.tsx"() {
@@ -3684,47 +3883,12 @@ var init_Select = __esm({
3684
3883
  init_Icon();
3685
3884
  init_useEventBus();
3686
3885
  Select = React80__namespace.default.forwardRef(
3687
- ({ className, options, placeholder, error, onChange, ...props }, ref) => {
3688
- const eventBus = useEventBus();
3689
- const handleChange = (e) => {
3690
- if (typeof onChange === "string") {
3691
- eventBus.emit(`UI:${onChange}`, { value: e.target.value });
3692
- } else {
3693
- onChange?.(e);
3694
- }
3695
- };
3696
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
3697
- /* @__PURE__ */ jsxRuntime.jsxs(
3698
- "select",
3699
- {
3700
- ref,
3701
- onChange: handleChange,
3702
- className: cn(
3703
- "block w-full border-[length:var(--border-width)] shadow-sm appearance-none",
3704
- "px-3 py-2 pr-10 text-sm text-foreground font-medium",
3705
- "bg-card",
3706
- "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
3707
- "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
3708
- error ? "border-error focus:border-error" : "border-border focus:border-primary",
3709
- className
3710
- ),
3711
- ...props,
3712
- children: [
3713
- placeholder && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: placeholder }),
3714
- options.map((option) => /* @__PURE__ */ jsxRuntime.jsx(
3715
- "option",
3716
- {
3717
- value: option.value,
3718
- disabled: option.disabled,
3719
- children: option.label
3720
- },
3721
- option.value
3722
- ))
3723
- ]
3724
- }
3725
- ),
3726
- /* @__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" }) })
3727
- ] });
3886
+ (props, _ref) => {
3887
+ const { multiple, searchable, clearable } = props;
3888
+ if (multiple || searchable || clearable) {
3889
+ return /* @__PURE__ */ jsxRuntime.jsx(RichSelect, { ...props });
3890
+ }
3891
+ return /* @__PURE__ */ jsxRuntime.jsx(NativeSelect, { ...props });
3728
3892
  }
3729
3893
  );
3730
3894
  Select.displayName = "Select";
@@ -3778,11 +3942,54 @@ var init_Checkbox = __esm({
3778
3942
  Checkbox.displayName = "Checkbox";
3779
3943
  }
3780
3944
  });
3945
+ var sizeStyles3, Spinner;
3946
+ var init_Spinner = __esm({
3947
+ "components/core/atoms/Spinner.tsx"() {
3948
+ init_cn();
3949
+ init_Icon();
3950
+ sizeStyles3 = {
3951
+ xs: "h-3 w-3",
3952
+ sm: "h-icon-default w-icon-default",
3953
+ md: "h-6 w-6",
3954
+ lg: "h-8 w-8"
3955
+ };
3956
+ Spinner = React80__namespace.default.forwardRef(
3957
+ ({ className, size = "md", overlay, ...props }, ref) => {
3958
+ if (overlay) {
3959
+ return /* @__PURE__ */ jsxRuntime.jsx(
3960
+ "div",
3961
+ {
3962
+ ref,
3963
+ className: cn(
3964
+ "absolute inset-0 z-10 flex items-center justify-center",
3965
+ "bg-background/60 backdrop-blur-sm",
3966
+ className
3967
+ ),
3968
+ ...props,
3969
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "loader", className: cn("animate-spin text-foreground", sizeStyles3[size]) })
3970
+ }
3971
+ );
3972
+ }
3973
+ return /* @__PURE__ */ jsxRuntime.jsx(
3974
+ "div",
3975
+ {
3976
+ ref,
3977
+ className: cn("text-foreground", className),
3978
+ ...props,
3979
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "loader", className: cn("animate-spin", sizeStyles3[size]) })
3980
+ }
3981
+ );
3982
+ }
3983
+ );
3984
+ Spinner.displayName = "Spinner";
3985
+ }
3986
+ });
3781
3987
  var variantStyles4, paddingStyles2, shadowStyles2, lookStyles2, Card, CardHeader, CardTitle, CardContent, CardBody, CardFooter;
3782
3988
  var init_Card = __esm({
3783
3989
  "components/core/atoms/Card.tsx"() {
3784
3990
  init_cn();
3785
3991
  init_useEventBus();
3992
+ init_Spinner();
3786
3993
  variantStyles4 = {
3787
3994
  default: [
3788
3995
  "bg-card",
@@ -3847,6 +4054,7 @@ var init_Card = __esm({
3847
4054
  look = "elevated",
3848
4055
  children,
3849
4056
  action,
4057
+ loading,
3850
4058
  onClick,
3851
4059
  ...props
3852
4060
  }, ref) => {
@@ -3860,7 +4068,7 @@ var init_Card = __esm({
3860
4068
  {
3861
4069
  ref,
3862
4070
  className: cn(
3863
- "rounded-container",
4071
+ "rounded-container relative",
3864
4072
  "transition-all duration-[var(--transition-normal)]",
3865
4073
  variantStyles4[variant],
3866
4074
  paddingStyles2[padding],
@@ -3871,6 +4079,7 @@ var init_Card = __esm({
3871
4079
  onClick: handleClick,
3872
4080
  ...props,
3873
4081
  children: [
4082
+ loading && /* @__PURE__ */ jsxRuntime.jsx(Spinner, { overlay: true, size: "md" }),
3874
4083
  (title || subtitle) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-4", children: [
3875
4084
  title && /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg text-card-foreground font-bold", children: title }),
3876
4085
  subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mt-1", children: subtitle })
@@ -3912,7 +4121,7 @@ var init_Card = __esm({
3912
4121
  CardFooter.displayName = "CardFooter";
3913
4122
  }
3914
4123
  });
3915
- var variantStyles5, sizeStyles3, iconSizes, FilterPill;
4124
+ var variantStyles5, sizeStyles4, iconSizes, FilterPill;
3916
4125
  var init_FilterPill = __esm({
3917
4126
  "components/core/atoms/FilterPill.tsx"() {
3918
4127
  init_cn();
@@ -3946,7 +4155,7 @@ var init_FilterPill = __esm({
3946
4155
  "border-[length:var(--border-width-thin)] border-border"
3947
4156
  ].join(" ")
3948
4157
  };
3949
- sizeStyles3 = {
4158
+ sizeStyles4 = {
3950
4159
  sm: "px-2 py-0.5 text-xs",
3951
4160
  md: "px-2.5 py-1 text-sm",
3952
4161
  lg: "px-3 py-1.5 text-base"
@@ -3990,7 +4199,7 @@ var init_FilterPill = __esm({
3990
4199
  className: cn(
3991
4200
  "inline-flex items-center gap-1 font-bold rounded-pill",
3992
4201
  variantStyles5[variant],
3993
- sizeStyles3[size],
4202
+ sizeStyles4[size],
3994
4203
  (onClick || clickEvent) && "cursor-pointer",
3995
4204
  className
3996
4205
  ),
@@ -4022,33 +4231,6 @@ var init_FilterPill = __esm({
4022
4231
  FilterPill.displayName = "FilterPill";
4023
4232
  }
4024
4233
  });
4025
- var sizeStyles4, Spinner;
4026
- var init_Spinner = __esm({
4027
- "components/core/atoms/Spinner.tsx"() {
4028
- init_cn();
4029
- init_Icon();
4030
- sizeStyles4 = {
4031
- xs: "h-3 w-3",
4032
- sm: "h-icon-default w-icon-default",
4033
- md: "h-6 w-6",
4034
- lg: "h-8 w-8"
4035
- };
4036
- Spinner = React80__namespace.default.forwardRef(
4037
- ({ className, size = "md", ...props }, ref) => {
4038
- return /* @__PURE__ */ jsxRuntime.jsx(
4039
- "div",
4040
- {
4041
- ref,
4042
- className: cn("text-foreground", className),
4043
- ...props,
4044
- children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "loader", className: cn("animate-spin", sizeStyles4[size]) })
4045
- }
4046
- );
4047
- }
4048
- );
4049
- Spinner.displayName = "Spinner";
4050
- }
4051
- });
4052
4234
  function generateInitials(name) {
4053
4235
  const parts = name.trim().split(/\s+/);
4054
4236
  if (parts.length === 1) {
@@ -16515,30 +16697,30 @@ var init_BranchingLogicBuilder = __esm({
16515
16697
  if (!sourceQuestion?.optionValues) return [];
16516
16698
  return sourceQuestion.optionValues.map((v) => ({ value: v, label: v }));
16517
16699
  }, [sourceQuestion]);
16518
- const handleSource = (e) => {
16519
- onChange({ ...rule, sourceQuestionId: e.target.value });
16700
+ const handleSource = (v) => {
16701
+ onChange({ ...rule, sourceQuestionId: v });
16520
16702
  };
16521
- const handleOperator = (e) => {
16522
- const next = e.target.value;
16703
+ const handleOperator = (v) => {
16704
+ const next = v;
16523
16705
  const nextValue = next === "in" && !Array.isArray(rule.value) ? rule.value ? [rule.value] : [] : next !== "in" && Array.isArray(rule.value) ? rule.value[0] ?? "" : rule.value;
16524
16706
  onChange({ ...rule, operator: next, value: nextValue });
16525
16707
  };
16526
16708
  const handleScalarValue = (e) => {
16527
16709
  onChange({ ...rule, value: e.target.value });
16528
16710
  };
16529
- const handleAddChip = (e) => {
16530
- const v = e.target.value;
16531
- if (!v) return;
16711
+ const handleAddChip = (v) => {
16712
+ const val = v;
16713
+ if (!val) return;
16532
16714
  const current = Array.isArray(rule.value) ? rule.value : [];
16533
- if (current.includes(v)) return;
16534
- onChange({ ...rule, value: [...current, v] });
16715
+ if (current.includes(val)) return;
16716
+ onChange({ ...rule, value: [...current, val] });
16535
16717
  };
16536
16718
  const handleRemoveChip = (chip) => {
16537
16719
  const current = Array.isArray(rule.value) ? rule.value : [];
16538
16720
  onChange({ ...rule, value: current.filter((c) => c !== chip) });
16539
16721
  };
16540
- const handleTarget = (e) => {
16541
- onChange({ ...rule, targetQuestionId: e.target.value });
16722
+ const handleTarget = (v) => {
16723
+ onChange({ ...rule, targetQuestionId: v });
16542
16724
  };
16543
16725
  const isMulti = rule.operator === "in";
16544
16726
  const chips = Array.isArray(rule.value) ? rule.value : [];
@@ -16619,7 +16801,7 @@ var init_BranchingLogicBuilder = __esm({
16619
16801
  options: valueOptions,
16620
16802
  value: scalarValue,
16621
16803
  placeholder: t("branchingLogic.selectValue"),
16622
- onChange: (e) => onChange({ ...rule, value: e.target.value }),
16804
+ onChange: (v) => onChange({ ...rule, value: v }),
16623
16805
  disabled: readOnly
16624
16806
  }
16625
16807
  ) : /* @__PURE__ */ jsxRuntime.jsx(
@@ -19051,7 +19233,7 @@ var init_Pagination = __esm({
19051
19233
  Select,
19052
19234
  {
19053
19235
  value: String(pageSize),
19054
- onChange: (e) => handlePageSizeChange(Number(e.target.value)),
19236
+ onChange: (v) => handlePageSizeChange(Number(v)),
19055
19237
  options: pageSizeOptions.map((size) => ({
19056
19238
  value: String(size),
19057
19239
  label: String(size)
@@ -21977,7 +22159,84 @@ var init_DashboardLayout = __esm({
21977
22159
  NavLinkBottom.displayName = "NavLinkBottom";
21978
22160
  }
21979
22161
  });
21980
- var Menu;
22162
+ function computeMenuStyle(position, triggerRect) {
22163
+ const isTop = position.startsWith("top");
22164
+ const isRight = position.endsWith("right") || position.endsWith("end");
22165
+ if (isTop) {
22166
+ return {
22167
+ top: triggerRect.top - MENU_GAP,
22168
+ transform: "translateY(-100%)",
22169
+ ...isRight ? { right: window.innerWidth - triggerRect.right } : { left: triggerRect.left }
22170
+ };
22171
+ }
22172
+ return {
22173
+ top: triggerRect.bottom + MENU_GAP,
22174
+ ...isRight ? { right: window.innerWidth - triggerRect.right } : { left: triggerRect.left }
22175
+ };
22176
+ }
22177
+ function SubMenu({
22178
+ items,
22179
+ itemRef,
22180
+ direction,
22181
+ eventBus
22182
+ }) {
22183
+ const [rect, setRect] = React80.useState(null);
22184
+ React80.useEffect(() => {
22185
+ if (itemRef) {
22186
+ setRect(itemRef.getBoundingClientRect());
22187
+ }
22188
+ }, [itemRef]);
22189
+ if (!rect) return null;
22190
+ const isRtl = direction === "rtl";
22191
+ const style = {
22192
+ top: rect.top,
22193
+ ...isRtl ? { right: window.innerWidth - rect.left } : { left: rect.right }
22194
+ };
22195
+ const panel = /* @__PURE__ */ jsxRuntime.jsx(
22196
+ "div",
22197
+ {
22198
+ className: cn("fixed z-50", menuContainerStyles),
22199
+ style,
22200
+ children: items.map((item, index) => {
22201
+ const isDivider = item.id === "divider" || item.label === "divider";
22202
+ const itemId = item.id ?? `item-${item.label.toLowerCase().replace(/\s+/g, "-")}-${index}`;
22203
+ const isDanger = item.variant === "danger";
22204
+ if (isDivider) {
22205
+ return /* @__PURE__ */ jsxRuntime.jsx(Divider, { className: "my-1" }, `divider-${index}`);
22206
+ }
22207
+ return /* @__PURE__ */ jsxRuntime.jsxs(
22208
+ Box,
22209
+ {
22210
+ as: "button",
22211
+ onClick: () => {
22212
+ if (item.disabled) return;
22213
+ if (item.event) eventBus.emit(`UI:${item.event}`, { itemId, label: item.label });
22214
+ item.onClick?.();
22215
+ },
22216
+ "aria-disabled": item.disabled || void 0,
22217
+ "data-testid": item.event ? `action-${item.event}` : void 0,
22218
+ className: cn(
22219
+ "w-full flex items-center gap-3 px-4 py-2 text-start",
22220
+ "text-sm transition-colors",
22221
+ "hover:bg-muted focus:outline-none focus:bg-muted",
22222
+ "disabled:opacity-50 disabled:cursor-not-allowed",
22223
+ item.disabled && "cursor-not-allowed",
22224
+ isDanger && "text-error hover:bg-error/10"
22225
+ ),
22226
+ children: [
22227
+ 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" })),
22228
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: cn("flex-1", isDanger && "text-red-600"), children: item.label }),
22229
+ item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto text-xs font-medium", children: item.badge })
22230
+ ]
22231
+ },
22232
+ itemId
22233
+ );
22234
+ })
22235
+ }
22236
+ );
22237
+ return typeof document !== "undefined" ? reactDom.createPortal(panel, document.body) : panel;
22238
+ }
22239
+ var MENU_GAP, menuContainerStyles, Menu;
21981
22240
  var init_Menu = __esm({
21982
22241
  "components/core/molecules/Menu.tsx"() {
21983
22242
  "use client";
@@ -21988,16 +22247,27 @@ var init_Menu = __esm({
21988
22247
  init_Badge();
21989
22248
  init_cn();
21990
22249
  init_useEventBus();
22250
+ MENU_GAP = 4;
22251
+ menuContainerStyles = cn(
22252
+ "bg-card",
22253
+ "border-[length:var(--border-width)] border-border",
22254
+ "shadow-elevation-popover",
22255
+ "rounded-sm",
22256
+ "min-w-0 sm:min-w-[200px] max-w-[calc(100vw-1rem)] py-1"
22257
+ );
21991
22258
  Menu = ({
21992
22259
  trigger,
21993
22260
  items,
21994
22261
  position = "bottom-left",
21995
- className
22262
+ className,
22263
+ header,
22264
+ footer
21996
22265
  }) => {
21997
22266
  const eventBus = useEventBus();
21998
- const { t, direction } = hooks.useTranslate();
22267
+ const { direction } = hooks.useTranslate();
21999
22268
  const [isOpen, setIsOpen] = React80.useState(false);
22000
22269
  const [activeSubMenu, setActiveSubMenu] = React80.useState(null);
22270
+ const [activeSubMenuRef, setActiveSubMenuRef] = React80.useState(null);
22001
22271
  const [triggerRect, setTriggerRect] = React80.useState(null);
22002
22272
  const triggerRef = React80.useRef(null);
22003
22273
  const menuRef = React80.useRef(null);
@@ -22012,13 +22282,14 @@ var init_Menu = __esm({
22012
22282
  }
22013
22283
  setIsOpen(!isOpen);
22014
22284
  setActiveSubMenu(null);
22285
+ setActiveSubMenuRef(null);
22015
22286
  };
22016
- const handleItemClick = (item) => {
22287
+ const handleItemClick = (item, itemId) => {
22017
22288
  if (item.disabled) return;
22018
22289
  if (item.subMenu && item.subMenu.length > 0) {
22019
- setActiveSubMenu(item.id ?? null);
22290
+ setActiveSubMenu(itemId);
22020
22291
  } else {
22021
- if (item.event) eventBus.emit(`UI:${item.event}`, { itemId: item.id, label: item.label });
22292
+ if (item.event) eventBus.emit(`UI:${item.event}`, { itemId, label: item.label });
22022
22293
  item.onClick?.();
22023
22294
  setIsOpen(false);
22024
22295
  }
@@ -22033,22 +22304,12 @@ var init_Menu = __esm({
22033
22304
  if (isOpen && menuRef.current && !menuRef.current.contains(e.target) && triggerRef.current && !triggerRef.current.contains(e.target)) {
22034
22305
  setIsOpen(false);
22035
22306
  setActiveSubMenu(null);
22307
+ setActiveSubMenuRef(null);
22036
22308
  }
22037
22309
  };
22038
22310
  document.addEventListener("mousedown", handleClickOutside);
22039
22311
  return () => document.removeEventListener("mousedown", handleClickOutside);
22040
22312
  }, [isOpen]);
22041
- const positionClasses = {
22042
- "top-left": "bottom-full left-0 mb-2",
22043
- "top-right": "bottom-full right-0 mb-2",
22044
- "bottom-left": "top-full left-0 mt-2",
22045
- "bottom-right": "top-full right-0 mt-2",
22046
- // Aliases for pattern compatibility
22047
- "top-start": "bottom-full left-0 mb-2",
22048
- "top-end": "bottom-full right-0 mb-2",
22049
- "bottom-start": "top-full left-0 mt-2",
22050
- "bottom-end": "top-full right-0 mt-2"
22051
- };
22052
22313
  const rtlMirror = {
22053
22314
  "top-left": "top-right",
22054
22315
  "top-right": "top-left",
@@ -22060,7 +22321,6 @@ var init_Menu = __esm({
22060
22321
  "bottom-end": "bottom-start"
22061
22322
  };
22062
22323
  const effectivePosition = direction === "rtl" ? rtlMirror[position] ?? position : position;
22063
- const subMenuSideClass = direction === "rtl" ? "right-full mr-2" : "left-full ml-2";
22064
22324
  const triggerChild = React80__namespace.default.isValidElement(trigger) ? trigger : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", as: "span", children: trigger });
22065
22325
  const triggerElement = React80__namespace.default.cloneElement(
22066
22326
  triggerChild,
@@ -22069,94 +22329,87 @@ var init_Menu = __esm({
22069
22329
  onClick: handleToggle
22070
22330
  }
22071
22331
  );
22072
- const menuContainerStyles = cn(
22073
- "bg-card",
22074
- "border-[length:var(--border-width)] border-border",
22075
- "shadow-elevation-popover",
22076
- "rounded-sm",
22077
- "min-w-0 sm:min-w-[200px] max-w-[calc(100vw-1rem)] py-1"
22078
- );
22079
- const renderMenuItem = (item, hasSubMenu, index) => {
22332
+ const renderMenuItems = (menuItems) => menuItems.map((item, index) => {
22333
+ const isDivider = item.id === "divider" || item.label === "divider";
22080
22334
  const itemId = item.id ?? `item-${item.label.toLowerCase().replace(/\s+/g, "-")}-${index}`;
22335
+ const hasSubMenu = !!(item.subMenu && item.subMenu.length > 0);
22081
22336
  const isDanger = item.variant === "danger";
22082
- return /* @__PURE__ */ jsxRuntime.jsx(
22083
- Box,
22084
- {
22085
- as: "button",
22086
- onClick: () => !item.disabled && handleItemClick({ ...item, id: itemId }),
22087
- "aria-disabled": item.disabled || void 0,
22088
- onMouseEnter: () => hasSubMenu && setActiveSubMenu(itemId),
22089
- "data-testid": item.event ? `action-${item.event}` : void 0,
22090
- className: cn(
22091
- "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
22092
- "text-sm transition-colors",
22093
- "hover:bg-muted",
22094
- "focus:outline-none focus:bg-muted",
22095
- "disabled:opacity-50 disabled:cursor-not-allowed",
22096
- item.disabled && "cursor-not-allowed",
22097
- isDanger && "text-error hover:bg-error/10"
22098
- ),
22099
- children: /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
22100
- 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" })),
22101
- /* @__PURE__ */ jsxRuntime.jsx(
22102
- Typography,
22103
- {
22104
- variant: "small",
22105
- className: cn("flex-1", isDanger && "text-red-600"),
22106
- children: item.label
22337
+ if (isDivider) {
22338
+ return /* @__PURE__ */ jsxRuntime.jsx(Divider, { className: "my-1" }, `divider-${index}`);
22339
+ }
22340
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { children: [
22341
+ /* @__PURE__ */ jsxRuntime.jsx(
22342
+ Box,
22343
+ {
22344
+ as: "button",
22345
+ onClick: () => handleItemClick({ ...item, id: itemId }, itemId),
22346
+ "aria-disabled": item.disabled || void 0,
22347
+ onMouseEnter: (e) => {
22348
+ if (hasSubMenu) {
22349
+ setActiveSubMenu(itemId);
22350
+ setActiveSubMenuRef(e.currentTarget);
22107
22351
  }
22352
+ },
22353
+ "data-testid": item.event ? `action-${item.event}` : void 0,
22354
+ className: cn(
22355
+ "w-full flex items-center justify-between gap-3 px-4 py-2 text-start",
22356
+ "text-sm transition-colors",
22357
+ "hover:bg-muted",
22358
+ "focus:outline-none focus:bg-muted",
22359
+ "disabled:opacity-50 disabled:cursor-not-allowed",
22360
+ item.disabled && "cursor-not-allowed",
22361
+ isDanger && "text-error hover:bg-error/10"
22108
22362
  ),
22109
- item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
22110
- hasSubMenu && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: direction === "rtl" ? "chevron-left" : "chevron-right", size: "sm", className: "flex-shrink-0" })
22111
- ] })
22112
- },
22113
- itemId
22114
- );
22115
- };
22116
- const renderMenuItems = (menuItems) => {
22117
- return menuItems.map((item, index) => {
22118
- const hasSubMenu = item.subMenu && item.subMenu.length > 0;
22119
- const isDivider = item.id === "divider" || item.label === "divider";
22120
- const itemId = item.id ?? `item-${item.label.toLowerCase().replace(/\s+/g, "-")}-${index}`;
22121
- if (isDivider) {
22122
- return /* @__PURE__ */ jsxRuntime.jsx(Divider, { className: "my-1" }, `divider-${index}`);
22123
- }
22124
- return /* @__PURE__ */ jsxRuntime.jsxs(Box, { children: [
22125
- renderMenuItem(item, !!hasSubMenu, index),
22126
- hasSubMenu && activeSubMenu === itemId && item.subMenu && /* @__PURE__ */ jsxRuntime.jsx(
22127
- Box,
22128
- {
22129
- className: cn(
22130
- "absolute top-0 z-50",
22131
- subMenuSideClass,
22132
- menuContainerStyles
22363
+ children: /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-3 flex-1 min-w-0", children: [
22364
+ 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" })),
22365
+ /* @__PURE__ */ jsxRuntime.jsx(
22366
+ Typography,
22367
+ {
22368
+ variant: "small",
22369
+ className: cn("flex-1", isDanger && "text-red-600"),
22370
+ children: item.label
22371
+ }
22133
22372
  ),
22134
- children: renderMenuItems(item.subMenu)
22135
- }
22136
- )
22137
- ] }, itemId);
22138
- });
22139
- };
22140
- return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "relative", children: [
22373
+ item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", children: item.badge }),
22374
+ hasSubMenu && /* @__PURE__ */ jsxRuntime.jsx(
22375
+ Icon,
22376
+ {
22377
+ name: direction === "rtl" ? "chevron-left" : "chevron-right",
22378
+ size: "sm",
22379
+ className: "flex-shrink-0"
22380
+ }
22381
+ )
22382
+ ] })
22383
+ }
22384
+ ),
22385
+ hasSubMenu && activeSubMenu === itemId && item.subMenu && /* @__PURE__ */ jsxRuntime.jsx(
22386
+ SubMenu,
22387
+ {
22388
+ items: item.subMenu,
22389
+ itemRef: activeSubMenuRef,
22390
+ direction,
22391
+ eventBus
22392
+ }
22393
+ )
22394
+ ] }, itemId);
22395
+ });
22396
+ const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxRuntime.jsxs(
22397
+ "div",
22398
+ {
22399
+ ref: menuRef,
22400
+ className: cn("fixed z-50", menuContainerStyles, className),
22401
+ style: computeMenuStyle(effectivePosition, triggerRect),
22402
+ role: "menu",
22403
+ children: [
22404
+ header && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-2 border-b border-border", children: header }),
22405
+ renderMenuItems(items),
22406
+ footer && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-2 border-t border-border", children: footer })
22407
+ ]
22408
+ }
22409
+ ) : null;
22410
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
22141
22411
  triggerElement,
22142
- isOpen && triggerRect && /* @__PURE__ */ jsxRuntime.jsx(
22143
- Box,
22144
- {
22145
- ref: menuRef,
22146
- className: cn(
22147
- "absolute z-50",
22148
- menuContainerStyles,
22149
- positionClasses[effectivePosition],
22150
- className
22151
- ),
22152
- style: {
22153
- left: effectivePosition.includes("left") ? 0 : "auto",
22154
- right: effectivePosition.includes("right") ? 0 : "auto"
22155
- },
22156
- role: "menu",
22157
- children: renderMenuItems(items)
22158
- }
22159
- )
22412
+ panel && typeof document !== "undefined" ? reactDom.createPortal(panel, document.body) : panel
22160
22413
  ] });
22161
22414
  };
22162
22415
  Menu.displayName = "Menu";
@@ -23910,7 +24163,7 @@ var init_FilterGroup = __esm({
23910
24163
  Select,
23911
24164
  {
23912
24165
  value: selectedValues[filter.field] || "all",
23913
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
24166
+ onChange: (v) => handleFilterSelect(filter.field, v),
23914
24167
  options: [
23915
24168
  { value: "all", label: t("filterGroup.all") },
23916
24169
  ...filter.options?.map((opt) => ({
@@ -23993,7 +24246,7 @@ var init_FilterGroup = __esm({
23993
24246
  Select,
23994
24247
  {
23995
24248
  value: selectedValues[filter.field] || "all",
23996
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
24249
+ onChange: (v) => handleFilterSelect(filter.field, v),
23997
24250
  options: [
23998
24251
  { value: "all", label: t("filterGroup.allOf", { label: filter.label }) },
23999
24252
  ...filter.options?.map((opt) => ({
@@ -24111,7 +24364,7 @@ var init_FilterGroup = __esm({
24111
24364
  Select,
24112
24365
  {
24113
24366
  value: selectedValues[filter.field] || "all",
24114
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
24367
+ onChange: (v) => handleFilterSelect(filter.field, v),
24115
24368
  options: [
24116
24369
  { value: "all", label: t("filterGroup.all") },
24117
24370
  ...filter.options?.map((opt) => ({
@@ -28293,13 +28546,13 @@ var init_MapView = __esm({
28293
28546
  shadowSize: [41, 41]
28294
28547
  });
28295
28548
  L.Marker.prototype.options.icon = defaultIcon;
28296
- const { useEffect: useEffect71, useRef: useRef66, useCallback: useCallback112, useState: useState99 } = React80__namespace.default;
28549
+ const { useEffect: useEffect72, useRef: useRef67, useCallback: useCallback112, useState: useState100 } = React80__namespace.default;
28297
28550
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
28298
28551
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
28299
28552
  function MapUpdater({ centerLat, centerLng, zoom }) {
28300
28553
  const map = useMap();
28301
- const prevRef = useRef66({ centerLat, centerLng, zoom });
28302
- useEffect71(() => {
28554
+ const prevRef = useRef67({ centerLat, centerLng, zoom });
28555
+ useEffect72(() => {
28303
28556
  const prev = prevRef.current;
28304
28557
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
28305
28558
  map.setView([centerLat, centerLng], zoom);
@@ -28310,7 +28563,7 @@ var init_MapView = __esm({
28310
28563
  }
28311
28564
  function MapClickHandler({ onMapClick }) {
28312
28565
  const map = useMap();
28313
- useEffect71(() => {
28566
+ useEffect72(() => {
28314
28567
  if (!onMapClick) return;
28315
28568
  const handler = (e) => {
28316
28569
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -28338,7 +28591,7 @@ var init_MapView = __esm({
28338
28591
  showAttribution = true
28339
28592
  }) {
28340
28593
  const eventBus = useEventBus2();
28341
- const [clickedPosition, setClickedPosition] = useState99(null);
28594
+ const [clickedPosition, setClickedPosition] = useState100(null);
28342
28595
  const handleMapClick = useCallback112((lat, lng) => {
28343
28596
  if (showClickedPin) {
28344
28597
  setClickedPosition({ lat, lng });
@@ -33774,8 +34027,8 @@ var init_VersionDiff = __esm({
33774
34027
  return { added, removed };
33775
34028
  }, [diff]);
33776
34029
  const handleBeforeChange = React80.useCallback(
33777
- (e) => {
33778
- const id = e.target.value;
34030
+ (v) => {
34031
+ const id = v;
33779
34032
  setInternalBefore(id);
33780
34033
  onSelectBefore?.(id);
33781
34034
  if (selectBeforeEvent) eventBus.emit(`UI:${selectBeforeEvent}`, { id });
@@ -33783,8 +34036,8 @@ var init_VersionDiff = __esm({
33783
34036
  [onSelectBefore, selectBeforeEvent, eventBus]
33784
34037
  );
33785
34038
  const handleAfterChange = React80.useCallback(
33786
- (e) => {
33787
- const id = e.target.value;
34039
+ (v) => {
34040
+ const id = v;
33788
34041
  setInternalAfter(id);
33789
34042
  onSelectAfter?.(id);
33790
34043
  if (selectAfterEvent) eventBus.emit(`UI:${selectAfterEvent}`, { id });
@@ -36562,7 +36815,6 @@ var init_DocumentViewer = __esm({
36562
36815
  showPrint = false,
36563
36816
  actions,
36564
36817
  documents,
36565
- entity,
36566
36818
  isLoading = false,
36567
36819
  error,
36568
36820
  className
@@ -38411,11 +38663,11 @@ function RuleEditor({
38411
38663
  className
38412
38664
  }) {
38413
38665
  const { t } = hooks.useTranslate();
38414
- const handleWhenChange = React80.useCallback((e) => {
38415
- onChange({ ...rule, whenEvent: e.target.value });
38666
+ const handleWhenChange = React80.useCallback((v) => {
38667
+ onChange({ ...rule, whenEvent: v });
38416
38668
  }, [rule, onChange]);
38417
- const handleThenChange = React80.useCallback((e) => {
38418
- onChange({ ...rule, thenAction: e.target.value });
38669
+ const handleThenChange = React80.useCallback((v) => {
38670
+ onChange({ ...rule, thenAction: v });
38419
38671
  }, [rule, onChange]);
38420
38672
  return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: cn("items-center p-2 rounded-lg bg-muted/50 border border-border", className), gap: "sm", children: [
38421
38673
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-primary font-bold whitespace-nowrap", children: t("eventHandler.when") }),
@@ -39491,7 +39743,7 @@ var init_Form = __esm({
39491
39743
  ...commonProps,
39492
39744
  options,
39493
39745
  value: String(currentValue),
39494
- onChange: (e) => handleChange(fieldName, e.target.value),
39746
+ onChange: (v) => handleChange(fieldName, v),
39495
39747
  placeholder: field.placeholder || `Select ${label}...`
39496
39748
  }
39497
39749
  );
@@ -43358,7 +43610,7 @@ function TraitSlot({
43358
43610
  size = "md",
43359
43611
  showTooltip = true,
43360
43612
  categoryColors,
43361
- tooltipFrameUrl,
43613
+ tooltipFrameUrl = "",
43362
43614
  className,
43363
43615
  feedback,
43364
43616
  onItemDrop,
@@ -46259,18 +46511,32 @@ var init_WorldMapTemplate = __esm({
46259
46511
  }
46260
46512
  });
46261
46513
  function lazyThree(name, loader) {
46262
- const Lazy = React80__namespace.default.lazy(() => loader().then((m) => ({ default: m[name] })));
46514
+ const Lazy = React80__namespace.default.lazy(
46515
+ () => loader().then((m) => {
46516
+ const Resolved = m[name];
46517
+ if (!Resolved) {
46518
+ throw new Error(
46519
+ `[@almadar/ui] 3D component "${name}" was not found in the three subpath bundle.`
46520
+ );
46521
+ }
46522
+ return { default: Resolved };
46523
+ })
46524
+ );
46263
46525
  function ThreeWrapper(props) {
46264
46526
  return React80__namespace.default.createElement(
46265
- React80__namespace.default.Suspense,
46266
- { fallback: null },
46267
- React80__namespace.default.createElement(Lazy, props)
46527
+ ThreeBoundary,
46528
+ { name },
46529
+ React80__namespace.default.createElement(
46530
+ React80__namespace.default.Suspense,
46531
+ { fallback: null },
46532
+ React80__namespace.default.createElement(Lazy, props)
46533
+ )
46268
46534
  );
46269
46535
  }
46270
46536
  ThreeWrapper.displayName = `Lazy(${name})`;
46271
46537
  return ThreeWrapper;
46272
46538
  }
46273
- var FeatureRenderer, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
46539
+ var ThreeBoundary, FeatureRenderer, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
46274
46540
  var init_component_registry_generated = __esm({
46275
46541
  "components/core/organisms/component-registry.generated.ts"() {
46276
46542
  init_AboutPageTemplate();
@@ -46556,6 +46822,28 @@ var init_component_registry_generated = __esm({
46556
46822
  init_WorldMapBoard();
46557
46823
  init_WorldMapTemplate();
46558
46824
  init_XPBar();
46825
+ ThreeBoundary = class extends React80__namespace.default.Component {
46826
+ constructor() {
46827
+ super(...arguments);
46828
+ __publicField(this, "state", { failed: false });
46829
+ }
46830
+ static getDerivedStateFromError() {
46831
+ return { failed: true };
46832
+ }
46833
+ render() {
46834
+ if (this.state.failed) {
46835
+ return React80__namespace.default.createElement(
46836
+ "div",
46837
+ {
46838
+ "data-testid": "three-unavailable",
46839
+ style: { padding: 16, fontSize: 13, lineHeight: 1.5, opacity: 0.7 }
46840
+ },
46841
+ `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.`
46842
+ );
46843
+ }
46844
+ return this.props.children;
46845
+ }
46846
+ };
46559
46847
  FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/molecules/game/three'));
46560
46848
  GameCanvas3D = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
46561
46849
  GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));