@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.
package/dist/avl/index.js CHANGED
@@ -6988,6 +6988,8 @@ var init_Input = __esm({
6988
6988
  className,
6989
6989
  inputType,
6990
6990
  type: htmlType,
6991
+ label,
6992
+ helperText,
6991
6993
  error,
6992
6994
  leftIcon,
6993
6995
  rightIcon,
@@ -7043,82 +7045,95 @@ var init_Input = __esm({
7043
7045
  onClear?.();
7044
7046
  }
7045
7047
  };
7048
+ const wrapField = (field) => /* @__PURE__ */ jsxs("div", { className: "w-full", children: [
7049
+ label && /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-foreground mb-1", children: label }),
7050
+ field,
7051
+ (helperText || error) && /* @__PURE__ */ jsx("p", { className: cn("mt-1 text-xs", error ? "text-error" : "text-muted-foreground"), children: error ?? helperText })
7052
+ ] });
7046
7053
  if (type === "select") {
7047
- return /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
7048
- resolvedLeftIcon && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
7049
- /* @__PURE__ */ jsxs(
7050
- "select",
7054
+ return wrapField(
7055
+ /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
7056
+ resolvedLeftIcon && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
7057
+ /* @__PURE__ */ jsxs(
7058
+ "select",
7059
+ {
7060
+ ref,
7061
+ value,
7062
+ onChange: handleChange,
7063
+ className: cn(baseClassName, "appearance-none pr-10", className),
7064
+ ...props,
7065
+ children: [
7066
+ /* @__PURE__ */ jsx("option", { value: "", children: t("form.selectPlaceholder", { label: "" }) }),
7067
+ options?.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
7068
+ ]
7069
+ }
7070
+ ),
7071
+ /* @__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" }) })
7072
+ ] })
7073
+ );
7074
+ }
7075
+ if (type === "textarea") {
7076
+ return wrapField(
7077
+ /* @__PURE__ */ jsx("div", { className: "relative w-full", children: /* @__PURE__ */ jsx(
7078
+ "textarea",
7051
7079
  {
7052
7080
  ref,
7053
7081
  value,
7054
7082
  onChange: handleChange,
7055
- className: cn(baseClassName, "appearance-none pr-10", className),
7056
- ...props,
7057
- children: [
7058
- /* @__PURE__ */ jsx("option", { value: "", children: t("form.selectPlaceholder", { label: "" }) }),
7059
- options?.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
7060
- ]
7083
+ rows: rows2,
7084
+ className: baseClassName,
7085
+ ...props
7061
7086
  }
7062
- ),
7063
- /* @__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" }) })
7064
- ] });
7065
- }
7066
- if (type === "textarea") {
7067
- return /* @__PURE__ */ jsx("div", { className: "relative w-full", children: /* @__PURE__ */ jsx(
7068
- "textarea",
7069
- {
7070
- ref,
7071
- value,
7072
- onChange: handleChange,
7073
- rows: rows2,
7074
- className: baseClassName,
7075
- ...props
7076
- }
7077
- ) });
7087
+ ) })
7088
+ );
7078
7089
  }
7079
7090
  if (type === "checkbox") {
7080
- return /* @__PURE__ */ jsx(
7081
- "input",
7082
- {
7083
- ref,
7084
- type: "checkbox",
7085
- checked: props.checked,
7086
- onChange: handleChange,
7087
- className: cn(
7088
- "h-icon-default w-icon-default rounded-sm",
7089
- "border-border",
7090
- "text-primary focus:ring-ring",
7091
- "disabled:opacity-50 disabled:cursor-not-allowed",
7092
- className
7093
- ),
7094
- ...props
7095
- }
7091
+ return wrapField(
7092
+ /* @__PURE__ */ jsx(
7093
+ "input",
7094
+ {
7095
+ ref,
7096
+ type: "checkbox",
7097
+ checked: props.checked,
7098
+ onChange: handleChange,
7099
+ className: cn(
7100
+ "h-icon-default w-icon-default rounded-sm",
7101
+ "border-border",
7102
+ "text-primary focus:ring-ring",
7103
+ "disabled:opacity-50 disabled:cursor-not-allowed",
7104
+ className
7105
+ ),
7106
+ ...props
7107
+ }
7108
+ )
7096
7109
  );
7097
7110
  }
7098
- return /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
7099
- resolvedLeftIcon && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
7100
- /* @__PURE__ */ jsx(
7101
- "input",
7102
- {
7103
- ref,
7104
- type,
7105
- value,
7106
- onChange: handleChange,
7107
- className: baseClassName,
7108
- ...props
7109
- }
7110
- ),
7111
- showClearButton && /* @__PURE__ */ jsx(
7112
- "button",
7113
- {
7114
- type: "button",
7115
- onClick: handleClear,
7116
- className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground hover:text-foreground",
7117
- children: /* @__PURE__ */ jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
7118
- }
7119
- ),
7120
- rightIcon && !showClearButton && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground", children: resolveIconNode(rightIcon, iconCls) })
7121
- ] });
7111
+ return wrapField(
7112
+ /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
7113
+ resolvedLeftIcon && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-muted-foreground", children: resolvedLeftIcon }),
7114
+ /* @__PURE__ */ jsx(
7115
+ "input",
7116
+ {
7117
+ ref,
7118
+ type,
7119
+ value,
7120
+ onChange: handleChange,
7121
+ className: baseClassName,
7122
+ ...props
7123
+ }
7124
+ ),
7125
+ showClearButton && /* @__PURE__ */ jsx(
7126
+ "button",
7127
+ {
7128
+ type: "button",
7129
+ onClick: handleClear,
7130
+ className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground hover:text-foreground",
7131
+ children: /* @__PURE__ */ jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
7132
+ }
7133
+ ),
7134
+ rightIcon && !showClearButton && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground", children: resolveIconNode(rightIcon, iconCls) })
7135
+ ] })
7136
+ );
7122
7137
  }
7123
7138
  );
7124
7139
  Input.displayName = "Input";
@@ -7189,6 +7204,190 @@ var init_Textarea = __esm({
7189
7204
  Textarea.displayName = "Textarea";
7190
7205
  }
7191
7206
  });
7207
+ function flatOptions(opts, groups) {
7208
+ const flat = opts ?? [];
7209
+ const grp = (groups ?? []).flatMap((g) => g.options);
7210
+ return [...flat, ...grp];
7211
+ }
7212
+ function NativeSelect({
7213
+ className,
7214
+ options,
7215
+ groups,
7216
+ placeholder,
7217
+ error,
7218
+ onChange,
7219
+ value,
7220
+ ...props
7221
+ }) {
7222
+ const eventBus = useEventBus();
7223
+ const handleChange = (e) => {
7224
+ if (typeof onChange === "string") {
7225
+ eventBus.emit(`UI:${onChange}`, { value: e.target.value });
7226
+ } else {
7227
+ onChange?.(e.target.value);
7228
+ }
7229
+ };
7230
+ return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
7231
+ /* @__PURE__ */ jsxs(
7232
+ "select",
7233
+ {
7234
+ onChange: handleChange,
7235
+ value,
7236
+ className: cn(
7237
+ "block w-full border-[length:var(--border-width)] shadow-sm appearance-none",
7238
+ "px-3 py-2 pr-10 text-sm text-foreground font-medium",
7239
+ "bg-card",
7240
+ "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
7241
+ "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
7242
+ error ? "border-error focus:border-error" : "border-border focus:border-primary",
7243
+ className
7244
+ ),
7245
+ ...props,
7246
+ children: [
7247
+ placeholder && /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: placeholder }),
7248
+ options?.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)),
7249
+ 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))
7250
+ ]
7251
+ }
7252
+ ),
7253
+ /* @__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" }) })
7254
+ ] });
7255
+ }
7256
+ function RichSelect({
7257
+ className,
7258
+ options,
7259
+ groups,
7260
+ placeholder,
7261
+ error,
7262
+ onChange,
7263
+ value,
7264
+ multiple,
7265
+ searchable,
7266
+ clearable,
7267
+ disabled
7268
+ }) {
7269
+ const eventBus = useEventBus();
7270
+ const [open, setOpen] = useState(false);
7271
+ const [search, setSearch] = useState("");
7272
+ const containerRef = useRef(null);
7273
+ const selected = multiple ? Array.isArray(value) ? value : value ? [value] : [] : value ? [value] : [];
7274
+ const all = flatOptions(options, groups);
7275
+ const filtered = searchable && search ? all.filter((o) => o.label.toLowerCase().includes(search.toLowerCase())) : all;
7276
+ const toggle = (optValue) => {
7277
+ let next;
7278
+ if (multiple) {
7279
+ next = selected.includes(optValue) ? selected.filter((v) => v !== optValue) : [...selected, optValue];
7280
+ } else {
7281
+ next = optValue;
7282
+ setOpen(false);
7283
+ }
7284
+ if (typeof onChange === "string") {
7285
+ eventBus.emit(`UI:${onChange}`, { value: next });
7286
+ } else {
7287
+ onChange?.(next);
7288
+ }
7289
+ };
7290
+ const clear = (e) => {
7291
+ e.stopPropagation();
7292
+ const next = multiple ? [] : "";
7293
+ if (typeof onChange === "string") {
7294
+ eventBus.emit(`UI:${onChange}`, { value: next });
7295
+ } else {
7296
+ onChange?.(next);
7297
+ }
7298
+ };
7299
+ useEffect(() => {
7300
+ const handler = (e) => {
7301
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
7302
+ setOpen(false);
7303
+ setSearch("");
7304
+ }
7305
+ };
7306
+ document.addEventListener("mousedown", handler);
7307
+ return () => document.removeEventListener("mousedown", handler);
7308
+ }, []);
7309
+ const displayLabel = selected.length === 0 ? placeholder ?? "" : multiple ? `${selected.length} selected` : all.find((o) => o.value === selected[0])?.label ?? selected[0];
7310
+ const hasValue = selected.length > 0;
7311
+ const renderOptions = (opts) => opts.map((opt) => /* @__PURE__ */ jsxs(
7312
+ "button",
7313
+ {
7314
+ type: "button",
7315
+ disabled: opt.disabled,
7316
+ onClick: () => !opt.disabled && toggle(opt.value),
7317
+ className: cn(
7318
+ "w-full flex items-center justify-between px-3 py-1.5 text-sm text-start",
7319
+ "hover:bg-muted transition-colors",
7320
+ "disabled:opacity-50 disabled:cursor-not-allowed",
7321
+ selected.includes(opt.value) && "text-primary font-medium"
7322
+ ),
7323
+ children: [
7324
+ /* @__PURE__ */ jsx("span", { children: opt.label }),
7325
+ selected.includes(opt.value) && /* @__PURE__ */ jsx(Icon, { name: "check", className: "h-icon-default w-icon-default" })
7326
+ ]
7327
+ },
7328
+ opt.value
7329
+ ));
7330
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: cn("relative w-full", className), children: [
7331
+ /* @__PURE__ */ jsx(
7332
+ "button",
7333
+ {
7334
+ type: "button",
7335
+ disabled,
7336
+ onClick: () => !disabled && setOpen((o) => !o),
7337
+ className: cn(
7338
+ "block w-full border-[length:var(--border-width)] shadow-sm",
7339
+ "px-3 py-2 pr-10 text-sm text-start font-medium",
7340
+ "bg-card rounded-sm",
7341
+ "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
7342
+ "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
7343
+ error ? "border-error focus:border-error" : "border-border focus:border-primary",
7344
+ !hasValue && "text-muted-foreground"
7345
+ ),
7346
+ children: displayLabel
7347
+ }
7348
+ ),
7349
+ /* @__PURE__ */ jsxs("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center gap-1 pointer-events-none", children: [
7350
+ clearable && hasValue && /* @__PURE__ */ jsx(
7351
+ "button",
7352
+ {
7353
+ type: "button",
7354
+ onClick: clear,
7355
+ className: "pointer-events-auto text-muted-foreground hover:text-foreground",
7356
+ children: /* @__PURE__ */ jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
7357
+ }
7358
+ ),
7359
+ /* @__PURE__ */ jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default text-foreground" })
7360
+ ] }),
7361
+ open && /* @__PURE__ */ jsxs("div", { className: cn(
7362
+ "absolute z-50 mt-1 w-full",
7363
+ "bg-card border-[length:var(--border-width)] border-border",
7364
+ "rounded-sm shadow-elevation-popover py-1 max-h-60 overflow-y-auto"
7365
+ ), children: [
7366
+ searchable && /* @__PURE__ */ jsx("div", { className: "px-2 pb-1 border-b border-border", children: /* @__PURE__ */ jsx(
7367
+ "input",
7368
+ {
7369
+ autoFocus: true,
7370
+ type: "text",
7371
+ value: search,
7372
+ onChange: (e) => setSearch(e.target.value),
7373
+ placeholder: "Search\u2026",
7374
+ className: cn(
7375
+ "w-full px-2 py-1 text-sm bg-transparent",
7376
+ "focus:outline-none text-foreground placeholder:text-muted-foreground"
7377
+ )
7378
+ }
7379
+ ) }),
7380
+ groups && groups.length > 0 ? groups.map((g) => {
7381
+ const groupFiltered = searchable && search ? g.options.filter((o) => o.label.toLowerCase().includes(search.toLowerCase())) : g.options;
7382
+ if (groupFiltered.length === 0) return null;
7383
+ return /* @__PURE__ */ jsxs("div", { children: [
7384
+ /* @__PURE__ */ jsx("div", { className: "px-3 py-1 text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: g.label }),
7385
+ renderOptions(groupFiltered)
7386
+ ] }, g.label);
7387
+ }) : renderOptions(filtered)
7388
+ ] })
7389
+ ] });
7390
+ }
7192
7391
  var Select;
7193
7392
  var init_Select = __esm({
7194
7393
  "components/core/atoms/Select.tsx"() {
@@ -7196,47 +7395,12 @@ var init_Select = __esm({
7196
7395
  init_Icon();
7197
7396
  init_useEventBus();
7198
7397
  Select = React88__default.forwardRef(
7199
- ({ className, options, placeholder, error, onChange, ...props }, ref) => {
7200
- const eventBus = useEventBus();
7201
- const handleChange = (e) => {
7202
- if (typeof onChange === "string") {
7203
- eventBus.emit(`UI:${onChange}`, { value: e.target.value });
7204
- } else {
7205
- onChange?.(e);
7206
- }
7207
- };
7208
- return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
7209
- /* @__PURE__ */ jsxs(
7210
- "select",
7211
- {
7212
- ref,
7213
- onChange: handleChange,
7214
- className: cn(
7215
- "block w-full border-[length:var(--border-width)] shadow-sm appearance-none",
7216
- "px-3 py-2 pr-10 text-sm text-foreground font-medium",
7217
- "bg-card",
7218
- "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
7219
- "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
7220
- error ? "border-error focus:border-error" : "border-border focus:border-primary",
7221
- className
7222
- ),
7223
- ...props,
7224
- children: [
7225
- placeholder && /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: placeholder }),
7226
- options.map((option) => /* @__PURE__ */ jsx(
7227
- "option",
7228
- {
7229
- value: option.value,
7230
- disabled: option.disabled,
7231
- children: option.label
7232
- },
7233
- option.value
7234
- ))
7235
- ]
7236
- }
7237
- ),
7238
- /* @__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" }) })
7239
- ] });
7398
+ (props, _ref) => {
7399
+ const { multiple, searchable, clearable } = props;
7400
+ if (multiple || searchable || clearable) {
7401
+ return /* @__PURE__ */ jsx(RichSelect, { ...props });
7402
+ }
7403
+ return /* @__PURE__ */ jsx(NativeSelect, { ...props });
7240
7404
  }
7241
7405
  );
7242
7406
  Select.displayName = "Select";
@@ -7290,11 +7454,54 @@ var init_Checkbox = __esm({
7290
7454
  Checkbox.displayName = "Checkbox";
7291
7455
  }
7292
7456
  });
7457
+ var sizeStyles3, Spinner;
7458
+ var init_Spinner = __esm({
7459
+ "components/core/atoms/Spinner.tsx"() {
7460
+ init_cn();
7461
+ init_Icon();
7462
+ sizeStyles3 = {
7463
+ xs: "h-3 w-3",
7464
+ sm: "h-icon-default w-icon-default",
7465
+ md: "h-6 w-6",
7466
+ lg: "h-8 w-8"
7467
+ };
7468
+ Spinner = React88__default.forwardRef(
7469
+ ({ className, size = "md", overlay, ...props }, ref) => {
7470
+ if (overlay) {
7471
+ return /* @__PURE__ */ jsx(
7472
+ "div",
7473
+ {
7474
+ ref,
7475
+ className: cn(
7476
+ "absolute inset-0 z-10 flex items-center justify-center",
7477
+ "bg-background/60 backdrop-blur-sm",
7478
+ className
7479
+ ),
7480
+ ...props,
7481
+ children: /* @__PURE__ */ jsx(Icon, { name: "loader", className: cn("animate-spin text-foreground", sizeStyles3[size]) })
7482
+ }
7483
+ );
7484
+ }
7485
+ return /* @__PURE__ */ jsx(
7486
+ "div",
7487
+ {
7488
+ ref,
7489
+ className: cn("text-foreground", className),
7490
+ ...props,
7491
+ children: /* @__PURE__ */ jsx(Icon, { name: "loader", className: cn("animate-spin", sizeStyles3[size]) })
7492
+ }
7493
+ );
7494
+ }
7495
+ );
7496
+ Spinner.displayName = "Spinner";
7497
+ }
7498
+ });
7293
7499
  var variantStyles4, paddingStyles2, shadowStyles2, lookStyles2, Card, CardHeader, CardTitle, CardContent, CardBody, CardFooter;
7294
7500
  var init_Card = __esm({
7295
7501
  "components/core/atoms/Card.tsx"() {
7296
7502
  init_cn();
7297
7503
  init_useEventBus();
7504
+ init_Spinner();
7298
7505
  variantStyles4 = {
7299
7506
  default: [
7300
7507
  "bg-card",
@@ -7359,6 +7566,7 @@ var init_Card = __esm({
7359
7566
  look = "elevated",
7360
7567
  children,
7361
7568
  action,
7569
+ loading,
7362
7570
  onClick,
7363
7571
  ...props
7364
7572
  }, ref) => {
@@ -7372,7 +7580,7 @@ var init_Card = __esm({
7372
7580
  {
7373
7581
  ref,
7374
7582
  className: cn(
7375
- "rounded-container",
7583
+ "rounded-container relative",
7376
7584
  "transition-all duration-[var(--transition-normal)]",
7377
7585
  variantStyles4[variant],
7378
7586
  paddingStyles2[padding],
@@ -7383,6 +7591,7 @@ var init_Card = __esm({
7383
7591
  onClick: handleClick,
7384
7592
  ...props,
7385
7593
  children: [
7594
+ loading && /* @__PURE__ */ jsx(Spinner, { overlay: true, size: "md" }),
7386
7595
  (title || subtitle) && /* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
7387
7596
  title && /* @__PURE__ */ jsx("h3", { className: "text-lg text-card-foreground font-bold", children: title }),
7388
7597
  subtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1", children: subtitle })
@@ -7424,7 +7633,7 @@ var init_Card = __esm({
7424
7633
  CardFooter.displayName = "CardFooter";
7425
7634
  }
7426
7635
  });
7427
- var variantStyles5, sizeStyles3, iconSizes, FilterPill;
7636
+ var variantStyles5, sizeStyles4, iconSizes, FilterPill;
7428
7637
  var init_FilterPill = __esm({
7429
7638
  "components/core/atoms/FilterPill.tsx"() {
7430
7639
  init_cn();
@@ -7458,7 +7667,7 @@ var init_FilterPill = __esm({
7458
7667
  "border-[length:var(--border-width-thin)] border-border"
7459
7668
  ].join(" ")
7460
7669
  };
7461
- sizeStyles3 = {
7670
+ sizeStyles4 = {
7462
7671
  sm: "px-2 py-0.5 text-xs",
7463
7672
  md: "px-2.5 py-1 text-sm",
7464
7673
  lg: "px-3 py-1.5 text-base"
@@ -7502,7 +7711,7 @@ var init_FilterPill = __esm({
7502
7711
  className: cn(
7503
7712
  "inline-flex items-center gap-1 font-bold rounded-pill",
7504
7713
  variantStyles5[variant],
7505
- sizeStyles3[size],
7714
+ sizeStyles4[size],
7506
7715
  (onClick || clickEvent) && "cursor-pointer",
7507
7716
  className
7508
7717
  ),
@@ -7534,33 +7743,6 @@ var init_FilterPill = __esm({
7534
7743
  FilterPill.displayName = "FilterPill";
7535
7744
  }
7536
7745
  });
7537
- var sizeStyles4, Spinner;
7538
- var init_Spinner = __esm({
7539
- "components/core/atoms/Spinner.tsx"() {
7540
- init_cn();
7541
- init_Icon();
7542
- sizeStyles4 = {
7543
- xs: "h-3 w-3",
7544
- sm: "h-icon-default w-icon-default",
7545
- md: "h-6 w-6",
7546
- lg: "h-8 w-8"
7547
- };
7548
- Spinner = React88__default.forwardRef(
7549
- ({ className, size = "md", ...props }, ref) => {
7550
- return /* @__PURE__ */ jsx(
7551
- "div",
7552
- {
7553
- ref,
7554
- className: cn("text-foreground", className),
7555
- ...props,
7556
- children: /* @__PURE__ */ jsx(Icon, { name: "loader", className: cn("animate-spin", sizeStyles4[size]) })
7557
- }
7558
- );
7559
- }
7560
- );
7561
- Spinner.displayName = "Spinner";
7562
- }
7563
- });
7564
7746
  function generateInitials(name) {
7565
7747
  const parts = name.trim().split(/\s+/);
7566
7748
  if (parts.length === 1) {
@@ -18943,30 +19125,30 @@ var init_BranchingLogicBuilder = __esm({
18943
19125
  if (!sourceQuestion?.optionValues) return [];
18944
19126
  return sourceQuestion.optionValues.map((v) => ({ value: v, label: v }));
18945
19127
  }, [sourceQuestion]);
18946
- const handleSource = (e) => {
18947
- onChange({ ...rule, sourceQuestionId: e.target.value });
19128
+ const handleSource = (v) => {
19129
+ onChange({ ...rule, sourceQuestionId: v });
18948
19130
  };
18949
- const handleOperator = (e) => {
18950
- const next = e.target.value;
19131
+ const handleOperator = (v) => {
19132
+ const next = v;
18951
19133
  const nextValue = next === "in" && !Array.isArray(rule.value) ? rule.value ? [rule.value] : [] : next !== "in" && Array.isArray(rule.value) ? rule.value[0] ?? "" : rule.value;
18952
19134
  onChange({ ...rule, operator: next, value: nextValue });
18953
19135
  };
18954
19136
  const handleScalarValue = (e) => {
18955
19137
  onChange({ ...rule, value: e.target.value });
18956
19138
  };
18957
- const handleAddChip = (e) => {
18958
- const v = e.target.value;
18959
- if (!v) return;
19139
+ const handleAddChip = (v) => {
19140
+ const val = v;
19141
+ if (!val) return;
18960
19142
  const current = Array.isArray(rule.value) ? rule.value : [];
18961
- if (current.includes(v)) return;
18962
- onChange({ ...rule, value: [...current, v] });
19143
+ if (current.includes(val)) return;
19144
+ onChange({ ...rule, value: [...current, val] });
18963
19145
  };
18964
19146
  const handleRemoveChip = (chip) => {
18965
19147
  const current = Array.isArray(rule.value) ? rule.value : [];
18966
19148
  onChange({ ...rule, value: current.filter((c) => c !== chip) });
18967
19149
  };
18968
- const handleTarget = (e) => {
18969
- onChange({ ...rule, targetQuestionId: e.target.value });
19150
+ const handleTarget = (v) => {
19151
+ onChange({ ...rule, targetQuestionId: v });
18970
19152
  };
18971
19153
  const isMulti = rule.operator === "in";
18972
19154
  const chips = Array.isArray(rule.value) ? rule.value : [];
@@ -19047,7 +19229,7 @@ var init_BranchingLogicBuilder = __esm({
19047
19229
  options: valueOptions,
19048
19230
  value: scalarValue,
19049
19231
  placeholder: t("branchingLogic.selectValue"),
19050
- onChange: (e) => onChange({ ...rule, value: e.target.value }),
19232
+ onChange: (v) => onChange({ ...rule, value: v }),
19051
19233
  disabled: readOnly
19052
19234
  }
19053
19235
  ) : /* @__PURE__ */ jsx(
@@ -21479,7 +21661,7 @@ var init_Pagination = __esm({
21479
21661
  Select,
21480
21662
  {
21481
21663
  value: String(pageSize),
21482
- onChange: (e) => handlePageSizeChange(Number(e.target.value)),
21664
+ onChange: (v) => handlePageSizeChange(Number(v)),
21483
21665
  options: pageSizeOptions.map((size) => ({
21484
21666
  value: String(size),
21485
21667
  label: String(size)
@@ -24505,7 +24687,9 @@ var init_Menu = __esm({
24505
24687
  trigger,
24506
24688
  items,
24507
24689
  position = "bottom-left",
24508
- className
24690
+ className,
24691
+ header,
24692
+ footer
24509
24693
  }) => {
24510
24694
  const eventBus = useEventBus();
24511
24695
  const { direction } = useTranslate();
@@ -24637,14 +24821,18 @@ var init_Menu = __esm({
24637
24821
  )
24638
24822
  ] }, itemId);
24639
24823
  });
24640
- const panel = isOpen && triggerRect ? /* @__PURE__ */ jsx(
24824
+ const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxs(
24641
24825
  "div",
24642
24826
  {
24643
24827
  ref: menuRef,
24644
24828
  className: cn("fixed z-50", menuContainerStyles, className),
24645
24829
  style: computeMenuStyle(effectivePosition, triggerRect),
24646
24830
  role: "menu",
24647
- children: renderMenuItems(items)
24831
+ children: [
24832
+ header && /* @__PURE__ */ jsx("div", { className: "px-4 py-2 border-b border-border", children: header }),
24833
+ renderMenuItems(items),
24834
+ footer && /* @__PURE__ */ jsx("div", { className: "px-4 py-2 border-t border-border", children: footer })
24835
+ ]
24648
24836
  }
24649
24837
  ) : null;
24650
24838
  return /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -26403,7 +26591,7 @@ var init_FilterGroup = __esm({
26403
26591
  Select,
26404
26592
  {
26405
26593
  value: selectedValues[filter.field] || "all",
26406
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
26594
+ onChange: (v) => handleFilterSelect(filter.field, v),
26407
26595
  options: [
26408
26596
  { value: "all", label: t("filterGroup.all") },
26409
26597
  ...filter.options?.map((opt) => ({
@@ -26486,7 +26674,7 @@ var init_FilterGroup = __esm({
26486
26674
  Select,
26487
26675
  {
26488
26676
  value: selectedValues[filter.field] || "all",
26489
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
26677
+ onChange: (v) => handleFilterSelect(filter.field, v),
26490
26678
  options: [
26491
26679
  { value: "all", label: t("filterGroup.allOf", { label: filter.label }) },
26492
26680
  ...filter.options?.map((opt) => ({
@@ -26604,7 +26792,7 @@ var init_FilterGroup = __esm({
26604
26792
  Select,
26605
26793
  {
26606
26794
  value: selectedValues[filter.field] || "all",
26607
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
26795
+ onChange: (v) => handleFilterSelect(filter.field, v),
26608
26796
  options: [
26609
26797
  { value: "all", label: t("filterGroup.all") },
26610
26798
  ...filter.options?.map((opt) => ({
@@ -30786,13 +30974,13 @@ var init_MapView = __esm({
30786
30974
  shadowSize: [41, 41]
30787
30975
  });
30788
30976
  L.Marker.prototype.options.icon = defaultIcon;
30789
- const { useEffect: useEffect76, useRef: useRef68, useCallback: useCallback118, useState: useState110 } = React88__default;
30977
+ const { useEffect: useEffect77, useRef: useRef69, useCallback: useCallback118, useState: useState111 } = React88__default;
30790
30978
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
30791
30979
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
30792
30980
  function MapUpdater({ centerLat, centerLng, zoom }) {
30793
30981
  const map = useMap();
30794
- const prevRef = useRef68({ centerLat, centerLng, zoom });
30795
- useEffect76(() => {
30982
+ const prevRef = useRef69({ centerLat, centerLng, zoom });
30983
+ useEffect77(() => {
30796
30984
  const prev = prevRef.current;
30797
30985
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
30798
30986
  map.setView([centerLat, centerLng], zoom);
@@ -30803,7 +30991,7 @@ var init_MapView = __esm({
30803
30991
  }
30804
30992
  function MapClickHandler({ onMapClick }) {
30805
30993
  const map = useMap();
30806
- useEffect76(() => {
30994
+ useEffect77(() => {
30807
30995
  if (!onMapClick) return;
30808
30996
  const handler = (e) => {
30809
30997
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -30831,7 +31019,7 @@ var init_MapView = __esm({
30831
31019
  showAttribution = true
30832
31020
  }) {
30833
31021
  const eventBus = useEventBus3();
30834
- const [clickedPosition, setClickedPosition] = useState110(null);
31022
+ const [clickedPosition, setClickedPosition] = useState111(null);
30835
31023
  const handleMapClick = useCallback118((lat, lng) => {
30836
31024
  if (showClickedPin) {
30837
31025
  setClickedPosition({ lat, lng });
@@ -36267,8 +36455,8 @@ var init_VersionDiff = __esm({
36267
36455
  return { added, removed };
36268
36456
  }, [diff]);
36269
36457
  const handleBeforeChange = useCallback(
36270
- (e) => {
36271
- const id = e.target.value;
36458
+ (v) => {
36459
+ const id = v;
36272
36460
  setInternalBefore(id);
36273
36461
  onSelectBefore?.(id);
36274
36462
  if (selectBeforeEvent) eventBus.emit(`UI:${selectBeforeEvent}`, { id });
@@ -36276,8 +36464,8 @@ var init_VersionDiff = __esm({
36276
36464
  [onSelectBefore, selectBeforeEvent, eventBus]
36277
36465
  );
36278
36466
  const handleAfterChange = useCallback(
36279
- (e) => {
36280
- const id = e.target.value;
36467
+ (v) => {
36468
+ const id = v;
36281
36469
  setInternalAfter(id);
36282
36470
  onSelectAfter?.(id);
36283
36471
  if (selectAfterEvent) eventBus.emit(`UI:${selectAfterEvent}`, { id });
@@ -40494,11 +40682,11 @@ function RuleEditor({
40494
40682
  className
40495
40683
  }) {
40496
40684
  const { t } = useTranslate();
40497
- const handleWhenChange = useCallback((e) => {
40498
- onChange({ ...rule, whenEvent: e.target.value });
40685
+ const handleWhenChange = useCallback((v) => {
40686
+ onChange({ ...rule, whenEvent: v });
40499
40687
  }, [rule, onChange]);
40500
- const handleThenChange = useCallback((e) => {
40501
- onChange({ ...rule, thenAction: e.target.value });
40688
+ const handleThenChange = useCallback((v) => {
40689
+ onChange({ ...rule, thenAction: v });
40502
40690
  }, [rule, onChange]);
40503
40691
  return /* @__PURE__ */ jsxs(HStack, { className: cn("items-center p-2 rounded-lg bg-muted/50 border border-border", className), gap: "sm", children: [
40504
40692
  /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-primary font-bold whitespace-nowrap", children: t("eventHandler.when") }),
@@ -41574,7 +41762,7 @@ var init_Form = __esm({
41574
41762
  ...commonProps,
41575
41763
  options,
41576
41764
  value: String(currentValue),
41577
- onChange: (e) => handleChange(fieldName, e.target.value),
41765
+ onChange: (v) => handleChange(fieldName, v),
41578
41766
  placeholder: field.placeholder || `Select ${label}...`
41579
41767
  }
41580
41768
  );
@@ -48361,18 +48549,32 @@ var init_WorldMapTemplate = __esm({
48361
48549
  }
48362
48550
  });
48363
48551
  function lazyThree(name, loader) {
48364
- const Lazy = React88__default.lazy(() => loader().then((m) => ({ default: m[name] })));
48552
+ const Lazy = React88__default.lazy(
48553
+ () => loader().then((m) => {
48554
+ const Resolved = m[name];
48555
+ if (!Resolved) {
48556
+ throw new Error(
48557
+ `[@almadar/ui] 3D component "${name}" was not found in the three subpath bundle.`
48558
+ );
48559
+ }
48560
+ return { default: Resolved };
48561
+ })
48562
+ );
48365
48563
  function ThreeWrapper(props) {
48366
48564
  return React88__default.createElement(
48367
- React88__default.Suspense,
48368
- { fallback: null },
48369
- React88__default.createElement(Lazy, props)
48565
+ ThreeBoundary,
48566
+ { name },
48567
+ React88__default.createElement(
48568
+ React88__default.Suspense,
48569
+ { fallback: null },
48570
+ React88__default.createElement(Lazy, props)
48571
+ )
48370
48572
  );
48371
48573
  }
48372
48574
  ThreeWrapper.displayName = `Lazy(${name})`;
48373
48575
  return ThreeWrapper;
48374
48576
  }
48375
- var FeatureRenderer, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
48577
+ var ThreeBoundary, FeatureRenderer, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
48376
48578
  var init_component_registry_generated = __esm({
48377
48579
  "components/core/organisms/component-registry.generated.ts"() {
48378
48580
  init_AboutPageTemplate();
@@ -48658,6 +48860,28 @@ var init_component_registry_generated = __esm({
48658
48860
  init_WorldMapBoard();
48659
48861
  init_WorldMapTemplate();
48660
48862
  init_XPBar();
48863
+ ThreeBoundary = class extends React88__default.Component {
48864
+ constructor() {
48865
+ super(...arguments);
48866
+ __publicField(this, "state", { failed: false });
48867
+ }
48868
+ static getDerivedStateFromError() {
48869
+ return { failed: true };
48870
+ }
48871
+ render() {
48872
+ if (this.state.failed) {
48873
+ return React88__default.createElement(
48874
+ "div",
48875
+ {
48876
+ "data-testid": "three-unavailable",
48877
+ style: { padding: 16, fontSize: 13, lineHeight: 1.5, opacity: 0.7 }
48878
+ },
48879
+ `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.`
48880
+ );
48881
+ }
48882
+ return this.props.children;
48883
+ }
48884
+ };
48661
48885
  FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/molecules/game/three'));
48662
48886
  GameCanvas3D = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
48663
48887
  GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
@@ -56911,7 +57135,7 @@ function OrbInspector({ node, schema, editable = false, userType = "builder", th
56911
57135
  {
56912
57136
  value: f3.type,
56913
57137
  options: FIELD_TYPE_OPTIONS,
56914
- onChange: (e) => handleUpdateField(f3.name, { type: e.target.value }),
57138
+ onChange: (v) => handleUpdateField(f3.name, { type: v }),
56915
57139
  className: "w-20 text-xs h-6"
56916
57140
  }
56917
57141
  ),