@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.
@@ -7037,6 +7037,8 @@ var init_Input = __esm({
7037
7037
  className,
7038
7038
  inputType,
7039
7039
  type: htmlType,
7040
+ label,
7041
+ helperText,
7040
7042
  error,
7041
7043
  leftIcon,
7042
7044
  rightIcon,
@@ -7092,82 +7094,95 @@ var init_Input = __esm({
7092
7094
  onClear?.();
7093
7095
  }
7094
7096
  };
7097
+ const wrapField = (field) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full", children: [
7098
+ label && /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-foreground mb-1", children: label }),
7099
+ field,
7100
+ (helperText || error) && /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn("mt-1 text-xs", error ? "text-error" : "text-muted-foreground"), children: error ?? helperText })
7101
+ ] });
7095
7102
  if (type === "select") {
7096
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
7097
- 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 }),
7098
- /* @__PURE__ */ jsxRuntime.jsxs(
7099
- "select",
7103
+ return wrapField(
7104
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
7105
+ 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 }),
7106
+ /* @__PURE__ */ jsxRuntime.jsxs(
7107
+ "select",
7108
+ {
7109
+ ref,
7110
+ value,
7111
+ onChange: handleChange,
7112
+ className: cn(baseClassName, "appearance-none pr-10", className),
7113
+ ...props,
7114
+ children: [
7115
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: t("form.selectPlaceholder", { label: "" }) }),
7116
+ options?.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.value, children: opt.label }, opt.value))
7117
+ ]
7118
+ }
7119
+ ),
7120
+ /* @__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" }) })
7121
+ ] })
7122
+ );
7123
+ }
7124
+ if (type === "textarea") {
7125
+ return wrapField(
7126
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
7127
+ "textarea",
7100
7128
  {
7101
7129
  ref,
7102
7130
  value,
7103
7131
  onChange: handleChange,
7104
- className: cn(baseClassName, "appearance-none pr-10", className),
7105
- ...props,
7106
- children: [
7107
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: t("form.selectPlaceholder", { label: "" }) }),
7108
- options?.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.value, children: opt.label }, opt.value))
7109
- ]
7132
+ rows: rows2,
7133
+ className: baseClassName,
7134
+ ...props
7110
7135
  }
7111
- ),
7112
- /* @__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" }) })
7113
- ] });
7114
- }
7115
- if (type === "textarea") {
7116
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
7117
- "textarea",
7118
- {
7119
- ref,
7120
- value,
7121
- onChange: handleChange,
7122
- rows: rows2,
7123
- className: baseClassName,
7124
- ...props
7125
- }
7126
- ) });
7136
+ ) })
7137
+ );
7127
7138
  }
7128
7139
  if (type === "checkbox") {
7129
- return /* @__PURE__ */ jsxRuntime.jsx(
7130
- "input",
7131
- {
7132
- ref,
7133
- type: "checkbox",
7134
- checked: props.checked,
7135
- onChange: handleChange,
7136
- className: cn(
7137
- "h-icon-default w-icon-default rounded-sm",
7138
- "border-border",
7139
- "text-primary focus:ring-ring",
7140
- "disabled:opacity-50 disabled:cursor-not-allowed",
7141
- className
7142
- ),
7143
- ...props
7144
- }
7140
+ return wrapField(
7141
+ /* @__PURE__ */ jsxRuntime.jsx(
7142
+ "input",
7143
+ {
7144
+ ref,
7145
+ type: "checkbox",
7146
+ checked: props.checked,
7147
+ onChange: handleChange,
7148
+ className: cn(
7149
+ "h-icon-default w-icon-default rounded-sm",
7150
+ "border-border",
7151
+ "text-primary focus:ring-ring",
7152
+ "disabled:opacity-50 disabled:cursor-not-allowed",
7153
+ className
7154
+ ),
7155
+ ...props
7156
+ }
7157
+ )
7145
7158
  );
7146
7159
  }
7147
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
7148
- 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 }),
7149
- /* @__PURE__ */ jsxRuntime.jsx(
7150
- "input",
7151
- {
7152
- ref,
7153
- type,
7154
- value,
7155
- onChange: handleChange,
7156
- className: baseClassName,
7157
- ...props
7158
- }
7159
- ),
7160
- showClearButton && /* @__PURE__ */ jsxRuntime.jsx(
7161
- "button",
7162
- {
7163
- type: "button",
7164
- onClick: handleClear,
7165
- className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground hover:text-foreground",
7166
- children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
7167
- }
7168
- ),
7169
- 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) })
7170
- ] });
7160
+ return wrapField(
7161
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
7162
+ 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 }),
7163
+ /* @__PURE__ */ jsxRuntime.jsx(
7164
+ "input",
7165
+ {
7166
+ ref,
7167
+ type,
7168
+ value,
7169
+ onChange: handleChange,
7170
+ className: baseClassName,
7171
+ ...props
7172
+ }
7173
+ ),
7174
+ showClearButton && /* @__PURE__ */ jsxRuntime.jsx(
7175
+ "button",
7176
+ {
7177
+ type: "button",
7178
+ onClick: handleClear,
7179
+ className: "absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground hover:text-foreground",
7180
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
7181
+ }
7182
+ ),
7183
+ 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) })
7184
+ ] })
7185
+ );
7171
7186
  }
7172
7187
  );
7173
7188
  Input.displayName = "Input";
@@ -7238,6 +7253,190 @@ var init_Textarea = __esm({
7238
7253
  Textarea.displayName = "Textarea";
7239
7254
  }
7240
7255
  });
7256
+ function flatOptions(opts, groups) {
7257
+ const flat = opts ?? [];
7258
+ const grp = (groups ?? []).flatMap((g) => g.options);
7259
+ return [...flat, ...grp];
7260
+ }
7261
+ function NativeSelect({
7262
+ className,
7263
+ options,
7264
+ groups,
7265
+ placeholder,
7266
+ error,
7267
+ onChange,
7268
+ value,
7269
+ ...props
7270
+ }) {
7271
+ const eventBus = useEventBus();
7272
+ const handleChange = (e) => {
7273
+ if (typeof onChange === "string") {
7274
+ eventBus.emit(`UI:${onChange}`, { value: e.target.value });
7275
+ } else {
7276
+ onChange?.(e.target.value);
7277
+ }
7278
+ };
7279
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
7280
+ /* @__PURE__ */ jsxRuntime.jsxs(
7281
+ "select",
7282
+ {
7283
+ onChange: handleChange,
7284
+ value,
7285
+ className: cn(
7286
+ "block w-full border-[length:var(--border-width)] shadow-sm appearance-none",
7287
+ "px-3 py-2 pr-10 text-sm text-foreground font-medium",
7288
+ "bg-card",
7289
+ "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
7290
+ "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
7291
+ error ? "border-error focus:border-error" : "border-border focus:border-primary",
7292
+ className
7293
+ ),
7294
+ ...props,
7295
+ children: [
7296
+ placeholder && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: placeholder }),
7297
+ options?.map((option) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)),
7298
+ 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))
7299
+ ]
7300
+ }
7301
+ ),
7302
+ /* @__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" }) })
7303
+ ] });
7304
+ }
7305
+ function RichSelect({
7306
+ className,
7307
+ options,
7308
+ groups,
7309
+ placeholder,
7310
+ error,
7311
+ onChange,
7312
+ value,
7313
+ multiple,
7314
+ searchable,
7315
+ clearable,
7316
+ disabled
7317
+ }) {
7318
+ const eventBus = useEventBus();
7319
+ const [open, setOpen] = React88.useState(false);
7320
+ const [search, setSearch] = React88.useState("");
7321
+ const containerRef = React88.useRef(null);
7322
+ const selected = multiple ? Array.isArray(value) ? value : value ? [value] : [] : value ? [value] : [];
7323
+ const all = flatOptions(options, groups);
7324
+ const filtered = searchable && search ? all.filter((o) => o.label.toLowerCase().includes(search.toLowerCase())) : all;
7325
+ const toggle = (optValue) => {
7326
+ let next;
7327
+ if (multiple) {
7328
+ next = selected.includes(optValue) ? selected.filter((v) => v !== optValue) : [...selected, optValue];
7329
+ } else {
7330
+ next = optValue;
7331
+ setOpen(false);
7332
+ }
7333
+ if (typeof onChange === "string") {
7334
+ eventBus.emit(`UI:${onChange}`, { value: next });
7335
+ } else {
7336
+ onChange?.(next);
7337
+ }
7338
+ };
7339
+ const clear = (e) => {
7340
+ e.stopPropagation();
7341
+ const next = multiple ? [] : "";
7342
+ if (typeof onChange === "string") {
7343
+ eventBus.emit(`UI:${onChange}`, { value: next });
7344
+ } else {
7345
+ onChange?.(next);
7346
+ }
7347
+ };
7348
+ React88.useEffect(() => {
7349
+ const handler = (e) => {
7350
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
7351
+ setOpen(false);
7352
+ setSearch("");
7353
+ }
7354
+ };
7355
+ document.addEventListener("mousedown", handler);
7356
+ return () => document.removeEventListener("mousedown", handler);
7357
+ }, []);
7358
+ const displayLabel = selected.length === 0 ? placeholder ?? "" : multiple ? `${selected.length} selected` : all.find((o) => o.value === selected[0])?.label ?? selected[0];
7359
+ const hasValue = selected.length > 0;
7360
+ const renderOptions = (opts) => opts.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
7361
+ "button",
7362
+ {
7363
+ type: "button",
7364
+ disabled: opt.disabled,
7365
+ onClick: () => !opt.disabled && toggle(opt.value),
7366
+ className: cn(
7367
+ "w-full flex items-center justify-between px-3 py-1.5 text-sm text-start",
7368
+ "hover:bg-muted transition-colors",
7369
+ "disabled:opacity-50 disabled:cursor-not-allowed",
7370
+ selected.includes(opt.value) && "text-primary font-medium"
7371
+ ),
7372
+ children: [
7373
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: opt.label }),
7374
+ selected.includes(opt.value) && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "check", className: "h-icon-default w-icon-default" })
7375
+ ]
7376
+ },
7377
+ opt.value
7378
+ ));
7379
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, className: cn("relative w-full", className), children: [
7380
+ /* @__PURE__ */ jsxRuntime.jsx(
7381
+ "button",
7382
+ {
7383
+ type: "button",
7384
+ disabled,
7385
+ onClick: () => !disabled && setOpen((o) => !o),
7386
+ className: cn(
7387
+ "block w-full border-[length:var(--border-width)] shadow-sm",
7388
+ "px-3 py-2 pr-10 text-sm text-start font-medium",
7389
+ "bg-card rounded-sm",
7390
+ "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
7391
+ "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
7392
+ error ? "border-error focus:border-error" : "border-border focus:border-primary",
7393
+ !hasValue && "text-muted-foreground"
7394
+ ),
7395
+ children: displayLabel
7396
+ }
7397
+ ),
7398
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center gap-1 pointer-events-none", children: [
7399
+ clearable && hasValue && /* @__PURE__ */ jsxRuntime.jsx(
7400
+ "button",
7401
+ {
7402
+ type: "button",
7403
+ onClick: clear,
7404
+ className: "pointer-events-auto text-muted-foreground hover:text-foreground",
7405
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "x", className: "h-icon-default w-icon-default" })
7406
+ }
7407
+ ),
7408
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "chevron-down", className: "h-icon-default w-icon-default text-foreground" })
7409
+ ] }),
7410
+ open && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(
7411
+ "absolute z-50 mt-1 w-full",
7412
+ "bg-card border-[length:var(--border-width)] border-border",
7413
+ "rounded-sm shadow-elevation-popover py-1 max-h-60 overflow-y-auto"
7414
+ ), children: [
7415
+ searchable && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 pb-1 border-b border-border", children: /* @__PURE__ */ jsxRuntime.jsx(
7416
+ "input",
7417
+ {
7418
+ autoFocus: true,
7419
+ type: "text",
7420
+ value: search,
7421
+ onChange: (e) => setSearch(e.target.value),
7422
+ placeholder: "Search\u2026",
7423
+ className: cn(
7424
+ "w-full px-2 py-1 text-sm bg-transparent",
7425
+ "focus:outline-none text-foreground placeholder:text-muted-foreground"
7426
+ )
7427
+ }
7428
+ ) }),
7429
+ groups && groups.length > 0 ? groups.map((g) => {
7430
+ const groupFiltered = searchable && search ? g.options.filter((o) => o.label.toLowerCase().includes(search.toLowerCase())) : g.options;
7431
+ if (groupFiltered.length === 0) return null;
7432
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
7433
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-1 text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: g.label }),
7434
+ renderOptions(groupFiltered)
7435
+ ] }, g.label);
7436
+ }) : renderOptions(filtered)
7437
+ ] })
7438
+ ] });
7439
+ }
7241
7440
  var Select;
7242
7441
  var init_Select = __esm({
7243
7442
  "components/core/atoms/Select.tsx"() {
@@ -7245,47 +7444,12 @@ var init_Select = __esm({
7245
7444
  init_Icon();
7246
7445
  init_useEventBus();
7247
7446
  Select = React88__namespace.default.forwardRef(
7248
- ({ className, options, placeholder, error, onChange, ...props }, ref) => {
7249
- const eventBus = useEventBus();
7250
- const handleChange = (e) => {
7251
- if (typeof onChange === "string") {
7252
- eventBus.emit(`UI:${onChange}`, { value: e.target.value });
7253
- } else {
7254
- onChange?.(e);
7255
- }
7256
- };
7257
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
7258
- /* @__PURE__ */ jsxRuntime.jsxs(
7259
- "select",
7260
- {
7261
- ref,
7262
- onChange: handleChange,
7263
- className: cn(
7264
- "block w-full border-[length:var(--border-width)] shadow-sm appearance-none",
7265
- "px-3 py-2 pr-10 text-sm text-foreground font-medium",
7266
- "bg-card",
7267
- "focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-ring",
7268
- "disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed",
7269
- error ? "border-error focus:border-error" : "border-border focus:border-primary",
7270
- className
7271
- ),
7272
- ...props,
7273
- children: [
7274
- placeholder && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: placeholder }),
7275
- options.map((option) => /* @__PURE__ */ jsxRuntime.jsx(
7276
- "option",
7277
- {
7278
- value: option.value,
7279
- disabled: option.disabled,
7280
- children: option.label
7281
- },
7282
- option.value
7283
- ))
7284
- ]
7285
- }
7286
- ),
7287
- /* @__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" }) })
7288
- ] });
7447
+ (props, _ref) => {
7448
+ const { multiple, searchable, clearable } = props;
7449
+ if (multiple || searchable || clearable) {
7450
+ return /* @__PURE__ */ jsxRuntime.jsx(RichSelect, { ...props });
7451
+ }
7452
+ return /* @__PURE__ */ jsxRuntime.jsx(NativeSelect, { ...props });
7289
7453
  }
7290
7454
  );
7291
7455
  Select.displayName = "Select";
@@ -7339,11 +7503,54 @@ var init_Checkbox = __esm({
7339
7503
  Checkbox.displayName = "Checkbox";
7340
7504
  }
7341
7505
  });
7506
+ var sizeStyles3, Spinner;
7507
+ var init_Spinner = __esm({
7508
+ "components/core/atoms/Spinner.tsx"() {
7509
+ init_cn();
7510
+ init_Icon();
7511
+ sizeStyles3 = {
7512
+ xs: "h-3 w-3",
7513
+ sm: "h-icon-default w-icon-default",
7514
+ md: "h-6 w-6",
7515
+ lg: "h-8 w-8"
7516
+ };
7517
+ Spinner = React88__namespace.default.forwardRef(
7518
+ ({ className, size = "md", overlay, ...props }, ref) => {
7519
+ if (overlay) {
7520
+ return /* @__PURE__ */ jsxRuntime.jsx(
7521
+ "div",
7522
+ {
7523
+ ref,
7524
+ className: cn(
7525
+ "absolute inset-0 z-10 flex items-center justify-center",
7526
+ "bg-background/60 backdrop-blur-sm",
7527
+ className
7528
+ ),
7529
+ ...props,
7530
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "loader", className: cn("animate-spin text-foreground", sizeStyles3[size]) })
7531
+ }
7532
+ );
7533
+ }
7534
+ return /* @__PURE__ */ jsxRuntime.jsx(
7535
+ "div",
7536
+ {
7537
+ ref,
7538
+ className: cn("text-foreground", className),
7539
+ ...props,
7540
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "loader", className: cn("animate-spin", sizeStyles3[size]) })
7541
+ }
7542
+ );
7543
+ }
7544
+ );
7545
+ Spinner.displayName = "Spinner";
7546
+ }
7547
+ });
7342
7548
  var variantStyles4, paddingStyles2, shadowStyles2, lookStyles2, Card, CardHeader, CardTitle, CardContent, CardBody, CardFooter;
7343
7549
  var init_Card = __esm({
7344
7550
  "components/core/atoms/Card.tsx"() {
7345
7551
  init_cn();
7346
7552
  init_useEventBus();
7553
+ init_Spinner();
7347
7554
  variantStyles4 = {
7348
7555
  default: [
7349
7556
  "bg-card",
@@ -7408,6 +7615,7 @@ var init_Card = __esm({
7408
7615
  look = "elevated",
7409
7616
  children,
7410
7617
  action,
7618
+ loading,
7411
7619
  onClick,
7412
7620
  ...props
7413
7621
  }, ref) => {
@@ -7421,7 +7629,7 @@ var init_Card = __esm({
7421
7629
  {
7422
7630
  ref,
7423
7631
  className: cn(
7424
- "rounded-container",
7632
+ "rounded-container relative",
7425
7633
  "transition-all duration-[var(--transition-normal)]",
7426
7634
  variantStyles4[variant],
7427
7635
  paddingStyles2[padding],
@@ -7432,6 +7640,7 @@ var init_Card = __esm({
7432
7640
  onClick: handleClick,
7433
7641
  ...props,
7434
7642
  children: [
7643
+ loading && /* @__PURE__ */ jsxRuntime.jsx(Spinner, { overlay: true, size: "md" }),
7435
7644
  (title || subtitle) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-4", children: [
7436
7645
  title && /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg text-card-foreground font-bold", children: title }),
7437
7646
  subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mt-1", children: subtitle })
@@ -7473,7 +7682,7 @@ var init_Card = __esm({
7473
7682
  CardFooter.displayName = "CardFooter";
7474
7683
  }
7475
7684
  });
7476
- var variantStyles5, sizeStyles3, iconSizes, FilterPill;
7685
+ var variantStyles5, sizeStyles4, iconSizes, FilterPill;
7477
7686
  var init_FilterPill = __esm({
7478
7687
  "components/core/atoms/FilterPill.tsx"() {
7479
7688
  init_cn();
@@ -7507,7 +7716,7 @@ var init_FilterPill = __esm({
7507
7716
  "border-[length:var(--border-width-thin)] border-border"
7508
7717
  ].join(" ")
7509
7718
  };
7510
- sizeStyles3 = {
7719
+ sizeStyles4 = {
7511
7720
  sm: "px-2 py-0.5 text-xs",
7512
7721
  md: "px-2.5 py-1 text-sm",
7513
7722
  lg: "px-3 py-1.5 text-base"
@@ -7551,7 +7760,7 @@ var init_FilterPill = __esm({
7551
7760
  className: cn(
7552
7761
  "inline-flex items-center gap-1 font-bold rounded-pill",
7553
7762
  variantStyles5[variant],
7554
- sizeStyles3[size],
7763
+ sizeStyles4[size],
7555
7764
  (onClick || clickEvent) && "cursor-pointer",
7556
7765
  className
7557
7766
  ),
@@ -7583,33 +7792,6 @@ var init_FilterPill = __esm({
7583
7792
  FilterPill.displayName = "FilterPill";
7584
7793
  }
7585
7794
  });
7586
- var sizeStyles4, Spinner;
7587
- var init_Spinner = __esm({
7588
- "components/core/atoms/Spinner.tsx"() {
7589
- init_cn();
7590
- init_Icon();
7591
- sizeStyles4 = {
7592
- xs: "h-3 w-3",
7593
- sm: "h-icon-default w-icon-default",
7594
- md: "h-6 w-6",
7595
- lg: "h-8 w-8"
7596
- };
7597
- Spinner = React88__namespace.default.forwardRef(
7598
- ({ className, size = "md", ...props }, ref) => {
7599
- return /* @__PURE__ */ jsxRuntime.jsx(
7600
- "div",
7601
- {
7602
- ref,
7603
- className: cn("text-foreground", className),
7604
- ...props,
7605
- children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "loader", className: cn("animate-spin", sizeStyles4[size]) })
7606
- }
7607
- );
7608
- }
7609
- );
7610
- Spinner.displayName = "Spinner";
7611
- }
7612
- });
7613
7795
  function generateInitials(name) {
7614
7796
  const parts = name.trim().split(/\s+/);
7615
7797
  if (parts.length === 1) {
@@ -18992,30 +19174,30 @@ var init_BranchingLogicBuilder = __esm({
18992
19174
  if (!sourceQuestion?.optionValues) return [];
18993
19175
  return sourceQuestion.optionValues.map((v) => ({ value: v, label: v }));
18994
19176
  }, [sourceQuestion]);
18995
- const handleSource = (e) => {
18996
- onChange({ ...rule, sourceQuestionId: e.target.value });
19177
+ const handleSource = (v) => {
19178
+ onChange({ ...rule, sourceQuestionId: v });
18997
19179
  };
18998
- const handleOperator = (e) => {
18999
- const next = e.target.value;
19180
+ const handleOperator = (v) => {
19181
+ const next = v;
19000
19182
  const nextValue = next === "in" && !Array.isArray(rule.value) ? rule.value ? [rule.value] : [] : next !== "in" && Array.isArray(rule.value) ? rule.value[0] ?? "" : rule.value;
19001
19183
  onChange({ ...rule, operator: next, value: nextValue });
19002
19184
  };
19003
19185
  const handleScalarValue = (e) => {
19004
19186
  onChange({ ...rule, value: e.target.value });
19005
19187
  };
19006
- const handleAddChip = (e) => {
19007
- const v = e.target.value;
19008
- if (!v) return;
19188
+ const handleAddChip = (v) => {
19189
+ const val = v;
19190
+ if (!val) return;
19009
19191
  const current = Array.isArray(rule.value) ? rule.value : [];
19010
- if (current.includes(v)) return;
19011
- onChange({ ...rule, value: [...current, v] });
19192
+ if (current.includes(val)) return;
19193
+ onChange({ ...rule, value: [...current, val] });
19012
19194
  };
19013
19195
  const handleRemoveChip = (chip) => {
19014
19196
  const current = Array.isArray(rule.value) ? rule.value : [];
19015
19197
  onChange({ ...rule, value: current.filter((c) => c !== chip) });
19016
19198
  };
19017
- const handleTarget = (e) => {
19018
- onChange({ ...rule, targetQuestionId: e.target.value });
19199
+ const handleTarget = (v) => {
19200
+ onChange({ ...rule, targetQuestionId: v });
19019
19201
  };
19020
19202
  const isMulti = rule.operator === "in";
19021
19203
  const chips = Array.isArray(rule.value) ? rule.value : [];
@@ -19096,7 +19278,7 @@ var init_BranchingLogicBuilder = __esm({
19096
19278
  options: valueOptions,
19097
19279
  value: scalarValue,
19098
19280
  placeholder: t("branchingLogic.selectValue"),
19099
- onChange: (e) => onChange({ ...rule, value: e.target.value }),
19281
+ onChange: (v) => onChange({ ...rule, value: v }),
19100
19282
  disabled: readOnly
19101
19283
  }
19102
19284
  ) : /* @__PURE__ */ jsxRuntime.jsx(
@@ -21528,7 +21710,7 @@ var init_Pagination = __esm({
21528
21710
  Select,
21529
21711
  {
21530
21712
  value: String(pageSize),
21531
- onChange: (e) => handlePageSizeChange(Number(e.target.value)),
21713
+ onChange: (v) => handlePageSizeChange(Number(v)),
21532
21714
  options: pageSizeOptions.map((size) => ({
21533
21715
  value: String(size),
21534
21716
  label: String(size)
@@ -24554,7 +24736,9 @@ var init_Menu = __esm({
24554
24736
  trigger,
24555
24737
  items,
24556
24738
  position = "bottom-left",
24557
- className
24739
+ className,
24740
+ header,
24741
+ footer
24558
24742
  }) => {
24559
24743
  const eventBus = useEventBus();
24560
24744
  const { direction } = hooks.useTranslate();
@@ -24686,14 +24870,18 @@ var init_Menu = __esm({
24686
24870
  )
24687
24871
  ] }, itemId);
24688
24872
  });
24689
- const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxRuntime.jsx(
24873
+ const panel = isOpen && triggerRect ? /* @__PURE__ */ jsxRuntime.jsxs(
24690
24874
  "div",
24691
24875
  {
24692
24876
  ref: menuRef,
24693
24877
  className: cn("fixed z-50", menuContainerStyles, className),
24694
24878
  style: computeMenuStyle(effectivePosition, triggerRect),
24695
24879
  role: "menu",
24696
- children: renderMenuItems(items)
24880
+ children: [
24881
+ header && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-2 border-b border-border", children: header }),
24882
+ renderMenuItems(items),
24883
+ footer && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-2 border-t border-border", children: footer })
24884
+ ]
24697
24885
  }
24698
24886
  ) : null;
24699
24887
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -26452,7 +26640,7 @@ var init_FilterGroup = __esm({
26452
26640
  Select,
26453
26641
  {
26454
26642
  value: selectedValues[filter.field] || "all",
26455
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
26643
+ onChange: (v) => handleFilterSelect(filter.field, v),
26456
26644
  options: [
26457
26645
  { value: "all", label: t("filterGroup.all") },
26458
26646
  ...filter.options?.map((opt) => ({
@@ -26535,7 +26723,7 @@ var init_FilterGroup = __esm({
26535
26723
  Select,
26536
26724
  {
26537
26725
  value: selectedValues[filter.field] || "all",
26538
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
26726
+ onChange: (v) => handleFilterSelect(filter.field, v),
26539
26727
  options: [
26540
26728
  { value: "all", label: t("filterGroup.allOf", { label: filter.label }) },
26541
26729
  ...filter.options?.map((opt) => ({
@@ -26653,7 +26841,7 @@ var init_FilterGroup = __esm({
26653
26841
  Select,
26654
26842
  {
26655
26843
  value: selectedValues[filter.field] || "all",
26656
- onChange: (e) => handleFilterSelect(filter.field, e.target.value),
26844
+ onChange: (v) => handleFilterSelect(filter.field, v),
26657
26845
  options: [
26658
26846
  { value: "all", label: t("filterGroup.all") },
26659
26847
  ...filter.options?.map((opt) => ({
@@ -30835,13 +31023,13 @@ var init_MapView = __esm({
30835
31023
  shadowSize: [41, 41]
30836
31024
  });
30837
31025
  L.Marker.prototype.options.icon = defaultIcon;
30838
- const { useEffect: useEffect76, useRef: useRef68, useCallback: useCallback118, useState: useState110 } = React88__namespace.default;
31026
+ const { useEffect: useEffect77, useRef: useRef69, useCallback: useCallback118, useState: useState111 } = React88__namespace.default;
30839
31027
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
30840
31028
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
30841
31029
  function MapUpdater({ centerLat, centerLng, zoom }) {
30842
31030
  const map = useMap();
30843
- const prevRef = useRef68({ centerLat, centerLng, zoom });
30844
- useEffect76(() => {
31031
+ const prevRef = useRef69({ centerLat, centerLng, zoom });
31032
+ useEffect77(() => {
30845
31033
  const prev = prevRef.current;
30846
31034
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
30847
31035
  map.setView([centerLat, centerLng], zoom);
@@ -30852,7 +31040,7 @@ var init_MapView = __esm({
30852
31040
  }
30853
31041
  function MapClickHandler({ onMapClick }) {
30854
31042
  const map = useMap();
30855
- useEffect76(() => {
31043
+ useEffect77(() => {
30856
31044
  if (!onMapClick) return;
30857
31045
  const handler = (e) => {
30858
31046
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -30880,7 +31068,7 @@ var init_MapView = __esm({
30880
31068
  showAttribution = true
30881
31069
  }) {
30882
31070
  const eventBus = useEventBus3();
30883
- const [clickedPosition, setClickedPosition] = useState110(null);
31071
+ const [clickedPosition, setClickedPosition] = useState111(null);
30884
31072
  const handleMapClick = useCallback118((lat, lng) => {
30885
31073
  if (showClickedPin) {
30886
31074
  setClickedPosition({ lat, lng });
@@ -36316,8 +36504,8 @@ var init_VersionDiff = __esm({
36316
36504
  return { added, removed };
36317
36505
  }, [diff]);
36318
36506
  const handleBeforeChange = React88.useCallback(
36319
- (e) => {
36320
- const id = e.target.value;
36507
+ (v) => {
36508
+ const id = v;
36321
36509
  setInternalBefore(id);
36322
36510
  onSelectBefore?.(id);
36323
36511
  if (selectBeforeEvent) eventBus.emit(`UI:${selectBeforeEvent}`, { id });
@@ -36325,8 +36513,8 @@ var init_VersionDiff = __esm({
36325
36513
  [onSelectBefore, selectBeforeEvent, eventBus]
36326
36514
  );
36327
36515
  const handleAfterChange = React88.useCallback(
36328
- (e) => {
36329
- const id = e.target.value;
36516
+ (v) => {
36517
+ const id = v;
36330
36518
  setInternalAfter(id);
36331
36519
  onSelectAfter?.(id);
36332
36520
  if (selectAfterEvent) eventBus.emit(`UI:${selectAfterEvent}`, { id });
@@ -40543,11 +40731,11 @@ function RuleEditor({
40543
40731
  className
40544
40732
  }) {
40545
40733
  const { t } = hooks.useTranslate();
40546
- const handleWhenChange = React88.useCallback((e) => {
40547
- onChange({ ...rule, whenEvent: e.target.value });
40734
+ const handleWhenChange = React88.useCallback((v) => {
40735
+ onChange({ ...rule, whenEvent: v });
40548
40736
  }, [rule, onChange]);
40549
- const handleThenChange = React88.useCallback((e) => {
40550
- onChange({ ...rule, thenAction: e.target.value });
40737
+ const handleThenChange = React88.useCallback((v) => {
40738
+ onChange({ ...rule, thenAction: v });
40551
40739
  }, [rule, onChange]);
40552
40740
  return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: cn("items-center p-2 rounded-lg bg-muted/50 border border-border", className), gap: "sm", children: [
40553
40741
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-primary font-bold whitespace-nowrap", children: t("eventHandler.when") }),
@@ -41623,7 +41811,7 @@ var init_Form = __esm({
41623
41811
  ...commonProps,
41624
41812
  options,
41625
41813
  value: String(currentValue),
41626
- onChange: (e) => handleChange(fieldName, e.target.value),
41814
+ onChange: (v) => handleChange(fieldName, v),
41627
41815
  placeholder: field.placeholder || `Select ${label}...`
41628
41816
  }
41629
41817
  );
@@ -48410,18 +48598,32 @@ var init_WorldMapTemplate = __esm({
48410
48598
  }
48411
48599
  });
48412
48600
  function lazyThree(name, loader) {
48413
- const Lazy = React88__namespace.default.lazy(() => loader().then((m) => ({ default: m[name] })));
48601
+ const Lazy = React88__namespace.default.lazy(
48602
+ () => loader().then((m) => {
48603
+ const Resolved = m[name];
48604
+ if (!Resolved) {
48605
+ throw new Error(
48606
+ `[@almadar/ui] 3D component "${name}" was not found in the three subpath bundle.`
48607
+ );
48608
+ }
48609
+ return { default: Resolved };
48610
+ })
48611
+ );
48414
48612
  function ThreeWrapper(props) {
48415
48613
  return React88__namespace.default.createElement(
48416
- React88__namespace.default.Suspense,
48417
- { fallback: null },
48418
- React88__namespace.default.createElement(Lazy, props)
48614
+ ThreeBoundary,
48615
+ { name },
48616
+ React88__namespace.default.createElement(
48617
+ React88__namespace.default.Suspense,
48618
+ { fallback: null },
48619
+ React88__namespace.default.createElement(Lazy, props)
48620
+ )
48419
48621
  );
48420
48622
  }
48421
48623
  ThreeWrapper.displayName = `Lazy(${name})`;
48422
48624
  return ThreeWrapper;
48423
48625
  }
48424
- var FeatureRenderer, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
48626
+ var ThreeBoundary, FeatureRenderer, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
48425
48627
  var init_component_registry_generated = __esm({
48426
48628
  "components/core/organisms/component-registry.generated.ts"() {
48427
48629
  init_AboutPageTemplate();
@@ -48707,6 +48909,28 @@ var init_component_registry_generated = __esm({
48707
48909
  init_WorldMapBoard();
48708
48910
  init_WorldMapTemplate();
48709
48911
  init_XPBar();
48912
+ ThreeBoundary = class extends React88__namespace.default.Component {
48913
+ constructor() {
48914
+ super(...arguments);
48915
+ __publicField(this, "state", { failed: false });
48916
+ }
48917
+ static getDerivedStateFromError() {
48918
+ return { failed: true };
48919
+ }
48920
+ render() {
48921
+ if (this.state.failed) {
48922
+ return React88__namespace.default.createElement(
48923
+ "div",
48924
+ {
48925
+ "data-testid": "three-unavailable",
48926
+ style: { padding: 16, fontSize: 13, lineHeight: 1.5, opacity: 0.7 }
48927
+ },
48928
+ `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.`
48929
+ );
48930
+ }
48931
+ return this.props.children;
48932
+ }
48933
+ };
48710
48934
  FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/molecules/game/three'));
48711
48935
  GameCanvas3D = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
48712
48936
  GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
@@ -56960,7 +57184,7 @@ function OrbInspector({ node, schema, editable = false, userType = "builder", th
56960
57184
  {
56961
57185
  value: f3.type,
56962
57186
  options: FIELD_TYPE_OPTIONS,
56963
- onChange: (e) => handleUpdateField(f3.name, { type: e.target.value }),
57187
+ onChange: (v) => handleUpdateField(f3.name, { type: v }),
56964
57188
  className: "w-20 text-xs h-6"
56965
57189
  }
56966
57190
  ),