@almadar/ui 5.30.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.
@@ -3427,6 +3427,8 @@ var init_Input = __esm({
3427
3427
  className,
3428
3428
  inputType,
3429
3429
  type: htmlType,
3430
+ label,
3431
+ helperText,
3430
3432
  error,
3431
3433
  leftIcon,
3432
3434
  rightIcon,
@@ -3482,82 +3484,95 @@ var init_Input = __esm({
3482
3484
  onClear?.();
3483
3485
  }
3484
3486
  };
3487
+ const wrapField = (field) => /* @__PURE__ */ jsxs("div", { className: "w-full", children: [
3488
+ label && /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-foreground mb-1", children: label }),
3489
+ field,
3490
+ (helperText || error) && /* @__PURE__ */ jsx("p", { className: cn("mt-1 text-xs", error ? "text-error" : "text-muted-foreground"), children: error ?? helperText })
3491
+ ] });
3485
3492
  if (type === "select") {
3486
- return /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
3487
- resolvedLeftIcon && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
3488
- /* @__PURE__ */ jsxs(
3489
- "select",
3493
+ return wrapField(
3494
+ /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
3495
+ resolvedLeftIcon && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
3496
+ /* @__PURE__ */ jsxs(
3497
+ "select",
3498
+ {
3499
+ ref,
3500
+ value,
3501
+ onChange: handleChange,
3502
+ className: cn(baseClassName, "appearance-none pr-10", className),
3503
+ ...props,
3504
+ children: [
3505
+ /* @__PURE__ */ jsx("option", { value: "", children: t("form.selectPlaceholder", { label: "" }) }),
3506
+ options?.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
3507
+ ]
3508
+ }
3509
+ ),
3510
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none text-muted-foreground", children: /* @__PURE__ */ jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default" }) })
3511
+ ] })
3512
+ );
3513
+ }
3514
+ if (type === "textarea") {
3515
+ return wrapField(
3516
+ /* @__PURE__ */ jsx("div", { className: "relative w-full", children: /* @__PURE__ */ jsx(
3517
+ "textarea",
3490
3518
  {
3491
3519
  ref,
3492
3520
  value,
3493
3521
  onChange: handleChange,
3494
- className: cn(baseClassName, "appearance-none pr-10", className),
3495
- ...props,
3496
- children: [
3497
- /* @__PURE__ */ jsx("option", { value: "", children: t("form.selectPlaceholder", { label: "" }) }),
3498
- options?.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
3499
- ]
3522
+ rows: rows2,
3523
+ className: baseClassName,
3524
+ ...props
3500
3525
  }
3501
- ),
3502
- /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none text-muted-foreground", children: /* @__PURE__ */ jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default" }) })
3503
- ] });
3504
- }
3505
- if (type === "textarea") {
3506
- return /* @__PURE__ */ jsx("div", { className: "relative w-full", children: /* @__PURE__ */ jsx(
3507
- "textarea",
3508
- {
3509
- ref,
3510
- value,
3511
- onChange: handleChange,
3512
- rows: rows2,
3513
- className: baseClassName,
3514
- ...props
3515
- }
3516
- ) });
3526
+ ) })
3527
+ );
3517
3528
  }
3518
3529
  if (type === "checkbox") {
3519
- return /* @__PURE__ */ jsx(
3520
- "input",
3521
- {
3522
- ref,
3523
- type: "checkbox",
3524
- checked: props.checked,
3525
- onChange: handleChange,
3526
- className: cn(
3527
- "h-icon-default w-icon-default rounded-sm",
3528
- "border-border",
3529
- "text-primary focus:ring-ring",
3530
- "disabled:opacity-50 disabled:cursor-not-allowed",
3531
- className
3532
- ),
3533
- ...props
3534
- }
3530
+ return wrapField(
3531
+ /* @__PURE__ */ jsx(
3532
+ "input",
3533
+ {
3534
+ ref,
3535
+ type: "checkbox",
3536
+ checked: props.checked,
3537
+ onChange: handleChange,
3538
+ className: cn(
3539
+ "h-icon-default w-icon-default rounded-sm",
3540
+ "border-border",
3541
+ "text-primary focus:ring-ring",
3542
+ "disabled:opacity-50 disabled:cursor-not-allowed",
3543
+ className
3544
+ ),
3545
+ ...props
3546
+ }
3547
+ )
3535
3548
  );
3536
3549
  }
3537
- return /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
3538
- resolvedLeftIcon && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
3539
- /* @__PURE__ */ jsx(
3540
- "input",
3541
- {
3542
- ref,
3543
- type,
3544
- value,
3545
- onChange: handleChange,
3546
- className: baseClassName,
3547
- ...props
3548
- }
3549
- ),
3550
- showClearButton && /* @__PURE__ */ jsx(
3551
- "button",
3552
- {
3553
- type: "button",
3554
- onClick: handleClear,
3555
- className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground hover:text-foreground",
3556
- children: /* @__PURE__ */ jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
3557
- }
3558
- ),
3559
- rightIcon && !showClearButton && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground", children: resolveIconNode(rightIcon, iconCls) })
3560
- ] });
3550
+ return wrapField(
3551
+ /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
3552
+ resolvedLeftIcon && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
3553
+ /* @__PURE__ */ jsx(
3554
+ "input",
3555
+ {
3556
+ ref,
3557
+ type,
3558
+ value,
3559
+ onChange: handleChange,
3560
+ className: baseClassName,
3561
+ ...props
3562
+ }
3563
+ ),
3564
+ showClearButton && /* @__PURE__ */ jsx(
3565
+ "button",
3566
+ {
3567
+ type: "button",
3568
+ onClick: handleClear,
3569
+ className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground hover:text-foreground",
3570
+ children: /* @__PURE__ */ jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
3571
+ }
3572
+ ),
3573
+ rightIcon && !showClearButton && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground", children: resolveIconNode(rightIcon, iconCls) })
3574
+ ] })
3575
+ );
3561
3576
  }
3562
3577
  );
3563
3578
  Input.displayName = "Input";
@@ -3628,6 +3643,190 @@ var init_Textarea = __esm({
3628
3643
  Textarea.displayName = "Textarea";
3629
3644
  }
3630
3645
  });
3646
+ function flatOptions(opts, groups) {
3647
+ const flat = opts ?? [];
3648
+ const grp = (groups ?? []).flatMap((g) => g.options);
3649
+ return [...flat, ...grp];
3650
+ }
3651
+ function NativeSelect({
3652
+ className,
3653
+ options,
3654
+ groups,
3655
+ placeholder,
3656
+ error,
3657
+ onChange,
3658
+ value,
3659
+ ...props
3660
+ }) {
3661
+ const eventBus = useEventBus();
3662
+ const handleChange = (e) => {
3663
+ if (typeof onChange === "string") {
3664
+ eventBus.emit(`UI:${onChange}`, { value: e.target.value });
3665
+ } else {
3666
+ onChange?.(e.target.value);
3667
+ }
3668
+ };
3669
+ return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
3670
+ /* @__PURE__ */ jsxs(
3671
+ "select",
3672
+ {
3673
+ onChange: handleChange,
3674
+ value,
3675
+ className: cn(
3676
+ "block w-full border-[length:var(--border-width)] shadow-sm appearance-none",
3677
+ "px-3 py-2 pr-10 text-sm text-foreground font-medium",
3678
+ "bg-card",
3679
+ "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
3680
+ "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
3681
+ error ? "border-error focus:border-error" : "border-border focus:border-primary",
3682
+ className
3683
+ ),
3684
+ ...props,
3685
+ children: [
3686
+ placeholder && /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: placeholder }),
3687
+ options?.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)),
3688
+ groups?.map((group) => /* @__PURE__ */ jsx("optgroup", { label: group.label, children: group.options.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)) }, group.label))
3689
+ ]
3690
+ }
3691
+ ),
3692
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default text-foreground" }) })
3693
+ ] });
3694
+ }
3695
+ function RichSelect({
3696
+ className,
3697
+ options,
3698
+ groups,
3699
+ placeholder,
3700
+ error,
3701
+ onChange,
3702
+ value,
3703
+ multiple,
3704
+ searchable,
3705
+ clearable,
3706
+ disabled
3707
+ }) {
3708
+ const eventBus = useEventBus();
3709
+ const [open, setOpen] = useState(false);
3710
+ const [search, setSearch] = useState("");
3711
+ const containerRef = useRef(null);
3712
+ const selected = multiple ? Array.isArray(value) ? value : value ? [value] : [] : value ? [value] : [];
3713
+ const all = flatOptions(options, groups);
3714
+ const filtered = searchable && search ? all.filter((o) => o.label.toLowerCase().includes(search.toLowerCase())) : all;
3715
+ const toggle = (optValue) => {
3716
+ let next;
3717
+ if (multiple) {
3718
+ next = selected.includes(optValue) ? selected.filter((v) => v !== optValue) : [...selected, optValue];
3719
+ } else {
3720
+ next = optValue;
3721
+ setOpen(false);
3722
+ }
3723
+ if (typeof onChange === "string") {
3724
+ eventBus.emit(`UI:${onChange}`, { value: next });
3725
+ } else {
3726
+ onChange?.(next);
3727
+ }
3728
+ };
3729
+ const clear = (e) => {
3730
+ e.stopPropagation();
3731
+ const next = multiple ? [] : "";
3732
+ if (typeof onChange === "string") {
3733
+ eventBus.emit(`UI:${onChange}`, { value: next });
3734
+ } else {
3735
+ onChange?.(next);
3736
+ }
3737
+ };
3738
+ useEffect(() => {
3739
+ const handler = (e) => {
3740
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
3741
+ setOpen(false);
3742
+ setSearch("");
3743
+ }
3744
+ };
3745
+ document.addEventListener("mousedown", handler);
3746
+ return () => document.removeEventListener("mousedown", handler);
3747
+ }, []);
3748
+ const displayLabel = selected.length === 0 ? placeholder ?? "" : multiple ? `${selected.length} selected` : all.find((o) => o.value === selected[0])?.label ?? selected[0];
3749
+ const hasValue = selected.length > 0;
3750
+ const renderOptions = (opts) => opts.map((opt) => /* @__PURE__ */ jsxs(
3751
+ "button",
3752
+ {
3753
+ type: "button",
3754
+ disabled: opt.disabled,
3755
+ onClick: () => !opt.disabled && toggle(opt.value),
3756
+ className: cn(
3757
+ "w-full flex items-center justify-between px-3 py-1.5 text-sm text-start",
3758
+ "hover:bg-muted transition-colors",
3759
+ "disabled:opacity-50 disabled:cursor-not-allowed",
3760
+ selected.includes(opt.value) && "text-primary font-medium"
3761
+ ),
3762
+ children: [
3763
+ /* @__PURE__ */ jsx("span", { children: opt.label }),
3764
+ selected.includes(opt.value) && /* @__PURE__ */ jsx(Icon, { name: "check", className: "h-icon-default w-icon-default" })
3765
+ ]
3766
+ },
3767
+ opt.value
3768
+ ));
3769
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: cn("relative w-full", className), children: [
3770
+ /* @__PURE__ */ jsx(
3771
+ "button",
3772
+ {
3773
+ type: "button",
3774
+ disabled,
3775
+ onClick: () => !disabled && setOpen((o) => !o),
3776
+ className: cn(
3777
+ "block w-full border-[length:var(--border-width)] shadow-sm",
3778
+ "px-3 py-2 pr-10 text-sm text-start font-medium",
3779
+ "bg-card rounded-sm",
3780
+ "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
3781
+ "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
3782
+ error ? "border-error focus:border-error" : "border-border focus:border-primary",
3783
+ !hasValue && "text-muted-foreground"
3784
+ ),
3785
+ children: displayLabel
3786
+ }
3787
+ ),
3788
+ /* @__PURE__ */ jsxs("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center gap-1 pointer-events-none", children: [
3789
+ clearable && hasValue && /* @__PURE__ */ jsx(
3790
+ "button",
3791
+ {
3792
+ type: "button",
3793
+ onClick: clear,
3794
+ className: "pointer-events-auto text-muted-foreground hover:text-foreground",
3795
+ children: /* @__PURE__ */ jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
3796
+ }
3797
+ ),
3798
+ /* @__PURE__ */ jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default text-foreground" })
3799
+ ] }),
3800
+ open && /* @__PURE__ */ jsxs("div", { className: cn(
3801
+ "absolute z-50 mt-1 w-full",
3802
+ "bg-card border-[length:var(--border-width)] border-border",
3803
+ "rounded-sm shadow-elevation-popover py-1 max-h-60 overflow-y-auto"
3804
+ ), children: [
3805
+ searchable && /* @__PURE__ */ jsx("div", { className: "px-2 pb-1 border-b border-border", children: /* @__PURE__ */ jsx(
3806
+ "input",
3807
+ {
3808
+ autoFocus: true,
3809
+ type: "text",
3810
+ value: search,
3811
+ onChange: (e) => setSearch(e.target.value),
3812
+ placeholder: "Search\u2026",
3813
+ className: cn(
3814
+ "w-full px-2 py-1 text-sm bg-transparent",
3815
+ "focus:outline-none text-foreground placeholder:text-muted-foreground"
3816
+ )
3817
+ }
3818
+ ) }),
3819
+ groups && groups.length > 0 ? groups.map((g) => {
3820
+ const groupFiltered = searchable && search ? g.options.filter((o) => o.label.toLowerCase().includes(search.toLowerCase())) : g.options;
3821
+ if (groupFiltered.length === 0) return null;
3822
+ return /* @__PURE__ */ jsxs("div", { children: [
3823
+ /* @__PURE__ */ jsx("div", { className: "px-3 py-1 text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: g.label }),
3824
+ renderOptions(groupFiltered)
3825
+ ] }, g.label);
3826
+ }) : renderOptions(filtered)
3827
+ ] })
3828
+ ] });
3829
+ }
3631
3830
  var Select;
3632
3831
  var init_Select = __esm({
3633
3832
  "components/core/atoms/Select.tsx"() {
@@ -3635,47 +3834,12 @@ var init_Select = __esm({
3635
3834
  init_Icon();
3636
3835
  init_useEventBus();
3637
3836
  Select = React80__default.forwardRef(
3638
- ({ className, options, placeholder, error, onChange, ...props }, ref) => {
3639
- const eventBus = useEventBus();
3640
- const handleChange = (e) => {
3641
- if (typeof onChange === "string") {
3642
- eventBus.emit(`UI:${onChange}`, { value: e.target.value });
3643
- } else {
3644
- onChange?.(e);
3645
- }
3646
- };
3647
- return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
3648
- /* @__PURE__ */ jsxs(
3649
- "select",
3650
- {
3651
- ref,
3652
- onChange: handleChange,
3653
- className: cn(
3654
- "block w-full border-[length:var(--border-width)] shadow-sm appearance-none",
3655
- "px-3 py-2 pr-10 text-sm text-foreground font-medium",
3656
- "bg-card",
3657
- "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
3658
- "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
3659
- error ? "border-error focus:border-error" : "border-border focus:border-primary",
3660
- className
3661
- ),
3662
- ...props,
3663
- children: [
3664
- placeholder && /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: placeholder }),
3665
- options.map((option) => /* @__PURE__ */ jsx(
3666
- "option",
3667
- {
3668
- value: option.value,
3669
- disabled: option.disabled,
3670
- children: option.label
3671
- },
3672
- option.value
3673
- ))
3674
- ]
3675
- }
3676
- ),
3677
- /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default text-foreground" }) })
3678
- ] });
3837
+ (props, _ref) => {
3838
+ const { multiple, searchable, clearable } = props;
3839
+ if (multiple || searchable || clearable) {
3840
+ return /* @__PURE__ */ jsx(RichSelect, { ...props });
3841
+ }
3842
+ return /* @__PURE__ */ jsx(NativeSelect, { ...props });
3679
3843
  }
3680
3844
  );
3681
3845
  Select.displayName = "Select";
@@ -3729,11 +3893,54 @@ var init_Checkbox = __esm({
3729
3893
  Checkbox.displayName = "Checkbox";
3730
3894
  }
3731
3895
  });
3896
+ var sizeStyles3, Spinner;
3897
+ var init_Spinner = __esm({
3898
+ "components/core/atoms/Spinner.tsx"() {
3899
+ init_cn();
3900
+ init_Icon();
3901
+ sizeStyles3 = {
3902
+ xs: "h-3 w-3",
3903
+ sm: "h-icon-default w-icon-default",
3904
+ md: "h-6 w-6",
3905
+ lg: "h-8 w-8"
3906
+ };
3907
+ Spinner = React80__default.forwardRef(
3908
+ ({ className, size = "md", overlay, ...props }, ref) => {
3909
+ if (overlay) {
3910
+ return /* @__PURE__ */ jsx(
3911
+ "div",
3912
+ {
3913
+ ref,
3914
+ className: cn(
3915
+ "absolute inset-0 z-10 flex items-center justify-center",
3916
+ "bg-background/60 backdrop-blur-sm",
3917
+ className
3918
+ ),
3919
+ ...props,
3920
+ children: /* @__PURE__ */ jsx(Icon, { name: "loader", className: cn("animate-spin text-foreground", sizeStyles3[size]) })
3921
+ }
3922
+ );
3923
+ }
3924
+ return /* @__PURE__ */ jsx(
3925
+ "div",
3926
+ {
3927
+ ref,
3928
+ className: cn("text-foreground", className),
3929
+ ...props,
3930
+ children: /* @__PURE__ */ jsx(Icon, { name: "loader", className: cn("animate-spin", sizeStyles3[size]) })
3931
+ }
3932
+ );
3933
+ }
3934
+ );
3935
+ Spinner.displayName = "Spinner";
3936
+ }
3937
+ });
3732
3938
  var variantStyles4, paddingStyles2, shadowStyles2, lookStyles2, Card, CardHeader, CardTitle, CardContent, CardBody, CardFooter;
3733
3939
  var init_Card = __esm({
3734
3940
  "components/core/atoms/Card.tsx"() {
3735
3941
  init_cn();
3736
3942
  init_useEventBus();
3943
+ init_Spinner();
3737
3944
  variantStyles4 = {
3738
3945
  default: [
3739
3946
  "bg-card",
@@ -3798,6 +4005,7 @@ var init_Card = __esm({
3798
4005
  look = "elevated",
3799
4006
  children,
3800
4007
  action,
4008
+ loading,
3801
4009
  onClick,
3802
4010
  ...props
3803
4011
  }, ref) => {
@@ -3811,7 +4019,7 @@ var init_Card = __esm({
3811
4019
  {
3812
4020
  ref,
3813
4021
  className: cn(
3814
- "rounded-container",
4022
+ "rounded-container relative",
3815
4023
  "transition-all duration-[var(--transition-normal)]",
3816
4024
  variantStyles4[variant],
3817
4025
  paddingStyles2[padding],
@@ -3822,6 +4030,7 @@ var init_Card = __esm({
3822
4030
  onClick: handleClick,
3823
4031
  ...props,
3824
4032
  children: [
4033
+ loading && /* @__PURE__ */ jsx(Spinner, { overlay: true, size: "md" }),
3825
4034
  (title || subtitle) && /* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
3826
4035
  title && /* @__PURE__ */ jsx("h3", { className: "text-lg text-card-foreground font-bold", children: title }),
3827
4036
  subtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1", children: subtitle })
@@ -3863,7 +4072,7 @@ var init_Card = __esm({
3863
4072
  CardFooter.displayName = "CardFooter";
3864
4073
  }
3865
4074
  });
3866
- var variantStyles5, sizeStyles3, iconSizes, FilterPill;
4075
+ var variantStyles5, sizeStyles4, iconSizes, FilterPill;
3867
4076
  var init_FilterPill = __esm({
3868
4077
  "components/core/atoms/FilterPill.tsx"() {
3869
4078
  init_cn();
@@ -3897,7 +4106,7 @@ var init_FilterPill = __esm({
3897
4106
  "border-[length:var(--border-width-thin)] border-border"
3898
4107
  ].join(" ")
3899
4108
  };
3900
- sizeStyles3 = {
4109
+ sizeStyles4 = {
3901
4110
  sm: "px-2 py-0.5 text-xs",
3902
4111
  md: "px-2.5 py-1 text-sm",
3903
4112
  lg: "px-3 py-1.5 text-base"
@@ -3941,7 +4150,7 @@ var init_FilterPill = __esm({
3941
4150
  className: cn(
3942
4151
  "inline-flex items-center gap-1 font-bold rounded-pill",
3943
4152
  variantStyles5[variant],
3944
- sizeStyles3[size],
4153
+ sizeStyles4[size],
3945
4154
  (onClick || clickEvent) && "cursor-pointer",
3946
4155
  className
3947
4156
  ),
@@ -3973,33 +4182,6 @@ var init_FilterPill = __esm({
3973
4182
  FilterPill.displayName = "FilterPill";
3974
4183
  }
3975
4184
  });
3976
- var sizeStyles4, Spinner;
3977
- var init_Spinner = __esm({
3978
- "components/core/atoms/Spinner.tsx"() {
3979
- init_cn();
3980
- init_Icon();
3981
- sizeStyles4 = {
3982
- xs: "h-3 w-3",
3983
- sm: "h-icon-default w-icon-default",
3984
- md: "h-6 w-6",
3985
- lg: "h-8 w-8"
3986
- };
3987
- Spinner = React80__default.forwardRef(
3988
- ({ className, size = "md", ...props }, ref) => {
3989
- return /* @__PURE__ */ jsx(
3990
- "div",
3991
- {
3992
- ref,
3993
- className: cn("text-foreground", className),
3994
- ...props,
3995
- children: /* @__PURE__ */ jsx(Icon, { name: "loader", className: cn("animate-spin", sizeStyles4[size]) })
3996
- }
3997
- );
3998
- }
3999
- );
4000
- Spinner.displayName = "Spinner";
4001
- }
4002
- });
4003
4185
  function generateInitials(name) {
4004
4186
  const parts = name.trim().split(/\s+/);
4005
4187
  if (parts.length === 1) {
@@ -16466,30 +16648,30 @@ var init_BranchingLogicBuilder = __esm({
16466
16648
  if (!sourceQuestion?.optionValues) return [];
16467
16649
  return sourceQuestion.optionValues.map((v) => ({ value: v, label: v }));
16468
16650
  }, [sourceQuestion]);
16469
- const handleSource = (e) => {
16470
- onChange({ ...rule, sourceQuestionId: e.target.value });
16651
+ const handleSource = (v) => {
16652
+ onChange({ ...rule, sourceQuestionId: v });
16471
16653
  };
16472
- const handleOperator = (e) => {
16473
- const next = e.target.value;
16654
+ const handleOperator = (v) => {
16655
+ const next = v;
16474
16656
  const nextValue = next === "in" && !Array.isArray(rule.value) ? rule.value ? [rule.value] : [] : next !== "in" && Array.isArray(rule.value) ? rule.value[0] ?? "" : rule.value;
16475
16657
  onChange({ ...rule, operator: next, value: nextValue });
16476
16658
  };
16477
16659
  const handleScalarValue = (e) => {
16478
16660
  onChange({ ...rule, value: e.target.value });
16479
16661
  };
16480
- const handleAddChip = (e) => {
16481
- const v = e.target.value;
16482
- if (!v) return;
16662
+ const handleAddChip = (v) => {
16663
+ const val = v;
16664
+ if (!val) return;
16483
16665
  const current = Array.isArray(rule.value) ? rule.value : [];
16484
- if (current.includes(v)) return;
16485
- onChange({ ...rule, value: [...current, v] });
16666
+ if (current.includes(val)) return;
16667
+ onChange({ ...rule, value: [...current, val] });
16486
16668
  };
16487
16669
  const handleRemoveChip = (chip) => {
16488
16670
  const current = Array.isArray(rule.value) ? rule.value : [];
16489
16671
  onChange({ ...rule, value: current.filter((c) => c !== chip) });
16490
16672
  };
16491
- const handleTarget = (e) => {
16492
- onChange({ ...rule, targetQuestionId: e.target.value });
16673
+ const handleTarget = (v) => {
16674
+ onChange({ ...rule, targetQuestionId: v });
16493
16675
  };
16494
16676
  const isMulti = rule.operator === "in";
16495
16677
  const chips = Array.isArray(rule.value) ? rule.value : [];
@@ -16570,7 +16752,7 @@ var init_BranchingLogicBuilder = __esm({
16570
16752
  options: valueOptions,
16571
16753
  value: scalarValue,
16572
16754
  placeholder: t("branchingLogic.selectValue"),
16573
- onChange: (e) => onChange({ ...rule, value: e.target.value }),
16755
+ onChange: (v) => onChange({ ...rule, value: v }),
16574
16756
  disabled: readOnly
16575
16757
  }
16576
16758
  ) : /* @__PURE__ */ jsx(
@@ -19002,7 +19184,7 @@ var init_Pagination = __esm({
19002
19184
  Select,
19003
19185
  {
19004
19186
  value: String(pageSize),
19005
- onChange: (e) => handlePageSizeChange(Number(e.target.value)),
19187
+ onChange: (v) => handlePageSizeChange(Number(v)),
19006
19188
  options: pageSizeOptions.map((size) => ({
19007
19189
  value: String(size),
19008
19190
  label: String(size)
@@ -22028,7 +22210,9 @@ var init_Menu = __esm({
22028
22210
  trigger,
22029
22211
  items,
22030
22212
  position = "bottom-left",
22031
- className
22213
+ className,
22214
+ header,
22215
+ footer
22032
22216
  }) => {
22033
22217
  const eventBus = useEventBus();
22034
22218
  const { direction } = useTranslate();
@@ -22160,14 +22344,18 @@ var init_Menu = __esm({
22160
22344
  )
22161
22345
  ] }, itemId);
22162
22346
  });
22163
- const panel = isOpen && triggerRect ? /* @__PURE__ */ jsx(
22347
+ const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxs(
22164
22348
  "div",
22165
22349
  {
22166
22350
  ref: menuRef,
22167
22351
  className: cn("fixed z-50", menuContainerStyles, className),
22168
22352
  style: computeMenuStyle(effectivePosition, triggerRect),
22169
22353
  role: "menu",
22170
- children: renderMenuItems(items)
22354
+ children: [
22355
+ header && /* @__PURE__ */ jsx("div", { className: "px-4 py-2 border-b border-border", children: header }),
22356
+ renderMenuItems(items),
22357
+ footer && /* @__PURE__ */ jsx("div", { className: "px-4 py-2 border-t border-border", children: footer })
22358
+ ]
22171
22359
  }
22172
22360
  ) : null;
22173
22361
  return /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -23926,7 +24114,7 @@ var init_FilterGroup = __esm({
23926
24114
  Select,
23927
24115
  {
23928
24116
  value: selectedValues[filter.field] || "all",
23929
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
24117
+ onChange: (v) => handleFilterSelect(filter.field, v),
23930
24118
  options: [
23931
24119
  { value: "all", label: t("filterGroup.all") },
23932
24120
  ...filter.options?.map((opt) => ({
@@ -24009,7 +24197,7 @@ var init_FilterGroup = __esm({
24009
24197
  Select,
24010
24198
  {
24011
24199
  value: selectedValues[filter.field] || "all",
24012
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
24200
+ onChange: (v) => handleFilterSelect(filter.field, v),
24013
24201
  options: [
24014
24202
  { value: "all", label: t("filterGroup.allOf", { label: filter.label }) },
24015
24203
  ...filter.options?.map((opt) => ({
@@ -24127,7 +24315,7 @@ var init_FilterGroup = __esm({
24127
24315
  Select,
24128
24316
  {
24129
24317
  value: selectedValues[filter.field] || "all",
24130
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
24318
+ onChange: (v) => handleFilterSelect(filter.field, v),
24131
24319
  options: [
24132
24320
  { value: "all", label: t("filterGroup.all") },
24133
24321
  ...filter.options?.map((opt) => ({
@@ -28309,13 +28497,13 @@ var init_MapView = __esm({
28309
28497
  shadowSize: [41, 41]
28310
28498
  });
28311
28499
  L.Marker.prototype.options.icon = defaultIcon;
28312
- const { useEffect: useEffect71, useRef: useRef66, useCallback: useCallback112, useState: useState99 } = React80__default;
28500
+ const { useEffect: useEffect72, useRef: useRef67, useCallback: useCallback112, useState: useState100 } = React80__default;
28313
28501
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
28314
28502
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
28315
28503
  function MapUpdater({ centerLat, centerLng, zoom }) {
28316
28504
  const map = useMap();
28317
- const prevRef = useRef66({ centerLat, centerLng, zoom });
28318
- useEffect71(() => {
28505
+ const prevRef = useRef67({ centerLat, centerLng, zoom });
28506
+ useEffect72(() => {
28319
28507
  const prev = prevRef.current;
28320
28508
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
28321
28509
  map.setView([centerLat, centerLng], zoom);
@@ -28326,7 +28514,7 @@ var init_MapView = __esm({
28326
28514
  }
28327
28515
  function MapClickHandler({ onMapClick }) {
28328
28516
  const map = useMap();
28329
- useEffect71(() => {
28517
+ useEffect72(() => {
28330
28518
  if (!onMapClick) return;
28331
28519
  const handler = (e) => {
28332
28520
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -28354,7 +28542,7 @@ var init_MapView = __esm({
28354
28542
  showAttribution = true
28355
28543
  }) {
28356
28544
  const eventBus = useEventBus2();
28357
- const [clickedPosition, setClickedPosition] = useState99(null);
28545
+ const [clickedPosition, setClickedPosition] = useState100(null);
28358
28546
  const handleMapClick = useCallback112((lat, lng) => {
28359
28547
  if (showClickedPin) {
28360
28548
  setClickedPosition({ lat, lng });
@@ -33790,8 +33978,8 @@ var init_VersionDiff = __esm({
33790
33978
  return { added, removed };
33791
33979
  }, [diff]);
33792
33980
  const handleBeforeChange = useCallback(
33793
- (e) => {
33794
- const id = e.target.value;
33981
+ (v) => {
33982
+ const id = v;
33795
33983
  setInternalBefore(id);
33796
33984
  onSelectBefore?.(id);
33797
33985
  if (selectBeforeEvent) eventBus.emit(`UI:${selectBeforeEvent}`, { id });
@@ -33799,8 +33987,8 @@ var init_VersionDiff = __esm({
33799
33987
  [onSelectBefore, selectBeforeEvent, eventBus]
33800
33988
  );
33801
33989
  const handleAfterChange = useCallback(
33802
- (e) => {
33803
- const id = e.target.value;
33990
+ (v) => {
33991
+ const id = v;
33804
33992
  setInternalAfter(id);
33805
33993
  onSelectAfter?.(id);
33806
33994
  if (selectAfterEvent) eventBus.emit(`UI:${selectAfterEvent}`, { id });
@@ -38426,11 +38614,11 @@ function RuleEditor({
38426
38614
  className
38427
38615
  }) {
38428
38616
  const { t } = useTranslate();
38429
- const handleWhenChange = useCallback((e) => {
38430
- onChange({ ...rule, whenEvent: e.target.value });
38617
+ const handleWhenChange = useCallback((v) => {
38618
+ onChange({ ...rule, whenEvent: v });
38431
38619
  }, [rule, onChange]);
38432
- const handleThenChange = useCallback((e) => {
38433
- onChange({ ...rule, thenAction: e.target.value });
38620
+ const handleThenChange = useCallback((v) => {
38621
+ onChange({ ...rule, thenAction: v });
38434
38622
  }, [rule, onChange]);
38435
38623
  return /* @__PURE__ */ jsxs(HStack, { className: cn("items-center p-2 rounded-lg bg-muted/50 border border-border", className), gap: "sm", children: [
38436
38624
  /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-primary font-bold whitespace-nowrap", children: t("eventHandler.when") }),
@@ -39506,7 +39694,7 @@ var init_Form = __esm({
39506
39694
  ...commonProps,
39507
39695
  options,
39508
39696
  value: String(currentValue),
39509
- onChange: (e) => handleChange(fieldName, e.target.value),
39697
+ onChange: (v) => handleChange(fieldName, v),
39510
39698
  placeholder: field.placeholder || `Select ${label}...`
39511
39699
  }
39512
39700
  );
@@ -46274,18 +46462,32 @@ var init_WorldMapTemplate = __esm({
46274
46462
  }
46275
46463
  });
46276
46464
  function lazyThree(name, loader) {
46277
- const Lazy = React80__default.lazy(() => loader().then((m) => ({ default: m[name] })));
46465
+ const Lazy = React80__default.lazy(
46466
+ () => loader().then((m) => {
46467
+ const Resolved = m[name];
46468
+ if (!Resolved) {
46469
+ throw new Error(
46470
+ `[@almadar/ui] 3D component "${name}" was not found in the three subpath bundle.`
46471
+ );
46472
+ }
46473
+ return { default: Resolved };
46474
+ })
46475
+ );
46278
46476
  function ThreeWrapper(props) {
46279
46477
  return React80__default.createElement(
46280
- React80__default.Suspense,
46281
- { fallback: null },
46282
- React80__default.createElement(Lazy, props)
46478
+ ThreeBoundary,
46479
+ { name },
46480
+ React80__default.createElement(
46481
+ React80__default.Suspense,
46482
+ { fallback: null },
46483
+ React80__default.createElement(Lazy, props)
46484
+ )
46283
46485
  );
46284
46486
  }
46285
46487
  ThreeWrapper.displayName = `Lazy(${name})`;
46286
46488
  return ThreeWrapper;
46287
46489
  }
46288
- var FeatureRenderer, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
46490
+ var ThreeBoundary, FeatureRenderer, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
46289
46491
  var init_component_registry_generated = __esm({
46290
46492
  "components/core/organisms/component-registry.generated.ts"() {
46291
46493
  init_AboutPageTemplate();
@@ -46571,6 +46773,28 @@ var init_component_registry_generated = __esm({
46571
46773
  init_WorldMapBoard();
46572
46774
  init_WorldMapTemplate();
46573
46775
  init_XPBar();
46776
+ ThreeBoundary = class extends React80__default.Component {
46777
+ constructor() {
46778
+ super(...arguments);
46779
+ __publicField(this, "state", { failed: false });
46780
+ }
46781
+ static getDerivedStateFromError() {
46782
+ return { failed: true };
46783
+ }
46784
+ render() {
46785
+ if (this.state.failed) {
46786
+ return React80__default.createElement(
46787
+ "div",
46788
+ {
46789
+ "data-testid": "three-unavailable",
46790
+ style: { padding: 16, fontSize: 13, lineHeight: 1.5, opacity: 0.7 }
46791
+ },
46792
+ `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.`
46793
+ );
46794
+ }
46795
+ return this.props.children;
46796
+ }
46797
+ };
46574
46798
  FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/molecules/game/three'));
46575
46799
  GameCanvas3D = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
46576
46800
  GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));