@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.
@@ -3163,6 +3163,8 @@ var init_Input = __esm({
3163
3163
  className,
3164
3164
  inputType,
3165
3165
  type: htmlType,
3166
+ label,
3167
+ helperText,
3166
3168
  error,
3167
3169
  leftIcon,
3168
3170
  rightIcon,
@@ -3218,82 +3220,95 @@ var init_Input = __esm({
3218
3220
  onClear?.();
3219
3221
  }
3220
3222
  };
3223
+ const wrapField = (field) => /* @__PURE__ */ jsxs("div", { className: "w-full", children: [
3224
+ label && /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-foreground mb-1", children: label }),
3225
+ field,
3226
+ (helperText || error) && /* @__PURE__ */ jsx("p", { className: cn("mt-1 text-xs", error ? "text-error" : "text-muted-foreground"), children: error ?? helperText })
3227
+ ] });
3221
3228
  if (type === "select") {
3222
- return /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
3223
- resolvedLeftIcon && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
3224
- /* @__PURE__ */ jsxs(
3225
- "select",
3229
+ return wrapField(
3230
+ /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
3231
+ resolvedLeftIcon && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
3232
+ /* @__PURE__ */ jsxs(
3233
+ "select",
3234
+ {
3235
+ ref,
3236
+ value,
3237
+ onChange: handleChange,
3238
+ className: cn(baseClassName, "appearance-none pr-10", className),
3239
+ ...props,
3240
+ children: [
3241
+ /* @__PURE__ */ jsx("option", { value: "", children: t("form.selectPlaceholder", { label: "" }) }),
3242
+ options?.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
3243
+ ]
3244
+ }
3245
+ ),
3246
+ /* @__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" }) })
3247
+ ] })
3248
+ );
3249
+ }
3250
+ if (type === "textarea") {
3251
+ return wrapField(
3252
+ /* @__PURE__ */ jsx("div", { className: "relative w-full", children: /* @__PURE__ */ jsx(
3253
+ "textarea",
3226
3254
  {
3227
3255
  ref,
3228
3256
  value,
3229
3257
  onChange: handleChange,
3230
- className: cn(baseClassName, "appearance-none pr-10", className),
3231
- ...props,
3232
- children: [
3233
- /* @__PURE__ */ jsx("option", { value: "", children: t("form.selectPlaceholder", { label: "" }) }),
3234
- options?.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
3235
- ]
3258
+ rows: rows2,
3259
+ className: baseClassName,
3260
+ ...props
3236
3261
  }
3237
- ),
3238
- /* @__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" }) })
3239
- ] });
3240
- }
3241
- if (type === "textarea") {
3242
- return /* @__PURE__ */ jsx("div", { className: "relative w-full", children: /* @__PURE__ */ jsx(
3243
- "textarea",
3244
- {
3245
- ref,
3246
- value,
3247
- onChange: handleChange,
3248
- rows: rows2,
3249
- className: baseClassName,
3250
- ...props
3251
- }
3252
- ) });
3262
+ ) })
3263
+ );
3253
3264
  }
3254
3265
  if (type === "checkbox") {
3255
- return /* @__PURE__ */ jsx(
3256
- "input",
3257
- {
3258
- ref,
3259
- type: "checkbox",
3260
- checked: props.checked,
3261
- onChange: handleChange,
3262
- className: cn(
3263
- "h-icon-default w-icon-default rounded-sm",
3264
- "border-border",
3265
- "text-primary focus:ring-ring",
3266
- "disabled:opacity-50 disabled:cursor-not-allowed",
3267
- className
3268
- ),
3269
- ...props
3270
- }
3266
+ return wrapField(
3267
+ /* @__PURE__ */ jsx(
3268
+ "input",
3269
+ {
3270
+ ref,
3271
+ type: "checkbox",
3272
+ checked: props.checked,
3273
+ onChange: handleChange,
3274
+ className: cn(
3275
+ "h-icon-default w-icon-default rounded-sm",
3276
+ "border-border",
3277
+ "text-primary focus:ring-ring",
3278
+ "disabled:opacity-50 disabled:cursor-not-allowed",
3279
+ className
3280
+ ),
3281
+ ...props
3282
+ }
3283
+ )
3271
3284
  );
3272
3285
  }
3273
- return /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
3274
- resolvedLeftIcon && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
3275
- /* @__PURE__ */ jsx(
3276
- "input",
3277
- {
3278
- ref,
3279
- type,
3280
- value,
3281
- onChange: handleChange,
3282
- className: baseClassName,
3283
- ...props
3284
- }
3285
- ),
3286
- showClearButton && /* @__PURE__ */ jsx(
3287
- "button",
3288
- {
3289
- type: "button",
3290
- onClick: handleClear,
3291
- className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground hover:text-foreground",
3292
- children: /* @__PURE__ */ jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
3293
- }
3294
- ),
3295
- rightIcon && !showClearButton && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground", children: resolveIconNode(rightIcon, iconCls) })
3296
- ] });
3286
+ return wrapField(
3287
+ /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
3288
+ resolvedLeftIcon && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
3289
+ /* @__PURE__ */ jsx(
3290
+ "input",
3291
+ {
3292
+ ref,
3293
+ type,
3294
+ value,
3295
+ onChange: handleChange,
3296
+ className: baseClassName,
3297
+ ...props
3298
+ }
3299
+ ),
3300
+ showClearButton && /* @__PURE__ */ jsx(
3301
+ "button",
3302
+ {
3303
+ type: "button",
3304
+ onClick: handleClear,
3305
+ className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground hover:text-foreground",
3306
+ children: /* @__PURE__ */ jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
3307
+ }
3308
+ ),
3309
+ rightIcon && !showClearButton && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground", children: resolveIconNode(rightIcon, iconCls) })
3310
+ ] })
3311
+ );
3297
3312
  }
3298
3313
  );
3299
3314
  Input.displayName = "Input";
@@ -3364,6 +3379,190 @@ var init_Textarea = __esm({
3364
3379
  Textarea.displayName = "Textarea";
3365
3380
  }
3366
3381
  });
3382
+ function flatOptions(opts, groups) {
3383
+ const flat = opts ?? [];
3384
+ const grp = (groups ?? []).flatMap((g) => g.options);
3385
+ return [...flat, ...grp];
3386
+ }
3387
+ function NativeSelect({
3388
+ className,
3389
+ options,
3390
+ groups,
3391
+ placeholder,
3392
+ error,
3393
+ onChange,
3394
+ value,
3395
+ ...props
3396
+ }) {
3397
+ const eventBus = useEventBus();
3398
+ const handleChange = (e) => {
3399
+ if (typeof onChange === "string") {
3400
+ eventBus.emit(`UI:${onChange}`, { value: e.target.value });
3401
+ } else {
3402
+ onChange?.(e.target.value);
3403
+ }
3404
+ };
3405
+ return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
3406
+ /* @__PURE__ */ jsxs(
3407
+ "select",
3408
+ {
3409
+ onChange: handleChange,
3410
+ value,
3411
+ className: cn(
3412
+ "block w-full border-[length:var(--border-width)] shadow-sm appearance-none",
3413
+ "px-3 py-2 pr-10 text-sm text-foreground font-medium",
3414
+ "bg-card",
3415
+ "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
3416
+ "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
3417
+ error ? "border-error focus:border-error" : "border-border focus:border-primary",
3418
+ className
3419
+ ),
3420
+ ...props,
3421
+ children: [
3422
+ placeholder && /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: placeholder }),
3423
+ options?.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)),
3424
+ 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))
3425
+ ]
3426
+ }
3427
+ ),
3428
+ /* @__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" }) })
3429
+ ] });
3430
+ }
3431
+ function RichSelect({
3432
+ className,
3433
+ options,
3434
+ groups,
3435
+ placeholder,
3436
+ error,
3437
+ onChange,
3438
+ value,
3439
+ multiple,
3440
+ searchable,
3441
+ clearable,
3442
+ disabled
3443
+ }) {
3444
+ const eventBus = useEventBus();
3445
+ const [open, setOpen] = useState(false);
3446
+ const [search, setSearch] = useState("");
3447
+ const containerRef = useRef(null);
3448
+ const selected = multiple ? Array.isArray(value) ? value : value ? [value] : [] : value ? [value] : [];
3449
+ const all = flatOptions(options, groups);
3450
+ const filtered = searchable && search ? all.filter((o) => o.label.toLowerCase().includes(search.toLowerCase())) : all;
3451
+ const toggle = (optValue) => {
3452
+ let next;
3453
+ if (multiple) {
3454
+ next = selected.includes(optValue) ? selected.filter((v) => v !== optValue) : [...selected, optValue];
3455
+ } else {
3456
+ next = optValue;
3457
+ setOpen(false);
3458
+ }
3459
+ if (typeof onChange === "string") {
3460
+ eventBus.emit(`UI:${onChange}`, { value: next });
3461
+ } else {
3462
+ onChange?.(next);
3463
+ }
3464
+ };
3465
+ const clear = (e) => {
3466
+ e.stopPropagation();
3467
+ const next = multiple ? [] : "";
3468
+ if (typeof onChange === "string") {
3469
+ eventBus.emit(`UI:${onChange}`, { value: next });
3470
+ } else {
3471
+ onChange?.(next);
3472
+ }
3473
+ };
3474
+ useEffect(() => {
3475
+ const handler = (e) => {
3476
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
3477
+ setOpen(false);
3478
+ setSearch("");
3479
+ }
3480
+ };
3481
+ document.addEventListener("mousedown", handler);
3482
+ return () => document.removeEventListener("mousedown", handler);
3483
+ }, []);
3484
+ const displayLabel = selected.length === 0 ? placeholder ?? "" : multiple ? `${selected.length} selected` : all.find((o) => o.value === selected[0])?.label ?? selected[0];
3485
+ const hasValue = selected.length > 0;
3486
+ const renderOptions = (opts) => opts.map((opt) => /* @__PURE__ */ jsxs(
3487
+ "button",
3488
+ {
3489
+ type: "button",
3490
+ disabled: opt.disabled,
3491
+ onClick: () => !opt.disabled && toggle(opt.value),
3492
+ className: cn(
3493
+ "w-full flex items-center justify-between px-3 py-1.5 text-sm text-start",
3494
+ "hover:bg-muted transition-colors",
3495
+ "disabled:opacity-50 disabled:cursor-not-allowed",
3496
+ selected.includes(opt.value) && "text-primary font-medium"
3497
+ ),
3498
+ children: [
3499
+ /* @__PURE__ */ jsx("span", { children: opt.label }),
3500
+ selected.includes(opt.value) && /* @__PURE__ */ jsx(Icon, { name: "check", className: "h-icon-default w-icon-default" })
3501
+ ]
3502
+ },
3503
+ opt.value
3504
+ ));
3505
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: cn("relative w-full", className), children: [
3506
+ /* @__PURE__ */ jsx(
3507
+ "button",
3508
+ {
3509
+ type: "button",
3510
+ disabled,
3511
+ onClick: () => !disabled && setOpen((o) => !o),
3512
+ className: cn(
3513
+ "block w-full border-[length:var(--border-width)] shadow-sm",
3514
+ "px-3 py-2 pr-10 text-sm text-start font-medium",
3515
+ "bg-card rounded-sm",
3516
+ "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
3517
+ "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
3518
+ error ? "border-error focus:border-error" : "border-border focus:border-primary",
3519
+ !hasValue && "text-muted-foreground"
3520
+ ),
3521
+ children: displayLabel
3522
+ }
3523
+ ),
3524
+ /* @__PURE__ */ jsxs("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center gap-1 pointer-events-none", children: [
3525
+ clearable && hasValue && /* @__PURE__ */ jsx(
3526
+ "button",
3527
+ {
3528
+ type: "button",
3529
+ onClick: clear,
3530
+ className: "pointer-events-auto text-muted-foreground hover:text-foreground",
3531
+ children: /* @__PURE__ */ jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
3532
+ }
3533
+ ),
3534
+ /* @__PURE__ */ jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default text-foreground" })
3535
+ ] }),
3536
+ open && /* @__PURE__ */ jsxs("div", { className: cn(
3537
+ "absolute z-50 mt-1 w-full",
3538
+ "bg-card border-[length:var(--border-width)] border-border",
3539
+ "rounded-sm shadow-elevation-popover py-1 max-h-60 overflow-y-auto"
3540
+ ), children: [
3541
+ searchable && /* @__PURE__ */ jsx("div", { className: "px-2 pb-1 border-b border-border", children: /* @__PURE__ */ jsx(
3542
+ "input",
3543
+ {
3544
+ autoFocus: true,
3545
+ type: "text",
3546
+ value: search,
3547
+ onChange: (e) => setSearch(e.target.value),
3548
+ placeholder: "Search\u2026",
3549
+ className: cn(
3550
+ "w-full px-2 py-1 text-sm bg-transparent",
3551
+ "focus:outline-none text-foreground placeholder:text-muted-foreground"
3552
+ )
3553
+ }
3554
+ ) }),
3555
+ groups && groups.length > 0 ? groups.map((g) => {
3556
+ const groupFiltered = searchable && search ? g.options.filter((o) => o.label.toLowerCase().includes(search.toLowerCase())) : g.options;
3557
+ if (groupFiltered.length === 0) return null;
3558
+ return /* @__PURE__ */ jsxs("div", { children: [
3559
+ /* @__PURE__ */ jsx("div", { className: "px-3 py-1 text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: g.label }),
3560
+ renderOptions(groupFiltered)
3561
+ ] }, g.label);
3562
+ }) : renderOptions(filtered)
3563
+ ] })
3564
+ ] });
3565
+ }
3367
3566
  var Select;
3368
3567
  var init_Select = __esm({
3369
3568
  "components/core/atoms/Select.tsx"() {
@@ -3371,47 +3570,12 @@ var init_Select = __esm({
3371
3570
  init_Icon();
3372
3571
  init_useEventBus();
3373
3572
  Select = React79__default.forwardRef(
3374
- ({ className, options, placeholder, error, onChange, ...props }, ref) => {
3375
- const eventBus = useEventBus();
3376
- const handleChange = (e) => {
3377
- if (typeof onChange === "string") {
3378
- eventBus.emit(`UI:${onChange}`, { value: e.target.value });
3379
- } else {
3380
- onChange?.(e);
3381
- }
3382
- };
3383
- return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
3384
- /* @__PURE__ */ jsxs(
3385
- "select",
3386
- {
3387
- ref,
3388
- onChange: handleChange,
3389
- className: cn(
3390
- "block w-full border-[length:var(--border-width)] shadow-sm appearance-none",
3391
- "px-3 py-2 pr-10 text-sm text-foreground font-medium",
3392
- "bg-card",
3393
- "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
3394
- "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
3395
- error ? "border-error focus:border-error" : "border-border focus:border-primary",
3396
- className
3397
- ),
3398
- ...props,
3399
- children: [
3400
- placeholder && /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: placeholder }),
3401
- options.map((option) => /* @__PURE__ */ jsx(
3402
- "option",
3403
- {
3404
- value: option.value,
3405
- disabled: option.disabled,
3406
- children: option.label
3407
- },
3408
- option.value
3409
- ))
3410
- ]
3411
- }
3412
- ),
3413
- /* @__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" }) })
3414
- ] });
3573
+ (props, _ref) => {
3574
+ const { multiple, searchable, clearable } = props;
3575
+ if (multiple || searchable || clearable) {
3576
+ return /* @__PURE__ */ jsx(RichSelect, { ...props });
3577
+ }
3578
+ return /* @__PURE__ */ jsx(NativeSelect, { ...props });
3415
3579
  }
3416
3580
  );
3417
3581
  Select.displayName = "Select";
@@ -3465,11 +3629,54 @@ var init_Checkbox = __esm({
3465
3629
  Checkbox.displayName = "Checkbox";
3466
3630
  }
3467
3631
  });
3632
+ var sizeStyles3, Spinner;
3633
+ var init_Spinner = __esm({
3634
+ "components/core/atoms/Spinner.tsx"() {
3635
+ init_cn();
3636
+ init_Icon();
3637
+ sizeStyles3 = {
3638
+ xs: "h-3 w-3",
3639
+ sm: "h-icon-default w-icon-default",
3640
+ md: "h-6 w-6",
3641
+ lg: "h-8 w-8"
3642
+ };
3643
+ Spinner = React79__default.forwardRef(
3644
+ ({ className, size = "md", overlay, ...props }, ref) => {
3645
+ if (overlay) {
3646
+ return /* @__PURE__ */ jsx(
3647
+ "div",
3648
+ {
3649
+ ref,
3650
+ className: cn(
3651
+ "absolute inset-0 z-10 flex items-center justify-center",
3652
+ "bg-background/60 backdrop-blur-sm",
3653
+ className
3654
+ ),
3655
+ ...props,
3656
+ children: /* @__PURE__ */ jsx(Icon, { name: "loader", className: cn("animate-spin text-foreground", sizeStyles3[size]) })
3657
+ }
3658
+ );
3659
+ }
3660
+ return /* @__PURE__ */ jsx(
3661
+ "div",
3662
+ {
3663
+ ref,
3664
+ className: cn("text-foreground", className),
3665
+ ...props,
3666
+ children: /* @__PURE__ */ jsx(Icon, { name: "loader", className: cn("animate-spin", sizeStyles3[size]) })
3667
+ }
3668
+ );
3669
+ }
3670
+ );
3671
+ Spinner.displayName = "Spinner";
3672
+ }
3673
+ });
3468
3674
  var variantStyles4, paddingStyles2, shadowStyles2, lookStyles2, Card, CardHeader, CardTitle, CardContent, CardBody, CardFooter;
3469
3675
  var init_Card = __esm({
3470
3676
  "components/core/atoms/Card.tsx"() {
3471
3677
  init_cn();
3472
3678
  init_useEventBus();
3679
+ init_Spinner();
3473
3680
  variantStyles4 = {
3474
3681
  default: [
3475
3682
  "bg-card",
@@ -3534,6 +3741,7 @@ var init_Card = __esm({
3534
3741
  look = "elevated",
3535
3742
  children,
3536
3743
  action,
3744
+ loading,
3537
3745
  onClick,
3538
3746
  ...props
3539
3747
  }, ref) => {
@@ -3547,7 +3755,7 @@ var init_Card = __esm({
3547
3755
  {
3548
3756
  ref,
3549
3757
  className: cn(
3550
- "rounded-container",
3758
+ "rounded-container relative",
3551
3759
  "transition-all duration-[var(--transition-normal)]",
3552
3760
  variantStyles4[variant],
3553
3761
  paddingStyles2[padding],
@@ -3558,6 +3766,7 @@ var init_Card = __esm({
3558
3766
  onClick: handleClick,
3559
3767
  ...props,
3560
3768
  children: [
3769
+ loading && /* @__PURE__ */ jsx(Spinner, { overlay: true, size: "md" }),
3561
3770
  (title || subtitle) && /* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
3562
3771
  title && /* @__PURE__ */ jsx("h3", { className: "text-lg text-card-foreground font-bold", children: title }),
3563
3772
  subtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1", children: subtitle })
@@ -3599,7 +3808,7 @@ var init_Card = __esm({
3599
3808
  CardFooter.displayName = "CardFooter";
3600
3809
  }
3601
3810
  });
3602
- var variantStyles5, sizeStyles3, iconSizes, FilterPill;
3811
+ var variantStyles5, sizeStyles4, iconSizes, FilterPill;
3603
3812
  var init_FilterPill = __esm({
3604
3813
  "components/core/atoms/FilterPill.tsx"() {
3605
3814
  init_cn();
@@ -3633,7 +3842,7 @@ var init_FilterPill = __esm({
3633
3842
  "border-[length:var(--border-width-thin)] border-border"
3634
3843
  ].join(" ")
3635
3844
  };
3636
- sizeStyles3 = {
3845
+ sizeStyles4 = {
3637
3846
  sm: "px-2 py-0.5 text-xs",
3638
3847
  md: "px-2.5 py-1 text-sm",
3639
3848
  lg: "px-3 py-1.5 text-base"
@@ -3677,7 +3886,7 @@ var init_FilterPill = __esm({
3677
3886
  className: cn(
3678
3887
  "inline-flex items-center gap-1 font-bold rounded-pill",
3679
3888
  variantStyles5[variant],
3680
- sizeStyles3[size],
3889
+ sizeStyles4[size],
3681
3890
  (onClick || clickEvent) && "cursor-pointer",
3682
3891
  className
3683
3892
  ),
@@ -3709,33 +3918,6 @@ var init_FilterPill = __esm({
3709
3918
  FilterPill.displayName = "FilterPill";
3710
3919
  }
3711
3920
  });
3712
- var sizeStyles4, Spinner;
3713
- var init_Spinner = __esm({
3714
- "components/core/atoms/Spinner.tsx"() {
3715
- init_cn();
3716
- init_Icon();
3717
- sizeStyles4 = {
3718
- xs: "h-3 w-3",
3719
- sm: "h-icon-default w-icon-default",
3720
- md: "h-6 w-6",
3721
- lg: "h-8 w-8"
3722
- };
3723
- Spinner = React79__default.forwardRef(
3724
- ({ className, size = "md", ...props }, ref) => {
3725
- return /* @__PURE__ */ jsx(
3726
- "div",
3727
- {
3728
- ref,
3729
- className: cn("text-foreground", className),
3730
- ...props,
3731
- children: /* @__PURE__ */ jsx(Icon, { name: "loader", className: cn("animate-spin", sizeStyles4[size]) })
3732
- }
3733
- );
3734
- }
3735
- );
3736
- Spinner.displayName = "Spinner";
3737
- }
3738
- });
3739
3921
  function generateInitials(name) {
3740
3922
  const parts = name.trim().split(/\s+/);
3741
3923
  if (parts.length === 1) {
@@ -16033,30 +16215,30 @@ var init_BranchingLogicBuilder = __esm({
16033
16215
  if (!sourceQuestion?.optionValues) return [];
16034
16216
  return sourceQuestion.optionValues.map((v) => ({ value: v, label: v }));
16035
16217
  }, [sourceQuestion]);
16036
- const handleSource = (e) => {
16037
- onChange({ ...rule, sourceQuestionId: e.target.value });
16218
+ const handleSource = (v) => {
16219
+ onChange({ ...rule, sourceQuestionId: v });
16038
16220
  };
16039
- const handleOperator = (e) => {
16040
- const next = e.target.value;
16221
+ const handleOperator = (v) => {
16222
+ const next = v;
16041
16223
  const nextValue = next === "in" && !Array.isArray(rule.value) ? rule.value ? [rule.value] : [] : next !== "in" && Array.isArray(rule.value) ? rule.value[0] ?? "" : rule.value;
16042
16224
  onChange({ ...rule, operator: next, value: nextValue });
16043
16225
  };
16044
16226
  const handleScalarValue = (e) => {
16045
16227
  onChange({ ...rule, value: e.target.value });
16046
16228
  };
16047
- const handleAddChip = (e) => {
16048
- const v = e.target.value;
16049
- if (!v) return;
16229
+ const handleAddChip = (v) => {
16230
+ const val = v;
16231
+ if (!val) return;
16050
16232
  const current = Array.isArray(rule.value) ? rule.value : [];
16051
- if (current.includes(v)) return;
16052
- onChange({ ...rule, value: [...current, v] });
16233
+ if (current.includes(val)) return;
16234
+ onChange({ ...rule, value: [...current, val] });
16053
16235
  };
16054
16236
  const handleRemoveChip = (chip) => {
16055
16237
  const current = Array.isArray(rule.value) ? rule.value : [];
16056
16238
  onChange({ ...rule, value: current.filter((c) => c !== chip) });
16057
16239
  };
16058
- const handleTarget = (e) => {
16059
- onChange({ ...rule, targetQuestionId: e.target.value });
16240
+ const handleTarget = (v) => {
16241
+ onChange({ ...rule, targetQuestionId: v });
16060
16242
  };
16061
16243
  const isMulti = rule.operator === "in";
16062
16244
  const chips = Array.isArray(rule.value) ? rule.value : [];
@@ -16137,7 +16319,7 @@ var init_BranchingLogicBuilder = __esm({
16137
16319
  options: valueOptions,
16138
16320
  value: scalarValue,
16139
16321
  placeholder: t("branchingLogic.selectValue"),
16140
- onChange: (e) => onChange({ ...rule, value: e.target.value }),
16322
+ onChange: (v) => onChange({ ...rule, value: v }),
16141
16323
  disabled: readOnly
16142
16324
  }
16143
16325
  ) : /* @__PURE__ */ jsx(
@@ -18569,7 +18751,7 @@ var init_Pagination = __esm({
18569
18751
  Select,
18570
18752
  {
18571
18753
  value: String(pageSize),
18572
- onChange: (e) => handlePageSizeChange(Number(e.target.value)),
18754
+ onChange: (v) => handlePageSizeChange(Number(v)),
18573
18755
  options: pageSizeOptions.map((size) => ({
18574
18756
  value: String(size),
18575
18757
  label: String(size)
@@ -21595,7 +21777,9 @@ var init_Menu = __esm({
21595
21777
  trigger,
21596
21778
  items,
21597
21779
  position = "bottom-left",
21598
- className
21780
+ className,
21781
+ header,
21782
+ footer
21599
21783
  }) => {
21600
21784
  const eventBus = useEventBus();
21601
21785
  const { direction } = useTranslate();
@@ -21727,14 +21911,18 @@ var init_Menu = __esm({
21727
21911
  )
21728
21912
  ] }, itemId);
21729
21913
  });
21730
- const panel = isOpen && triggerRect ? /* @__PURE__ */ jsx(
21914
+ const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxs(
21731
21915
  "div",
21732
21916
  {
21733
21917
  ref: menuRef,
21734
21918
  className: cn("fixed z-50", menuContainerStyles, className),
21735
21919
  style: computeMenuStyle(effectivePosition, triggerRect),
21736
21920
  role: "menu",
21737
- children: renderMenuItems(items)
21921
+ children: [
21922
+ header && /* @__PURE__ */ jsx("div", { className: "px-4 py-2 border-b border-border", children: header }),
21923
+ renderMenuItems(items),
21924
+ footer && /* @__PURE__ */ jsx("div", { className: "px-4 py-2 border-t border-border", children: footer })
21925
+ ]
21738
21926
  }
21739
21927
  ) : null;
21740
21928
  return /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -23493,7 +23681,7 @@ var init_FilterGroup = __esm({
23493
23681
  Select,
23494
23682
  {
23495
23683
  value: selectedValues[filter.field] || "all",
23496
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
23684
+ onChange: (v) => handleFilterSelect(filter.field, v),
23497
23685
  options: [
23498
23686
  { value: "all", label: t("filterGroup.all") },
23499
23687
  ...filter.options?.map((opt) => ({
@@ -23576,7 +23764,7 @@ var init_FilterGroup = __esm({
23576
23764
  Select,
23577
23765
  {
23578
23766
  value: selectedValues[filter.field] || "all",
23579
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
23767
+ onChange: (v) => handleFilterSelect(filter.field, v),
23580
23768
  options: [
23581
23769
  { value: "all", label: t("filterGroup.allOf", { label: filter.label }) },
23582
23770
  ...filter.options?.map((opt) => ({
@@ -23694,7 +23882,7 @@ var init_FilterGroup = __esm({
23694
23882
  Select,
23695
23883
  {
23696
23884
  value: selectedValues[filter.field] || "all",
23697
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
23885
+ onChange: (v) => handleFilterSelect(filter.field, v),
23698
23886
  options: [
23699
23887
  { value: "all", label: t("filterGroup.all") },
23700
23888
  ...filter.options?.map((opt) => ({
@@ -27876,13 +28064,13 @@ var init_MapView = __esm({
27876
28064
  shadowSize: [41, 41]
27877
28065
  });
27878
28066
  L.Marker.prototype.options.icon = defaultIcon;
27879
- const { useEffect: useEffect72, useRef: useRef66, useCallback: useCallback112, useState: useState102 } = React79__default;
28067
+ const { useEffect: useEffect73, useRef: useRef67, useCallback: useCallback112, useState: useState103 } = React79__default;
27880
28068
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
27881
28069
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
27882
28070
  function MapUpdater({ centerLat, centerLng, zoom }) {
27883
28071
  const map = useMap();
27884
- const prevRef = useRef66({ centerLat, centerLng, zoom });
27885
- useEffect72(() => {
28072
+ const prevRef = useRef67({ centerLat, centerLng, zoom });
28073
+ useEffect73(() => {
27886
28074
  const prev = prevRef.current;
27887
28075
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
27888
28076
  map.setView([centerLat, centerLng], zoom);
@@ -27893,7 +28081,7 @@ var init_MapView = __esm({
27893
28081
  }
27894
28082
  function MapClickHandler({ onMapClick }) {
27895
28083
  const map = useMap();
27896
- useEffect72(() => {
28084
+ useEffect73(() => {
27897
28085
  if (!onMapClick) return;
27898
28086
  const handler = (e) => {
27899
28087
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -27921,7 +28109,7 @@ var init_MapView = __esm({
27921
28109
  showAttribution = true
27922
28110
  }) {
27923
28111
  const eventBus = useEventBus3();
27924
- const [clickedPosition, setClickedPosition] = useState102(null);
28112
+ const [clickedPosition, setClickedPosition] = useState103(null);
27925
28113
  const handleMapClick = useCallback112((lat, lng) => {
27926
28114
  if (showClickedPin) {
27927
28115
  setClickedPosition({ lat, lng });
@@ -33357,8 +33545,8 @@ var init_VersionDiff = __esm({
33357
33545
  return { added, removed };
33358
33546
  }, [diff]);
33359
33547
  const handleBeforeChange = useCallback(
33360
- (e) => {
33361
- const id = e.target.value;
33548
+ (v) => {
33549
+ const id = v;
33362
33550
  setInternalBefore(id);
33363
33551
  onSelectBefore?.(id);
33364
33552
  if (selectBeforeEvent) eventBus.emit(`UI:${selectBeforeEvent}`, { id });
@@ -33366,8 +33554,8 @@ var init_VersionDiff = __esm({
33366
33554
  [onSelectBefore, selectBeforeEvent, eventBus]
33367
33555
  );
33368
33556
  const handleAfterChange = useCallback(
33369
- (e) => {
33370
- const id = e.target.value;
33557
+ (v) => {
33558
+ const id = v;
33371
33559
  setInternalAfter(id);
33372
33560
  onSelectAfter?.(id);
33373
33561
  if (selectAfterEvent) eventBus.emit(`UI:${selectAfterEvent}`, { id });
@@ -37993,11 +38181,11 @@ function RuleEditor({
37993
38181
  className
37994
38182
  }) {
37995
38183
  const { t } = useTranslate();
37996
- const handleWhenChange = useCallback((e) => {
37997
- onChange({ ...rule, whenEvent: e.target.value });
38184
+ const handleWhenChange = useCallback((v) => {
38185
+ onChange({ ...rule, whenEvent: v });
37998
38186
  }, [rule, onChange]);
37999
- const handleThenChange = useCallback((e) => {
38000
- onChange({ ...rule, thenAction: e.target.value });
38187
+ const handleThenChange = useCallback((v) => {
38188
+ onChange({ ...rule, thenAction: v });
38001
38189
  }, [rule, onChange]);
38002
38190
  return /* @__PURE__ */ jsxs(HStack, { className: cn("items-center p-2 rounded-lg bg-muted/50 border border-border", className), gap: "sm", children: [
38003
38191
  /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-primary font-bold whitespace-nowrap", children: t("eventHandler.when") }),
@@ -39073,7 +39261,7 @@ var init_Form = __esm({
39073
39261
  ...commonProps,
39074
39262
  options,
39075
39263
  value: String(currentValue),
39076
- onChange: (e) => handleChange(fieldName, e.target.value),
39264
+ onChange: (v) => handleChange(fieldName, v),
39077
39265
  placeholder: field.placeholder || `Select ${label}...`
39078
39266
  }
39079
39267
  );
@@ -45860,18 +46048,32 @@ var init_WorldMapTemplate = __esm({
45860
46048
  }
45861
46049
  });
45862
46050
  function lazyThree(name, loader) {
45863
- const Lazy = React79__default.lazy(() => loader().then((m) => ({ default: m[name] })));
46051
+ const Lazy = React79__default.lazy(
46052
+ () => loader().then((m) => {
46053
+ const Resolved = m[name];
46054
+ if (!Resolved) {
46055
+ throw new Error(
46056
+ `[@almadar/ui] 3D component "${name}" was not found in the three subpath bundle.`
46057
+ );
46058
+ }
46059
+ return { default: Resolved };
46060
+ })
46061
+ );
45864
46062
  function ThreeWrapper(props) {
45865
46063
  return React79__default.createElement(
45866
- React79__default.Suspense,
45867
- { fallback: null },
45868
- React79__default.createElement(Lazy, props)
46064
+ ThreeBoundary,
46065
+ { name },
46066
+ React79__default.createElement(
46067
+ React79__default.Suspense,
46068
+ { fallback: null },
46069
+ React79__default.createElement(Lazy, props)
46070
+ )
45869
46071
  );
45870
46072
  }
45871
46073
  ThreeWrapper.displayName = `Lazy(${name})`;
45872
46074
  return ThreeWrapper;
45873
46075
  }
45874
- var FeatureRenderer, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
46076
+ var ThreeBoundary, FeatureRenderer, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
45875
46077
  var init_component_registry_generated = __esm({
45876
46078
  "components/core/organisms/component-registry.generated.ts"() {
45877
46079
  init_AboutPageTemplate();
@@ -46157,6 +46359,28 @@ var init_component_registry_generated = __esm({
46157
46359
  init_WorldMapBoard();
46158
46360
  init_WorldMapTemplate();
46159
46361
  init_XPBar();
46362
+ ThreeBoundary = class extends React79__default.Component {
46363
+ constructor() {
46364
+ super(...arguments);
46365
+ __publicField(this, "state", { failed: false });
46366
+ }
46367
+ static getDerivedStateFromError() {
46368
+ return { failed: true };
46369
+ }
46370
+ render() {
46371
+ if (this.state.failed) {
46372
+ return React79__default.createElement(
46373
+ "div",
46374
+ {
46375
+ "data-testid": "three-unavailable",
46376
+ style: { padding: 16, fontSize: 13, lineHeight: 1.5, opacity: 0.7 }
46377
+ },
46378
+ `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.`
46379
+ );
46380
+ }
46381
+ return this.props.children;
46382
+ }
46383
+ };
46160
46384
  FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/molecules/game/three'));
46161
46385
  GameCanvas3D = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
46162
46386
  GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));