@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.
@@ -3212,6 +3212,8 @@ var init_Input = __esm({
3212
3212
  className,
3213
3213
  inputType,
3214
3214
  type: htmlType,
3215
+ label,
3216
+ helperText,
3215
3217
  error,
3216
3218
  leftIcon,
3217
3219
  rightIcon,
@@ -3267,82 +3269,95 @@ var init_Input = __esm({
3267
3269
  onClear?.();
3268
3270
  }
3269
3271
  };
3272
+ const wrapField = (field) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full", children: [
3273
+ label && /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-foreground mb-1", children: label }),
3274
+ field,
3275
+ (helperText || error) && /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn("mt-1 text-xs", error ? "text-error" : "text-muted-foreground"), children: error ?? helperText })
3276
+ ] });
3270
3277
  if (type === "select") {
3271
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
3272
- resolvedLeftIcon && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
3273
- /* @__PURE__ */ jsxRuntime.jsxs(
3274
- "select",
3278
+ return wrapField(
3279
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
3280
+ resolvedLeftIcon && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
3281
+ /* @__PURE__ */ jsxRuntime.jsxs(
3282
+ "select",
3283
+ {
3284
+ ref,
3285
+ value,
3286
+ onChange: handleChange,
3287
+ className: cn(baseClassName, "appearance-none pr-10", className),
3288
+ ...props,
3289
+ children: [
3290
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: t("form.selectPlaceholder", { label: "" }) }),
3291
+ options?.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.value, children: opt.label }, opt.value))
3292
+ ]
3293
+ }
3294
+ ),
3295
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default" }) })
3296
+ ] })
3297
+ );
3298
+ }
3299
+ if (type === "textarea") {
3300
+ return wrapField(
3301
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
3302
+ "textarea",
3275
3303
  {
3276
3304
  ref,
3277
3305
  value,
3278
3306
  onChange: handleChange,
3279
- className: cn(baseClassName, "appearance-none pr-10", className),
3280
- ...props,
3281
- children: [
3282
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: t("form.selectPlaceholder", { label: "" }) }),
3283
- options?.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.value, children: opt.label }, opt.value))
3284
- ]
3307
+ rows: rows2,
3308
+ className: baseClassName,
3309
+ ...props
3285
3310
  }
3286
- ),
3287
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default" }) })
3288
- ] });
3289
- }
3290
- if (type === "textarea") {
3291
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
3292
- "textarea",
3293
- {
3294
- ref,
3295
- value,
3296
- onChange: handleChange,
3297
- rows: rows2,
3298
- className: baseClassName,
3299
- ...props
3300
- }
3301
- ) });
3311
+ ) })
3312
+ );
3302
3313
  }
3303
3314
  if (type === "checkbox") {
3304
- return /* @__PURE__ */ jsxRuntime.jsx(
3305
- "input",
3306
- {
3307
- ref,
3308
- type: "checkbox",
3309
- checked: props.checked,
3310
- onChange: handleChange,
3311
- className: cn(
3312
- "h-icon-default w-icon-default rounded-sm",
3313
- "border-border",
3314
- "text-primary focus:ring-ring",
3315
- "disabled:opacity-50 disabled:cursor-not-allowed",
3316
- className
3317
- ),
3318
- ...props
3319
- }
3315
+ return wrapField(
3316
+ /* @__PURE__ */ jsxRuntime.jsx(
3317
+ "input",
3318
+ {
3319
+ ref,
3320
+ type: "checkbox",
3321
+ checked: props.checked,
3322
+ onChange: handleChange,
3323
+ className: cn(
3324
+ "h-icon-default w-icon-default rounded-sm",
3325
+ "border-border",
3326
+ "text-primary focus:ring-ring",
3327
+ "disabled:opacity-50 disabled:cursor-not-allowed",
3328
+ className
3329
+ ),
3330
+ ...props
3331
+ }
3332
+ )
3320
3333
  );
3321
3334
  }
3322
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
3323
- resolvedLeftIcon && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
3324
- /* @__PURE__ */ jsxRuntime.jsx(
3325
- "input",
3326
- {
3327
- ref,
3328
- type,
3329
- value,
3330
- onChange: handleChange,
3331
- className: baseClassName,
3332
- ...props
3333
- }
3334
- ),
3335
- showClearButton && /* @__PURE__ */ jsxRuntime.jsx(
3336
- "button",
3337
- {
3338
- type: "button",
3339
- onClick: handleClear,
3340
- className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground hover:text-foreground",
3341
- children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
3342
- }
3343
- ),
3344
- rightIcon && !showClearButton && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground", children: resolveIconNode(rightIcon, iconCls) })
3345
- ] });
3335
+ return wrapField(
3336
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
3337
+ resolvedLeftIcon && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
3338
+ /* @__PURE__ */ jsxRuntime.jsx(
3339
+ "input",
3340
+ {
3341
+ ref,
3342
+ type,
3343
+ value,
3344
+ onChange: handleChange,
3345
+ className: baseClassName,
3346
+ ...props
3347
+ }
3348
+ ),
3349
+ showClearButton && /* @__PURE__ */ jsxRuntime.jsx(
3350
+ "button",
3351
+ {
3352
+ type: "button",
3353
+ onClick: handleClear,
3354
+ className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground hover:text-foreground",
3355
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
3356
+ }
3357
+ ),
3358
+ rightIcon && !showClearButton && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground", children: resolveIconNode(rightIcon, iconCls) })
3359
+ ] })
3360
+ );
3346
3361
  }
3347
3362
  );
3348
3363
  Input.displayName = "Input";
@@ -3413,6 +3428,190 @@ var init_Textarea = __esm({
3413
3428
  Textarea.displayName = "Textarea";
3414
3429
  }
3415
3430
  });
3431
+ function flatOptions(opts, groups) {
3432
+ const flat = opts ?? [];
3433
+ const grp = (groups ?? []).flatMap((g) => g.options);
3434
+ return [...flat, ...grp];
3435
+ }
3436
+ function NativeSelect({
3437
+ className,
3438
+ options,
3439
+ groups,
3440
+ placeholder,
3441
+ error,
3442
+ onChange,
3443
+ value,
3444
+ ...props
3445
+ }) {
3446
+ const eventBus = useEventBus();
3447
+ const handleChange = (e) => {
3448
+ if (typeof onChange === "string") {
3449
+ eventBus.emit(`UI:${onChange}`, { value: e.target.value });
3450
+ } else {
3451
+ onChange?.(e.target.value);
3452
+ }
3453
+ };
3454
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
3455
+ /* @__PURE__ */ jsxRuntime.jsxs(
3456
+ "select",
3457
+ {
3458
+ onChange: handleChange,
3459
+ value,
3460
+ className: cn(
3461
+ "block w-full border-[length:var(--border-width)] shadow-sm appearance-none",
3462
+ "px-3 py-2 pr-10 text-sm text-foreground font-medium",
3463
+ "bg-card",
3464
+ "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
3465
+ "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
3466
+ error ? "border-error focus:border-error" : "border-border focus:border-primary",
3467
+ className
3468
+ ),
3469
+ ...props,
3470
+ children: [
3471
+ placeholder && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: placeholder }),
3472
+ options?.map((option) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)),
3473
+ groups?.map((group) => /* @__PURE__ */ jsxRuntime.jsx("optgroup", { label: group.label, children: group.options.map((option) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)) }, group.label))
3474
+ ]
3475
+ }
3476
+ ),
3477
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default text-foreground" }) })
3478
+ ] });
3479
+ }
3480
+ function RichSelect({
3481
+ className,
3482
+ options,
3483
+ groups,
3484
+ placeholder,
3485
+ error,
3486
+ onChange,
3487
+ value,
3488
+ multiple,
3489
+ searchable,
3490
+ clearable,
3491
+ disabled
3492
+ }) {
3493
+ const eventBus = useEventBus();
3494
+ const [open, setOpen] = React79.useState(false);
3495
+ const [search, setSearch] = React79.useState("");
3496
+ const containerRef = React79.useRef(null);
3497
+ const selected = multiple ? Array.isArray(value) ? value : value ? [value] : [] : value ? [value] : [];
3498
+ const all = flatOptions(options, groups);
3499
+ const filtered = searchable && search ? all.filter((o) => o.label.toLowerCase().includes(search.toLowerCase())) : all;
3500
+ const toggle = (optValue) => {
3501
+ let next;
3502
+ if (multiple) {
3503
+ next = selected.includes(optValue) ? selected.filter((v) => v !== optValue) : [...selected, optValue];
3504
+ } else {
3505
+ next = optValue;
3506
+ setOpen(false);
3507
+ }
3508
+ if (typeof onChange === "string") {
3509
+ eventBus.emit(`UI:${onChange}`, { value: next });
3510
+ } else {
3511
+ onChange?.(next);
3512
+ }
3513
+ };
3514
+ const clear = (e) => {
3515
+ e.stopPropagation();
3516
+ const next = multiple ? [] : "";
3517
+ if (typeof onChange === "string") {
3518
+ eventBus.emit(`UI:${onChange}`, { value: next });
3519
+ } else {
3520
+ onChange?.(next);
3521
+ }
3522
+ };
3523
+ React79.useEffect(() => {
3524
+ const handler = (e) => {
3525
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
3526
+ setOpen(false);
3527
+ setSearch("");
3528
+ }
3529
+ };
3530
+ document.addEventListener("mousedown", handler);
3531
+ return () => document.removeEventListener("mousedown", handler);
3532
+ }, []);
3533
+ const displayLabel = selected.length === 0 ? placeholder ?? "" : multiple ? `${selected.length} selected` : all.find((o) => o.value === selected[0])?.label ?? selected[0];
3534
+ const hasValue = selected.length > 0;
3535
+ const renderOptions = (opts) => opts.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
3536
+ "button",
3537
+ {
3538
+ type: "button",
3539
+ disabled: opt.disabled,
3540
+ onClick: () => !opt.disabled && toggle(opt.value),
3541
+ className: cn(
3542
+ "w-full flex items-center justify-between px-3 py-1.5 text-sm text-start",
3543
+ "hover:bg-muted transition-colors",
3544
+ "disabled:opacity-50 disabled:cursor-not-allowed",
3545
+ selected.includes(opt.value) && "text-primary font-medium"
3546
+ ),
3547
+ children: [
3548
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: opt.label }),
3549
+ selected.includes(opt.value) && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "check", className: "h-icon-default w-icon-default" })
3550
+ ]
3551
+ },
3552
+ opt.value
3553
+ ));
3554
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, className: cn("relative w-full", className), children: [
3555
+ /* @__PURE__ */ jsxRuntime.jsx(
3556
+ "button",
3557
+ {
3558
+ type: "button",
3559
+ disabled,
3560
+ onClick: () => !disabled && setOpen((o) => !o),
3561
+ className: cn(
3562
+ "block w-full border-[length:var(--border-width)] shadow-sm",
3563
+ "px-3 py-2 pr-10 text-sm text-start font-medium",
3564
+ "bg-card rounded-sm",
3565
+ "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
3566
+ "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
3567
+ error ? "border-error focus:border-error" : "border-border focus:border-primary",
3568
+ !hasValue && "text-muted-foreground"
3569
+ ),
3570
+ children: displayLabel
3571
+ }
3572
+ ),
3573
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center gap-1 pointer-events-none", children: [
3574
+ clearable && hasValue && /* @__PURE__ */ jsxRuntime.jsx(
3575
+ "button",
3576
+ {
3577
+ type: "button",
3578
+ onClick: clear,
3579
+ className: "pointer-events-auto text-muted-foreground hover:text-foreground",
3580
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
3581
+ }
3582
+ ),
3583
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default text-foreground" })
3584
+ ] }),
3585
+ open && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(
3586
+ "absolute z-50 mt-1 w-full",
3587
+ "bg-card border-[length:var(--border-width)] border-border",
3588
+ "rounded-sm shadow-elevation-popover py-1 max-h-60 overflow-y-auto"
3589
+ ), children: [
3590
+ searchable && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 pb-1 border-b border-border", children: /* @__PURE__ */ jsxRuntime.jsx(
3591
+ "input",
3592
+ {
3593
+ autoFocus: true,
3594
+ type: "text",
3595
+ value: search,
3596
+ onChange: (e) => setSearch(e.target.value),
3597
+ placeholder: "Search\u2026",
3598
+ className: cn(
3599
+ "w-full px-2 py-1 text-sm bg-transparent",
3600
+ "focus:outline-none text-foreground placeholder:text-muted-foreground"
3601
+ )
3602
+ }
3603
+ ) }),
3604
+ groups && groups.length > 0 ? groups.map((g) => {
3605
+ const groupFiltered = searchable && search ? g.options.filter((o) => o.label.toLowerCase().includes(search.toLowerCase())) : g.options;
3606
+ if (groupFiltered.length === 0) return null;
3607
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3608
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-1 text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: g.label }),
3609
+ renderOptions(groupFiltered)
3610
+ ] }, g.label);
3611
+ }) : renderOptions(filtered)
3612
+ ] })
3613
+ ] });
3614
+ }
3416
3615
  var Select;
3417
3616
  var init_Select = __esm({
3418
3617
  "components/core/atoms/Select.tsx"() {
@@ -3420,47 +3619,12 @@ var init_Select = __esm({
3420
3619
  init_Icon();
3421
3620
  init_useEventBus();
3422
3621
  Select = React79__namespace.default.forwardRef(
3423
- ({ className, options, placeholder, error, onChange, ...props }, ref) => {
3424
- const eventBus = useEventBus();
3425
- const handleChange = (e) => {
3426
- if (typeof onChange === "string") {
3427
- eventBus.emit(`UI:${onChange}`, { value: e.target.value });
3428
- } else {
3429
- onChange?.(e);
3430
- }
3431
- };
3432
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
3433
- /* @__PURE__ */ jsxRuntime.jsxs(
3434
- "select",
3435
- {
3436
- ref,
3437
- onChange: handleChange,
3438
- className: cn(
3439
- "block w-full border-[length:var(--border-width)] shadow-sm appearance-none",
3440
- "px-3 py-2 pr-10 text-sm text-foreground font-medium",
3441
- "bg-card",
3442
- "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
3443
- "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
3444
- error ? "border-error focus:border-error" : "border-border focus:border-primary",
3445
- className
3446
- ),
3447
- ...props,
3448
- children: [
3449
- placeholder && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: placeholder }),
3450
- options.map((option) => /* @__PURE__ */ jsxRuntime.jsx(
3451
- "option",
3452
- {
3453
- value: option.value,
3454
- disabled: option.disabled,
3455
- children: option.label
3456
- },
3457
- option.value
3458
- ))
3459
- ]
3460
- }
3461
- ),
3462
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default text-foreground" }) })
3463
- ] });
3622
+ (props, _ref) => {
3623
+ const { multiple, searchable, clearable } = props;
3624
+ if (multiple || searchable || clearable) {
3625
+ return /* @__PURE__ */ jsxRuntime.jsx(RichSelect, { ...props });
3626
+ }
3627
+ return /* @__PURE__ */ jsxRuntime.jsx(NativeSelect, { ...props });
3464
3628
  }
3465
3629
  );
3466
3630
  Select.displayName = "Select";
@@ -3514,11 +3678,54 @@ var init_Checkbox = __esm({
3514
3678
  Checkbox.displayName = "Checkbox";
3515
3679
  }
3516
3680
  });
3681
+ var sizeStyles3, Spinner;
3682
+ var init_Spinner = __esm({
3683
+ "components/core/atoms/Spinner.tsx"() {
3684
+ init_cn();
3685
+ init_Icon();
3686
+ sizeStyles3 = {
3687
+ xs: "h-3 w-3",
3688
+ sm: "h-icon-default w-icon-default",
3689
+ md: "h-6 w-6",
3690
+ lg: "h-8 w-8"
3691
+ };
3692
+ Spinner = React79__namespace.default.forwardRef(
3693
+ ({ className, size = "md", overlay, ...props }, ref) => {
3694
+ if (overlay) {
3695
+ return /* @__PURE__ */ jsxRuntime.jsx(
3696
+ "div",
3697
+ {
3698
+ ref,
3699
+ className: cn(
3700
+ "absolute inset-0 z-10 flex items-center justify-center",
3701
+ "bg-background/60 backdrop-blur-sm",
3702
+ className
3703
+ ),
3704
+ ...props,
3705
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "loader", className: cn("animate-spin text-foreground", sizeStyles3[size]) })
3706
+ }
3707
+ );
3708
+ }
3709
+ return /* @__PURE__ */ jsxRuntime.jsx(
3710
+ "div",
3711
+ {
3712
+ ref,
3713
+ className: cn("text-foreground", className),
3714
+ ...props,
3715
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "loader", className: cn("animate-spin", sizeStyles3[size]) })
3716
+ }
3717
+ );
3718
+ }
3719
+ );
3720
+ Spinner.displayName = "Spinner";
3721
+ }
3722
+ });
3517
3723
  var variantStyles4, paddingStyles2, shadowStyles2, lookStyles2, Card, CardHeader, CardTitle, CardContent, CardBody, CardFooter;
3518
3724
  var init_Card = __esm({
3519
3725
  "components/core/atoms/Card.tsx"() {
3520
3726
  init_cn();
3521
3727
  init_useEventBus();
3728
+ init_Spinner();
3522
3729
  variantStyles4 = {
3523
3730
  default: [
3524
3731
  "bg-card",
@@ -3583,6 +3790,7 @@ var init_Card = __esm({
3583
3790
  look = "elevated",
3584
3791
  children,
3585
3792
  action,
3793
+ loading,
3586
3794
  onClick,
3587
3795
  ...props
3588
3796
  }, ref) => {
@@ -3596,7 +3804,7 @@ var init_Card = __esm({
3596
3804
  {
3597
3805
  ref,
3598
3806
  className: cn(
3599
- "rounded-container",
3807
+ "rounded-container relative",
3600
3808
  "transition-all duration-[var(--transition-normal)]",
3601
3809
  variantStyles4[variant],
3602
3810
  paddingStyles2[padding],
@@ -3607,6 +3815,7 @@ var init_Card = __esm({
3607
3815
  onClick: handleClick,
3608
3816
  ...props,
3609
3817
  children: [
3818
+ loading && /* @__PURE__ */ jsxRuntime.jsx(Spinner, { overlay: true, size: "md" }),
3610
3819
  (title || subtitle) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-4", children: [
3611
3820
  title && /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg text-card-foreground font-bold", children: title }),
3612
3821
  subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mt-1", children: subtitle })
@@ -3648,7 +3857,7 @@ var init_Card = __esm({
3648
3857
  CardFooter.displayName = "CardFooter";
3649
3858
  }
3650
3859
  });
3651
- var variantStyles5, sizeStyles3, iconSizes, FilterPill;
3860
+ var variantStyles5, sizeStyles4, iconSizes, FilterPill;
3652
3861
  var init_FilterPill = __esm({
3653
3862
  "components/core/atoms/FilterPill.tsx"() {
3654
3863
  init_cn();
@@ -3682,7 +3891,7 @@ var init_FilterPill = __esm({
3682
3891
  "border-[length:var(--border-width-thin)] border-border"
3683
3892
  ].join(" ")
3684
3893
  };
3685
- sizeStyles3 = {
3894
+ sizeStyles4 = {
3686
3895
  sm: "px-2 py-0.5 text-xs",
3687
3896
  md: "px-2.5 py-1 text-sm",
3688
3897
  lg: "px-3 py-1.5 text-base"
@@ -3726,7 +3935,7 @@ var init_FilterPill = __esm({
3726
3935
  className: cn(
3727
3936
  "inline-flex items-center gap-1 font-bold rounded-pill",
3728
3937
  variantStyles5[variant],
3729
- sizeStyles3[size],
3938
+ sizeStyles4[size],
3730
3939
  (onClick || clickEvent) && "cursor-pointer",
3731
3940
  className
3732
3941
  ),
@@ -3758,33 +3967,6 @@ var init_FilterPill = __esm({
3758
3967
  FilterPill.displayName = "FilterPill";
3759
3968
  }
3760
3969
  });
3761
- var sizeStyles4, Spinner;
3762
- var init_Spinner = __esm({
3763
- "components/core/atoms/Spinner.tsx"() {
3764
- init_cn();
3765
- init_Icon();
3766
- sizeStyles4 = {
3767
- xs: "h-3 w-3",
3768
- sm: "h-icon-default w-icon-default",
3769
- md: "h-6 w-6",
3770
- lg: "h-8 w-8"
3771
- };
3772
- Spinner = React79__namespace.default.forwardRef(
3773
- ({ className, size = "md", ...props }, ref) => {
3774
- return /* @__PURE__ */ jsxRuntime.jsx(
3775
- "div",
3776
- {
3777
- ref,
3778
- className: cn("text-foreground", className),
3779
- ...props,
3780
- children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "loader", className: cn("animate-spin", sizeStyles4[size]) })
3781
- }
3782
- );
3783
- }
3784
- );
3785
- Spinner.displayName = "Spinner";
3786
- }
3787
- });
3788
3970
  function generateInitials(name) {
3789
3971
  const parts = name.trim().split(/\s+/);
3790
3972
  if (parts.length === 1) {
@@ -16082,30 +16264,30 @@ var init_BranchingLogicBuilder = __esm({
16082
16264
  if (!sourceQuestion?.optionValues) return [];
16083
16265
  return sourceQuestion.optionValues.map((v) => ({ value: v, label: v }));
16084
16266
  }, [sourceQuestion]);
16085
- const handleSource = (e) => {
16086
- onChange({ ...rule, sourceQuestionId: e.target.value });
16267
+ const handleSource = (v) => {
16268
+ onChange({ ...rule, sourceQuestionId: v });
16087
16269
  };
16088
- const handleOperator = (e) => {
16089
- const next = e.target.value;
16270
+ const handleOperator = (v) => {
16271
+ const next = v;
16090
16272
  const nextValue = next === "in" && !Array.isArray(rule.value) ? rule.value ? [rule.value] : [] : next !== "in" && Array.isArray(rule.value) ? rule.value[0] ?? "" : rule.value;
16091
16273
  onChange({ ...rule, operator: next, value: nextValue });
16092
16274
  };
16093
16275
  const handleScalarValue = (e) => {
16094
16276
  onChange({ ...rule, value: e.target.value });
16095
16277
  };
16096
- const handleAddChip = (e) => {
16097
- const v = e.target.value;
16098
- if (!v) return;
16278
+ const handleAddChip = (v) => {
16279
+ const val = v;
16280
+ if (!val) return;
16099
16281
  const current = Array.isArray(rule.value) ? rule.value : [];
16100
- if (current.includes(v)) return;
16101
- onChange({ ...rule, value: [...current, v] });
16282
+ if (current.includes(val)) return;
16283
+ onChange({ ...rule, value: [...current, val] });
16102
16284
  };
16103
16285
  const handleRemoveChip = (chip) => {
16104
16286
  const current = Array.isArray(rule.value) ? rule.value : [];
16105
16287
  onChange({ ...rule, value: current.filter((c) => c !== chip) });
16106
16288
  };
16107
- const handleTarget = (e) => {
16108
- onChange({ ...rule, targetQuestionId: e.target.value });
16289
+ const handleTarget = (v) => {
16290
+ onChange({ ...rule, targetQuestionId: v });
16109
16291
  };
16110
16292
  const isMulti = rule.operator === "in";
16111
16293
  const chips = Array.isArray(rule.value) ? rule.value : [];
@@ -16186,7 +16368,7 @@ var init_BranchingLogicBuilder = __esm({
16186
16368
  options: valueOptions,
16187
16369
  value: scalarValue,
16188
16370
  placeholder: t("branchingLogic.selectValue"),
16189
- onChange: (e) => onChange({ ...rule, value: e.target.value }),
16371
+ onChange: (v) => onChange({ ...rule, value: v }),
16190
16372
  disabled: readOnly
16191
16373
  }
16192
16374
  ) : /* @__PURE__ */ jsxRuntime.jsx(
@@ -18618,7 +18800,7 @@ var init_Pagination = __esm({
18618
18800
  Select,
18619
18801
  {
18620
18802
  value: String(pageSize),
18621
- onChange: (e) => handlePageSizeChange(Number(e.target.value)),
18803
+ onChange: (v) => handlePageSizeChange(Number(v)),
18622
18804
  options: pageSizeOptions.map((size) => ({
18623
18805
  value: String(size),
18624
18806
  label: String(size)
@@ -21644,7 +21826,9 @@ var init_Menu = __esm({
21644
21826
  trigger,
21645
21827
  items,
21646
21828
  position = "bottom-left",
21647
- className
21829
+ className,
21830
+ header,
21831
+ footer
21648
21832
  }) => {
21649
21833
  const eventBus = useEventBus();
21650
21834
  const { direction } = hooks.useTranslate();
@@ -21776,14 +21960,18 @@ var init_Menu = __esm({
21776
21960
  )
21777
21961
  ] }, itemId);
21778
21962
  });
21779
- const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxRuntime.jsx(
21963
+ const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxRuntime.jsxs(
21780
21964
  "div",
21781
21965
  {
21782
21966
  ref: menuRef,
21783
21967
  className: cn("fixed z-50", menuContainerStyles, className),
21784
21968
  style: computeMenuStyle(effectivePosition, triggerRect),
21785
21969
  role: "menu",
21786
- children: renderMenuItems(items)
21970
+ children: [
21971
+ header && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-2 border-b border-border", children: header }),
21972
+ renderMenuItems(items),
21973
+ footer && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-2 border-t border-border", children: footer })
21974
+ ]
21787
21975
  }
21788
21976
  ) : null;
21789
21977
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -23542,7 +23730,7 @@ var init_FilterGroup = __esm({
23542
23730
  Select,
23543
23731
  {
23544
23732
  value: selectedValues[filter.field] || "all",
23545
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
23733
+ onChange: (v) => handleFilterSelect(filter.field, v),
23546
23734
  options: [
23547
23735
  { value: "all", label: t("filterGroup.all") },
23548
23736
  ...filter.options?.map((opt) => ({
@@ -23625,7 +23813,7 @@ var init_FilterGroup = __esm({
23625
23813
  Select,
23626
23814
  {
23627
23815
  value: selectedValues[filter.field] || "all",
23628
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
23816
+ onChange: (v) => handleFilterSelect(filter.field, v),
23629
23817
  options: [
23630
23818
  { value: "all", label: t("filterGroup.allOf", { label: filter.label }) },
23631
23819
  ...filter.options?.map((opt) => ({
@@ -23743,7 +23931,7 @@ var init_FilterGroup = __esm({
23743
23931
  Select,
23744
23932
  {
23745
23933
  value: selectedValues[filter.field] || "all",
23746
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
23934
+ onChange: (v) => handleFilterSelect(filter.field, v),
23747
23935
  options: [
23748
23936
  { value: "all", label: t("filterGroup.all") },
23749
23937
  ...filter.options?.map((opt) => ({
@@ -27925,13 +28113,13 @@ var init_MapView = __esm({
27925
28113
  shadowSize: [41, 41]
27926
28114
  });
27927
28115
  L.Marker.prototype.options.icon = defaultIcon;
27928
- const { useEffect: useEffect72, useRef: useRef66, useCallback: useCallback112, useState: useState102 } = React79__namespace.default;
28116
+ const { useEffect: useEffect73, useRef: useRef67, useCallback: useCallback112, useState: useState103 } = React79__namespace.default;
27929
28117
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
27930
28118
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
27931
28119
  function MapUpdater({ centerLat, centerLng, zoom }) {
27932
28120
  const map = useMap();
27933
- const prevRef = useRef66({ centerLat, centerLng, zoom });
27934
- useEffect72(() => {
28121
+ const prevRef = useRef67({ centerLat, centerLng, zoom });
28122
+ useEffect73(() => {
27935
28123
  const prev = prevRef.current;
27936
28124
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
27937
28125
  map.setView([centerLat, centerLng], zoom);
@@ -27942,7 +28130,7 @@ var init_MapView = __esm({
27942
28130
  }
27943
28131
  function MapClickHandler({ onMapClick }) {
27944
28132
  const map = useMap();
27945
- useEffect72(() => {
28133
+ useEffect73(() => {
27946
28134
  if (!onMapClick) return;
27947
28135
  const handler = (e) => {
27948
28136
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -27970,7 +28158,7 @@ var init_MapView = __esm({
27970
28158
  showAttribution = true
27971
28159
  }) {
27972
28160
  const eventBus = useEventBus3();
27973
- const [clickedPosition, setClickedPosition] = useState102(null);
28161
+ const [clickedPosition, setClickedPosition] = useState103(null);
27974
28162
  const handleMapClick = useCallback112((lat, lng) => {
27975
28163
  if (showClickedPin) {
27976
28164
  setClickedPosition({ lat, lng });
@@ -33406,8 +33594,8 @@ var init_VersionDiff = __esm({
33406
33594
  return { added, removed };
33407
33595
  }, [diff]);
33408
33596
  const handleBeforeChange = React79.useCallback(
33409
- (e) => {
33410
- const id = e.target.value;
33597
+ (v) => {
33598
+ const id = v;
33411
33599
  setInternalBefore(id);
33412
33600
  onSelectBefore?.(id);
33413
33601
  if (selectBeforeEvent) eventBus.emit(`UI:${selectBeforeEvent}`, { id });
@@ -33415,8 +33603,8 @@ var init_VersionDiff = __esm({
33415
33603
  [onSelectBefore, selectBeforeEvent, eventBus]
33416
33604
  );
33417
33605
  const handleAfterChange = React79.useCallback(
33418
- (e) => {
33419
- const id = e.target.value;
33606
+ (v) => {
33607
+ const id = v;
33420
33608
  setInternalAfter(id);
33421
33609
  onSelectAfter?.(id);
33422
33610
  if (selectAfterEvent) eventBus.emit(`UI:${selectAfterEvent}`, { id });
@@ -38042,11 +38230,11 @@ function RuleEditor({
38042
38230
  className
38043
38231
  }) {
38044
38232
  const { t } = hooks.useTranslate();
38045
- const handleWhenChange = React79.useCallback((e) => {
38046
- onChange({ ...rule, whenEvent: e.target.value });
38233
+ const handleWhenChange = React79.useCallback((v) => {
38234
+ onChange({ ...rule, whenEvent: v });
38047
38235
  }, [rule, onChange]);
38048
- const handleThenChange = React79.useCallback((e) => {
38049
- onChange({ ...rule, thenAction: e.target.value });
38236
+ const handleThenChange = React79.useCallback((v) => {
38237
+ onChange({ ...rule, thenAction: v });
38050
38238
  }, [rule, onChange]);
38051
38239
  return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: cn("items-center p-2 rounded-lg bg-muted/50 border border-border", className), gap: "sm", children: [
38052
38240
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-primary font-bold whitespace-nowrap", children: t("eventHandler.when") }),
@@ -39122,7 +39310,7 @@ var init_Form = __esm({
39122
39310
  ...commonProps,
39123
39311
  options,
39124
39312
  value: String(currentValue),
39125
- onChange: (e) => handleChange(fieldName, e.target.value),
39313
+ onChange: (v) => handleChange(fieldName, v),
39126
39314
  placeholder: field.placeholder || `Select ${label}...`
39127
39315
  }
39128
39316
  );
@@ -45909,18 +46097,32 @@ var init_WorldMapTemplate = __esm({
45909
46097
  }
45910
46098
  });
45911
46099
  function lazyThree(name, loader) {
45912
- const Lazy = React79__namespace.default.lazy(() => loader().then((m) => ({ default: m[name] })));
46100
+ const Lazy = React79__namespace.default.lazy(
46101
+ () => loader().then((m) => {
46102
+ const Resolved = m[name];
46103
+ if (!Resolved) {
46104
+ throw new Error(
46105
+ `[@almadar/ui] 3D component "${name}" was not found in the three subpath bundle.`
46106
+ );
46107
+ }
46108
+ return { default: Resolved };
46109
+ })
46110
+ );
45913
46111
  function ThreeWrapper(props) {
45914
46112
  return React79__namespace.default.createElement(
45915
- React79__namespace.default.Suspense,
45916
- { fallback: null },
45917
- React79__namespace.default.createElement(Lazy, props)
46113
+ ThreeBoundary,
46114
+ { name },
46115
+ React79__namespace.default.createElement(
46116
+ React79__namespace.default.Suspense,
46117
+ { fallback: null },
46118
+ React79__namespace.default.createElement(Lazy, props)
46119
+ )
45918
46120
  );
45919
46121
  }
45920
46122
  ThreeWrapper.displayName = `Lazy(${name})`;
45921
46123
  return ThreeWrapper;
45922
46124
  }
45923
- var FeatureRenderer, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
46125
+ var ThreeBoundary, FeatureRenderer, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
45924
46126
  var init_component_registry_generated = __esm({
45925
46127
  "components/core/organisms/component-registry.generated.ts"() {
45926
46128
  init_AboutPageTemplate();
@@ -46206,6 +46408,28 @@ var init_component_registry_generated = __esm({
46206
46408
  init_WorldMapBoard();
46207
46409
  init_WorldMapTemplate();
46208
46410
  init_XPBar();
46411
+ ThreeBoundary = class extends React79__namespace.default.Component {
46412
+ constructor() {
46413
+ super(...arguments);
46414
+ __publicField(this, "state", { failed: false });
46415
+ }
46416
+ static getDerivedStateFromError() {
46417
+ return { failed: true };
46418
+ }
46419
+ render() {
46420
+ if (this.state.failed) {
46421
+ return React79__namespace.default.createElement(
46422
+ "div",
46423
+ {
46424
+ "data-testid": "three-unavailable",
46425
+ style: { padding: 16, fontSize: 13, lineHeight: 1.5, opacity: 0.7 }
46426
+ },
46427
+ `3D pattern "${this.props.name}" requires three.js. Install the optional peers three + @react-three/fiber + @react-three/drei (matching the host React major) to render it.`
46428
+ );
46429
+ }
46430
+ return this.props.children;
46431
+ }
46432
+ };
46209
46433
  FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/molecules/game/three'));
46210
46434
  GameCanvas3D = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
46211
46435
  GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));