@geomak/ui 5.1.0 → 5.3.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/index.js CHANGED
@@ -14,6 +14,7 @@ import * as ContextMenuPrimitive from '@radix-ui/react-context-menu';
14
14
  import * as Popover from '@radix-ui/react-popover';
15
15
  import * as SwitchPrimitive from '@radix-ui/react-switch';
16
16
  import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
17
+ import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
17
18
 
18
19
  var Moon = ({ color = "gray" }) => /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", fill: color, viewBox: "0 0 24 24", strokeWidth: 1.5, stroke: color, className: "w-8 h-8", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M21.752 15.002A9.718 9.718 0 0118 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 003 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 009.002-5.998z" }) });
19
20
  var Sun = ({ color = "yellow" }) => /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", fill: color, viewBox: "0 0 24 24", strokeWidth: 1.5, stroke: color, className: "w-8 h-8", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" }) });
@@ -2132,46 +2133,112 @@ function Wizard({
2132
2133
  ] }) })
2133
2134
  ] });
2134
2135
  }
2135
- var SearchInput = React8.forwardRef(function SearchInput2({
2136
- value,
2137
- onChange,
2138
- disabled,
2136
+ var FIELD_SIZE = {
2137
+ sm: { control: "h-control-sm", text: "text-xs", padX: "px-2.5", gap: "gap-1.5" },
2138
+ md: { control: "h-control-md", text: "text-sm", padX: "px-3", gap: "gap-2" },
2139
+ lg: { control: "h-control-lg", text: "text-sm", padX: "px-3.5", gap: "gap-2.5" }
2140
+ };
2141
+ var FOCUS_WITHIN = "focus-within:outline-none focus-within:border-accent focus-within:ring-[3px] focus-within:ring-focus-ring";
2142
+ var FOCUS_VISIBLE = "focus-visible:outline-none focus-visible:border-accent focus-visible:ring-[3px] focus-visible:ring-focus-ring";
2143
+ var FOCUS_WITHIN_ERROR = "focus-within:border-status-error focus-within:ring-focus-ring-error";
2144
+ var FOCUS_VISIBLE_ERROR = "focus-visible:border-status-error focus-visible:ring-focus-ring-error";
2145
+ function fieldShell({
2146
+ size = "md",
2147
+ hasError = false,
2148
+ disabled = false,
2149
+ focusWithin = false,
2150
+ sized = true
2151
+ } = {}) {
2152
+ const s = FIELD_SIZE[size];
2153
+ return [
2154
+ "w-full rounded-lg border bg-surface text-foreground",
2155
+ "transition-[color,box-shadow,border-color] duration-150",
2156
+ s.text,
2157
+ sized ? `${s.control} ${s.padX}` : "",
2158
+ // resting border
2159
+ hasError ? "border-status-error" : "border-border",
2160
+ // hover (only when interactive + no error)
2161
+ disabled ? "bg-surface-raised text-foreground-muted cursor-not-allowed" : hasError ? "" : "hover:border-border-strong",
2162
+ // focus
2163
+ focusWithin ? FOCUS_WITHIN : FOCUS_VISIBLE,
2164
+ hasError ? focusWithin ? FOCUS_WITHIN_ERROR : FOCUS_VISIBLE_ERROR : "",
2165
+ // placeholder colour for native inputs
2166
+ "placeholder:text-foreground-muted"
2167
+ ].filter(Boolean).join(" ");
2168
+ }
2169
+ function Field({
2139
2170
  label,
2140
2171
  htmlFor,
2141
- placeholder,
2142
- name,
2143
- inputStyle,
2144
- style,
2145
- layout = "vertical"
2146
- }, ref) {
2147
- return /* @__PURE__ */ jsx("div", { className: "relative flex flex-col items-center justify-center", children: /* @__PURE__ */ jsxs(
2172
+ errorId,
2173
+ errorMessage,
2174
+ layout = "vertical",
2175
+ required,
2176
+ labelStyle,
2177
+ labelWidth,
2178
+ className = "",
2179
+ children
2180
+ }) {
2181
+ const hasError = errorMessage != null;
2182
+ const horizontal = layout === "horizontal";
2183
+ return /* @__PURE__ */ jsxs(
2148
2184
  "div",
2149
2185
  {
2150
- className: `flex ${layout === "vertical" ? "flex-col" : "flex-row items-center gap-2"}`,
2151
- style: style ?? {},
2186
+ className: [
2187
+ "flex",
2188
+ horizontal ? "flex-row items-start gap-3" : "flex-col gap-1.5",
2189
+ className
2190
+ ].filter(Boolean).join(" "),
2152
2191
  children: [
2153
- label && /* @__PURE__ */ jsx("label", { className: "text-sm font-medium ml-1 max-content text-foreground", htmlFor, children: label }),
2154
- /* @__PURE__ */ jsxs("div", { className: "bg-surface text-foreground flex items-center gap-1 rounded-lg border border-border pr-2 focus-within:border-transparent focus-within:ring-2 focus-within:ring-accent transition-colors", children: [
2155
- /* @__PURE__ */ jsx(
2156
- "input",
2157
- {
2158
- ref,
2159
- disabled,
2160
- value,
2161
- onChange,
2162
- type: "search",
2163
- enterKeyHint: "search",
2164
- name,
2165
- id: htmlFor,
2166
- className: "bg-transparent focus:outline-none pl-2 h-9 w-56 rounded-lg disabled:cursor-not-allowed",
2167
- style: inputStyle ?? {},
2168
- placeholder: placeholder ?? ""
2169
- }
2170
- ),
2171
- /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-5 h-5 text-foreground-muted", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M10.5 3.75a6.75 6.75 0 100 13.5 6.75 6.75 0 000-13.5zM2.25 10.5a8.25 8.25 0 1114.59 5.28l4.69 4.69a.75.75 0 11-1.06 1.06l-4.69-4.69A8.25 8.25 0 012.25 10.5z", clipRule: "evenodd" }) })
2192
+ label && /* @__PURE__ */ jsxs(
2193
+ "label",
2194
+ {
2195
+ htmlFor,
2196
+ style: { width: horizontal ? labelWidth : void 0, ...labelStyle },
2197
+ className: [
2198
+ "text-sm font-medium text-foreground select-none",
2199
+ horizontal ? "mt-2 flex-shrink-0" : ""
2200
+ ].filter(Boolean).join(" "),
2201
+ children: [
2202
+ label,
2203
+ required && /* @__PURE__ */ jsx("span", { className: "text-status-error ml-0.5", "aria-hidden": "true", children: "*" })
2204
+ ]
2205
+ }
2206
+ ),
2207
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col min-w-0 flex-1", children: [
2208
+ children,
2209
+ hasError && /* @__PURE__ */ jsx("div", { id: errorId, className: "text-status-error text-xs mt-1", children: errorMessage })
2172
2210
  ] })
2173
2211
  ]
2174
2212
  }
2213
+ );
2214
+ }
2215
+ var SearchIcon = /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-4 h-4", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M10.5 3.75a6.75 6.75 0 100 13.5 6.75 6.75 0 000-13.5zM2.25 10.5a8.25 8.25 0 1114.59 5.28l4.69 4.69a.75.75 0 11-1.06 1.06l-4.69-4.69A8.25 8.25 0 012.25 10.5z", clipRule: "evenodd" }) });
2216
+ var SearchInput = React8.forwardRef(function SearchInput2({ value, onChange, disabled, label, htmlFor, placeholder, name, inputStyle, style, layout = "vertical", size = "md", icon }, ref) {
2217
+ return /* @__PURE__ */ jsx(Field, { label, htmlFor, layout, children: /* @__PURE__ */ jsxs(
2218
+ "div",
2219
+ {
2220
+ className: `flex items-center ${fieldShell({ size, disabled, focusWithin: true })}`,
2221
+ style,
2222
+ children: [
2223
+ /* @__PURE__ */ jsx("span", { className: "flex-shrink-0 mr-2 text-foreground-muted", children: icon ?? SearchIcon }),
2224
+ /* @__PURE__ */ jsx(
2225
+ "input",
2226
+ {
2227
+ ref,
2228
+ disabled,
2229
+ value,
2230
+ onChange,
2231
+ type: "search",
2232
+ enterKeyHint: "search",
2233
+ name,
2234
+ id: htmlFor,
2235
+ className: "min-w-0 flex-1 bg-transparent outline-none disabled:cursor-not-allowed placeholder:text-foreground-muted",
2236
+ style: inputStyle,
2237
+ placeholder: placeholder ?? ""
2238
+ }
2239
+ )
2240
+ ]
2241
+ }
2175
2242
  ) });
2176
2243
  });
2177
2244
  var SearchInput_default = SearchInput;
@@ -2199,7 +2266,8 @@ function Dropdown({
2199
2266
  items = [],
2200
2267
  labelStyle = {},
2201
2268
  placeholder,
2202
- showSelectedCount = false
2269
+ showSelectedCount = false,
2270
+ size = "md"
2203
2271
  }) {
2204
2272
  const [open, setOpen] = useState(false);
2205
2273
  const [selectedItems, setSelectedItems] = useState([]);
@@ -2262,7 +2330,7 @@ function Dropdown({
2262
2330
  "aria-invalid": hasError || void 0,
2263
2331
  "aria-describedby": hasError ? errorId : void 0,
2264
2332
  style,
2265
- className: `flex items-center justify-between relative h-9 rounded-lg border border-border cursor-pointer select-none focus:outline-none focus-visible:border-transparent focus-visible:ring-2 focus-visible:ring-accent ${disabled ? "cursor-not-allowed bg-surface-raised text-foreground-muted" : "bg-surface text-foreground"} ${hasError ? "border-status-error" : ""}`,
2333
+ className: `flex items-center justify-between cursor-pointer select-none ${fieldShell({ size, hasError, disabled })}`,
2266
2334
  tabIndex: disabled ? -1 : 0,
2267
2335
  onKeyDown: (e) => {
2268
2336
  if (disabled) return;
@@ -2275,7 +2343,7 @@ function Dropdown({
2275
2343
  /* @__PURE__ */ jsx(
2276
2344
  "div",
2277
2345
  {
2278
- className: `h-7 pl-2 ${!style?.width ? "min-w-[240px]" : ""} flex items-center gap-1 overflow-hidden`,
2346
+ className: `${!style?.width ? "min-w-[200px]" : ""} flex items-center gap-1 overflow-hidden`,
2279
2347
  children: !value || Array.isArray(value) && value.length === 0 ? /* @__PURE__ */ jsx("span", { className: "text-foreground-muted text-sm", children: placeholder }) : Array.isArray(value) ? /* @__PURE__ */ jsxs(Fragment, { children: [
2280
2348
  value.slice(0, 1).map((val) => /* @__PURE__ */ jsx(
2281
2349
  DropdownPill,
@@ -2289,7 +2357,7 @@ function Dropdown({
2289
2357
  ] }) : /* @__PURE__ */ jsx(DropdownPill, { value: innerItems.find((it) => it.key === value)?.label })
2290
2358
  }
2291
2359
  ),
2292
- /* @__PURE__ */ jsx("div", { className: `transition-transform duration-200 mr-2 ${open ? "rotate-180" : "rotate-0"}`, "aria-hidden": "true", children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) }) })
2360
+ /* @__PURE__ */ jsx("div", { className: `flex-shrink-0 ml-2 text-foreground-muted transition-transform duration-200 ${open ? "rotate-180" : "rotate-0"}`, "aria-hidden": "true", children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) }) })
2293
2361
  ]
2294
2362
  }
2295
2363
  ) }),
@@ -2364,9 +2432,105 @@ function Dropdown({
2364
2432
  ]
2365
2433
  }
2366
2434
  ),
2367
- hasError && /* @__PURE__ */ jsx("div", { id: errorId, className: "text-center text-status-error text-xs mt-1", children: errorMessage })
2435
+ hasError && /* @__PURE__ */ jsx("div", { id: errorId, className: "text-status-error text-xs mt-1", children: errorMessage })
2368
2436
  ] });
2369
2437
  }
2438
+ var SHIMMER = [
2439
+ "relative overflow-hidden rounded-sm bg-surface-raised",
2440
+ 'before:absolute before:inset-0 before:content-[""]',
2441
+ "before:bg-gradient-to-r before:from-transparent before:via-white/30 before:to-transparent",
2442
+ "before:animate-[shimmer_1.6s_linear_infinite]",
2443
+ // Respect prefers-reduced-motion — the resting bg-surface-raised is still
2444
+ // a perfectly legible placeholder for users who have animations off.
2445
+ "motion-reduce:before:hidden"
2446
+ ].join(" ");
2447
+ function SkeletonBox({ width, height = 16, radius, className = "", style }) {
2448
+ return /* @__PURE__ */ jsx(
2449
+ "span",
2450
+ {
2451
+ role: "presentation",
2452
+ "aria-hidden": "true",
2453
+ className: `block ${SHIMMER} ${className}`,
2454
+ style: {
2455
+ width: width ?? "100%",
2456
+ height,
2457
+ borderRadius: radius ?? "var(--radius-md)",
2458
+ ...style
2459
+ }
2460
+ }
2461
+ );
2462
+ }
2463
+ function SkeletonText({
2464
+ lines = 3,
2465
+ lastLineWidth = 60,
2466
+ lineHeight = 14,
2467
+ gap = 8,
2468
+ className = "",
2469
+ style
2470
+ }) {
2471
+ return /* @__PURE__ */ jsx(
2472
+ "div",
2473
+ {
2474
+ role: "presentation",
2475
+ "aria-hidden": "true",
2476
+ className: `flex flex-col ${className}`,
2477
+ style: { gap, ...style },
2478
+ children: Array.from({ length: lines }).map((_, i) => {
2479
+ const isLast = i === lines - 1;
2480
+ const width = isLast && lines > 1 ? `${lastLineWidth}%` : "100%";
2481
+ return /* @__PURE__ */ jsx(
2482
+ "span",
2483
+ {
2484
+ className: `block ${SHIMMER}`,
2485
+ style: { height: lineHeight, width, borderRadius: "var(--radius-sm)" }
2486
+ },
2487
+ i
2488
+ );
2489
+ })
2490
+ }
2491
+ );
2492
+ }
2493
+ function SkeletonCircle({ size = 40, className = "", style }) {
2494
+ return /* @__PURE__ */ jsx(
2495
+ "span",
2496
+ {
2497
+ role: "presentation",
2498
+ "aria-hidden": "true",
2499
+ className: `block flex-shrink-0 ${SHIMMER} ${className}`,
2500
+ style: {
2501
+ width: size,
2502
+ height: size,
2503
+ borderRadius: "50%",
2504
+ ...style
2505
+ }
2506
+ }
2507
+ );
2508
+ }
2509
+ function SkeletonCard({ hasAvatar = true, lines = 3, className = "", style }) {
2510
+ return /* @__PURE__ */ jsxs(
2511
+ "div",
2512
+ {
2513
+ role: "presentation",
2514
+ "aria-hidden": "true",
2515
+ className: `rounded-lg border border-border bg-surface p-4 ${className}`,
2516
+ style,
2517
+ children: [
2518
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
2519
+ hasAvatar && /* @__PURE__ */ jsx(SkeletonCircle, { size: 36 }),
2520
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col gap-2", children: [
2521
+ /* @__PURE__ */ jsx(SkeletonBox, { height: 12, width: "55%" }),
2522
+ /* @__PURE__ */ jsx(SkeletonBox, { height: 10, width: "35%" })
2523
+ ] })
2524
+ ] }),
2525
+ /* @__PURE__ */ jsx(SkeletonText, { lines, lastLineWidth: 55 }),
2526
+ /* @__PURE__ */ jsxs("div", { className: "mt-4 flex gap-2", children: [
2527
+ /* @__PURE__ */ jsx(SkeletonBox, { height: 28, width: 72 }),
2528
+ /* @__PURE__ */ jsx(SkeletonBox, { height: 28, width: 56 })
2529
+ ] })
2530
+ ]
2531
+ }
2532
+ );
2533
+ }
2370
2534
  var DEFAULT_PICKER = [
2371
2535
  { key: 1, value: 5, label: 5 },
2372
2536
  { key: 2, value: 10, label: 10 },
@@ -2552,7 +2716,9 @@ function Table({
2552
2716
  expandRow = DEFAULT_EXPAND,
2553
2717
  hasSearch = true,
2554
2718
  footer = null,
2555
- header = null
2719
+ header = null,
2720
+ loading = false,
2721
+ loadingRowCount = 8
2556
2722
  }) {
2557
2723
  const searchRef = useRef(null);
2558
2724
  const [searchTerm, setSearchTerm] = useState("");
@@ -2636,9 +2802,16 @@ function Table({
2636
2802
  )
2637
2803
  ] }),
2638
2804
  /* @__PURE__ */ jsx("div", { children: header }),
2639
- /* @__PURE__ */ jsx("div", { className: "overflow-x-auto rounded-lg", children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse", children: [
2805
+ /* @__PURE__ */ jsx("div", { className: "overflow-x-auto rounded-lg", children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse", "aria-busy": loading || void 0, children: [
2640
2806
  /* @__PURE__ */ jsx(TableHeader, { columns, hasExpand: !!expandRow.enabled }),
2641
- /* @__PURE__ */ jsx(
2807
+ loading ? /* @__PURE__ */ jsx(
2808
+ TableSkeletonBody,
2809
+ {
2810
+ columns,
2811
+ rowCount: loadingRowCount,
2812
+ hasExpand: !!expandRow.enabled
2813
+ }
2814
+ ) : /* @__PURE__ */ jsx(
2642
2815
  TableBody,
2643
2816
  {
2644
2817
  columns,
@@ -2651,6 +2824,23 @@ function Table({
2651
2824
  /* @__PURE__ */ jsx("div", { children: footer })
2652
2825
  ] });
2653
2826
  }
2827
+ function TableSkeletonBody({
2828
+ columns,
2829
+ rowCount,
2830
+ hasExpand
2831
+ }) {
2832
+ return /* @__PURE__ */ jsx("tbody", { "aria-hidden": "true", children: Array.from({ length: rowCount }).map((_, i) => /* @__PURE__ */ jsxs(
2833
+ "tr",
2834
+ {
2835
+ className: `border-b border-border ${i % 2 === 0 ? "bg-surface" : "bg-surface-raised"}`,
2836
+ children: [
2837
+ hasExpand && /* @__PURE__ */ jsx("td", { className: "p-0 align-middle w-9" }),
2838
+ columns.map((col) => /* @__PURE__ */ jsx("td", { className: "py-3 px-3 align-middle", children: /* @__PURE__ */ jsx(SkeletonBox, { height: 12, width: `${50 + i % 4 * 12}%` }) }, col.key))
2839
+ ]
2840
+ },
2841
+ i
2842
+ )) });
2843
+ }
2654
2844
  function ThemeSwitch({ checked, onChange, label = "Toggle dark mode" }) {
2655
2845
  const id = useId();
2656
2846
  return /* @__PURE__ */ jsx("label", { htmlFor: id, className: "flex items-center gap-2 cursor-pointer select-none", children: /* @__PURE__ */ jsx(
@@ -3083,162 +3273,71 @@ function ThemeProvider({
3083
3273
  )
3084
3274
  ] });
3085
3275
  }
3086
- var SHIMMER = [
3087
- "relative overflow-hidden rounded-sm bg-surface-raised",
3088
- 'before:absolute before:inset-0 before:content-[""]',
3089
- "before:bg-gradient-to-r before:from-transparent before:via-white/30 before:to-transparent",
3090
- "before:animate-[shimmer_1.6s_linear_infinite]",
3091
- // Respect prefers-reduced-motion — the resting bg-surface-raised is still
3092
- // a perfectly legible placeholder for users who have animations off.
3093
- "motion-reduce:before:hidden"
3094
- ].join(" ");
3095
- function SkeletonBox({ width, height = 16, radius, className = "", style }) {
3096
- return /* @__PURE__ */ jsx(
3097
- "span",
3098
- {
3099
- role: "presentation",
3100
- "aria-hidden": "true",
3101
- className: `block ${SHIMMER} ${className}`,
3102
- style: {
3103
- width: width ?? "100%",
3104
- height,
3105
- borderRadius: radius ?? "var(--radius-md)",
3106
- ...style
3107
- }
3108
- }
3109
- );
3110
- }
3111
- function SkeletonText({
3112
- lines = 3,
3113
- lastLineWidth = 60,
3114
- lineHeight = 14,
3115
- gap = 8,
3116
- className = "",
3117
- style
3276
+ function TextInput({
3277
+ value,
3278
+ onChange,
3279
+ disabled,
3280
+ label,
3281
+ htmlFor,
3282
+ placeholder,
3283
+ name,
3284
+ type = "text",
3285
+ inputStyle,
3286
+ style,
3287
+ layout = "vertical",
3288
+ size = "md",
3289
+ onBlur,
3290
+ errorMessage,
3291
+ required,
3292
+ prefix,
3293
+ suffix
3118
3294
  }) {
3119
- return /* @__PURE__ */ jsx(
3120
- "div",
3295
+ const errorId = useId();
3296
+ const hasError = errorMessage != null;
3297
+ const hasAdornment = prefix != null || suffix != null;
3298
+ const input = /* @__PURE__ */ jsx(
3299
+ "input",
3121
3300
  {
3122
- role: "presentation",
3123
- "aria-hidden": "true",
3124
- className: `flex flex-col ${className}`,
3125
- style: { gap, ...style },
3126
- children: Array.from({ length: lines }).map((_, i) => {
3127
- const isLast = i === lines - 1;
3128
- const width = isLast && lines > 1 ? `${lastLineWidth}%` : "100%";
3129
- return /* @__PURE__ */ jsx(
3130
- "span",
3131
- {
3132
- className: `block ${SHIMMER}`,
3133
- style: { height: lineHeight, width, borderRadius: "var(--radius-sm)" }
3134
- },
3135
- i
3136
- );
3137
- })
3301
+ autoComplete: "off",
3302
+ disabled,
3303
+ value,
3304
+ onChange,
3305
+ onBlur,
3306
+ type,
3307
+ name,
3308
+ id: htmlFor,
3309
+ "aria-invalid": hasError || void 0,
3310
+ "aria-describedby": hasError ? errorId : void 0,
3311
+ placeholder: placeholder ?? "",
3312
+ className: hasAdornment ? "min-w-0 flex-1 bg-transparent outline-none disabled:cursor-not-allowed placeholder:text-foreground-muted" : fieldShell({ size, hasError, disabled }),
3313
+ style: inputStyle
3138
3314
  }
3139
3315
  );
3140
- }
3141
- function SkeletonCircle({ size = 40, className = "", style }) {
3142
3316
  return /* @__PURE__ */ jsx(
3143
- "span",
3317
+ Field,
3144
3318
  {
3145
- role: "presentation",
3146
- "aria-hidden": "true",
3147
- className: `block flex-shrink-0 ${SHIMMER} ${className}`,
3148
- style: {
3149
- width: size,
3150
- height: size,
3151
- borderRadius: "50%",
3152
- ...style
3153
- }
3319
+ label,
3320
+ htmlFor,
3321
+ errorId,
3322
+ errorMessage,
3323
+ layout,
3324
+ required,
3325
+ className: style ? void 0 : void 0,
3326
+ children: hasAdornment ? /* @__PURE__ */ jsxs(
3327
+ "div",
3328
+ {
3329
+ className: `flex items-center ${fieldShell({ size, hasError, disabled, focusWithin: true })}`,
3330
+ style,
3331
+ children: [
3332
+ prefix && /* @__PURE__ */ jsx("span", { className: "flex-shrink-0 mr-2 text-foreground-muted", children: prefix }),
3333
+ input,
3334
+ suffix && /* @__PURE__ */ jsx("span", { className: "flex-shrink-0 ml-2 text-foreground-muted", children: suffix })
3335
+ ]
3336
+ }
3337
+ ) : input
3154
3338
  }
3155
3339
  );
3156
3340
  }
3157
- function SkeletonCard({ hasAvatar = true, lines = 3, className = "", style }) {
3158
- return /* @__PURE__ */ jsxs(
3159
- "div",
3160
- {
3161
- role: "presentation",
3162
- "aria-hidden": "true",
3163
- className: `rounded-lg border border-border bg-surface p-4 ${className}`,
3164
- style,
3165
- children: [
3166
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
3167
- hasAvatar && /* @__PURE__ */ jsx(SkeletonCircle, { size: 36 }),
3168
- /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col gap-2", children: [
3169
- /* @__PURE__ */ jsx(SkeletonBox, { height: 12, width: "55%" }),
3170
- /* @__PURE__ */ jsx(SkeletonBox, { height: 10, width: "35%" })
3171
- ] })
3172
- ] }),
3173
- /* @__PURE__ */ jsx(SkeletonText, { lines, lastLineWidth: 55 }),
3174
- /* @__PURE__ */ jsxs("div", { className: "mt-4 flex gap-2", children: [
3175
- /* @__PURE__ */ jsx(SkeletonBox, { height: 28, width: 72 }),
3176
- /* @__PURE__ */ jsx(SkeletonBox, { height: 28, width: 56 })
3177
- ] })
3178
- ]
3179
- }
3180
- );
3181
- }
3182
- function TextInput({
3183
- value,
3184
- onChange,
3185
- disabled,
3186
- label,
3187
- htmlFor,
3188
- placeholder,
3189
- name,
3190
- inputStyle,
3191
- style,
3192
- layout,
3193
- onBlur,
3194
- errorMessage,
3195
- labelColor
3196
- }) {
3197
- const errorId = useId();
3198
- const hasError = errorMessage != null;
3199
- return /* @__PURE__ */ jsxs("div", { className: "relative flex flex-col items-center justify-center", children: [
3200
- /* @__PURE__ */ jsxs(
3201
- "div",
3202
- {
3203
- className: `flex ${layout === "vertical" ? "flex-col" : "flex-row items-center gap-2"}`,
3204
- style: style ?? {},
3205
- children: [
3206
- label && // Render <label> only when a label is provided. An empty
3207
- // <label htmlFor=…> announces as an unlabeled control in
3208
- // some screen readers.
3209
- /* @__PURE__ */ jsx(
3210
- "label",
3211
- {
3212
- style: { color: labelColor || void 0 },
3213
- className: `text-sm font-medium ml-1 max-content ${!labelColor && "text-foreground"}`,
3214
- htmlFor,
3215
- children: label
3216
- }
3217
- ),
3218
- /* @__PURE__ */ jsx(
3219
- "input",
3220
- {
3221
- autoComplete: "off",
3222
- disabled,
3223
- value,
3224
- onChange,
3225
- onBlur,
3226
- type: "text",
3227
- name,
3228
- id: htmlFor,
3229
- "aria-invalid": hasError || void 0,
3230
- "aria-describedby": hasError ? errorId : void 0,
3231
- className: `${hasError ? "border border-status-error" : "border border-border"} bg-surface text-foreground p-2 h-9 w-60 mt-1 rounded-lg disabled:bg-surface-raised disabled:text-foreground-muted disabled:cursor-not-allowed focus:outline-none focus:border-transparent focus:ring-2 focus:ring-accent transition-colors`,
3232
- style: inputStyle ?? {},
3233
- placeholder: placeholder ?? ""
3234
- }
3235
- )
3236
- ]
3237
- }
3238
- ),
3239
- hasError && /* @__PURE__ */ jsx("div", { id: errorId, className: "text-center text-status-error text-xs mt-1", children: errorMessage })
3240
- ] });
3241
- }
3242
3341
  function NumberInput({
3243
3342
  step = 1,
3244
3343
  value,
@@ -3247,8 +3346,10 @@ function NumberInput({
3247
3346
  htmlFor,
3248
3347
  name,
3249
3348
  disabled,
3250
- layout = "horizontal",
3349
+ layout = "vertical",
3350
+ size = "md",
3251
3351
  errorMessage,
3352
+ required,
3252
3353
  inputStyle,
3253
3354
  labelStyle,
3254
3355
  placeholder,
@@ -3289,22 +3390,21 @@ function NumberInput({
3289
3390
  if (Number.isNaN(parsed)) return;
3290
3391
  onChange?.({ target: { value: round(parsed), id: htmlFor, name } });
3291
3392
  };
3292
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
3293
- /* @__PURE__ */ jsxs("div", { className: `flex ${layout === "vertical" ? "flex-col gap-1" : "flex-row items-center gap-2"}`, children: [
3294
- label && /* @__PURE__ */ jsx(
3295
- "label",
3296
- {
3297
- className: "text-sm font-medium ml-1 max-content select-none text-foreground",
3298
- style: labelStyle,
3299
- htmlFor,
3300
- children: label
3301
- }
3302
- ),
3303
- /* @__PURE__ */ jsxs(
3393
+ return /* @__PURE__ */ jsx(
3394
+ Field,
3395
+ {
3396
+ label,
3397
+ htmlFor,
3398
+ errorId,
3399
+ errorMessage,
3400
+ layout,
3401
+ required,
3402
+ labelStyle,
3403
+ children: /* @__PURE__ */ jsxs(
3304
3404
  "div",
3305
3405
  {
3306
3406
  style,
3307
- className: `flex items-center rounded-lg border overflow-hidden ${hasError ? "border-status-error" : "border-border"} ${disabled ? "bg-surface-raised text-foreground-muted cursor-not-allowed" : "bg-surface text-foreground"} focus-within:border-transparent focus-within:ring-2 focus-within:ring-accent transition-colors`,
3407
+ className: `flex items-center overflow-hidden pr-0 ${fieldShell({ size, hasError, disabled, focusWithin: true })}`,
3308
3408
  children: [
3309
3409
  /* @__PURE__ */ jsx(
3310
3410
  "input",
@@ -3321,13 +3421,13 @@ function NumberInput({
3321
3421
  type: "number",
3322
3422
  "aria-invalid": hasError || void 0,
3323
3423
  "aria-describedby": hasError ? errorId : void 0,
3324
- className: "bg-transparent focus:outline-none h-9 w-full px-3 disabled:cursor-not-allowed [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none",
3325
- style: inputStyle ?? {},
3424
+ className: "min-w-0 flex-1 bg-transparent outline-none h-full disabled:cursor-not-allowed [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none placeholder:text-foreground-muted",
3425
+ style: inputStyle,
3326
3426
  placeholder: placeholder ?? "",
3327
3427
  readOnly
3328
3428
  }
3329
3429
  ),
3330
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col border-l border-border h-9", children: [
3430
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col self-stretch border-l border-border flex-shrink-0", children: [
3331
3431
  /* @__PURE__ */ jsx(
3332
3432
  "button",
3333
3433
  {
@@ -3336,7 +3436,7 @@ function NumberInput({
3336
3436
  onClick: onIncrement,
3337
3437
  disabled: disabled || readOnly || max !== void 0 && numeric >= max,
3338
3438
  "aria-label": "Increase value",
3339
- className: "flex-1 px-1.5 flex items-center justify-center hover:bg-surface-raised disabled:opacity-30 disabled:cursor-not-allowed transition-colors focus:outline-none focus-visible:bg-surface-raised",
3439
+ className: "flex-1 px-1.5 flex items-center justify-center text-foreground-muted hover:bg-surface-raised hover:text-foreground disabled:opacity-30 disabled:cursor-not-allowed transition-colors",
3340
3440
  children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, className: "h-3 w-3", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 15l7-7 7 7" }) })
3341
3441
  }
3342
3442
  ),
@@ -3348,7 +3448,7 @@ function NumberInput({
3348
3448
  onClick: onDecrement,
3349
3449
  disabled: disabled || readOnly || min !== void 0 && numeric <= min,
3350
3450
  "aria-label": "Decrease value",
3351
- className: "flex-1 px-1.5 flex items-center justify-center hover:bg-surface-raised disabled:opacity-30 disabled:cursor-not-allowed transition-colors focus:outline-none focus-visible:bg-surface-raised border-t border-border",
3451
+ className: "flex-1 px-1.5 flex items-center justify-center text-foreground-muted hover:bg-surface-raised hover:text-foreground disabled:opacity-30 disabled:cursor-not-allowed transition-colors border-t border-border",
3352
3452
  children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, className: "h-3 w-3", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })
3353
3453
  }
3354
3454
  )
@@ -3356,10 +3456,18 @@ function NumberInput({
3356
3456
  ]
3357
3457
  }
3358
3458
  )
3359
- ] }),
3360
- hasError && /* @__PURE__ */ jsx("div", { id: errorId, className: "text-xs text-status-error ml-1", children: errorMessage })
3361
- ] });
3459
+ }
3460
+ );
3362
3461
  }
3462
+ var EyeIcon = /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-5 h-5", "aria-hidden": "true", children: [
3463
+ /* @__PURE__ */ jsx("path", { d: "M12 15a3 3 0 100-6 3 3 0 000 6z" }),
3464
+ /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M1.323 11.447C2.811 6.976 7.028 3.75 12.001 3.75c4.97 0 9.185 3.223 10.675 7.69.12.362.12.752 0 1.113-1.487 4.471-5.705 7.697-10.677 7.697-4.97 0-9.186-3.223-10.675-7.69a1.762 1.762 0 010-1.113zM17.25 12a5.25 5.25 0 11-10.5 0 5.25 5.25 0 0110.5 0z", clipRule: "evenodd" })
3465
+ ] });
3466
+ var EyeSlashIcon = /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-5 h-5", "aria-hidden": "true", children: [
3467
+ /* @__PURE__ */ jsx("path", { d: "M3.53 2.47a.75.75 0 00-1.06 1.06l18 18a.75.75 0 101.06-1.06l-18-18zM22.676 12.553a11.249 11.249 0 01-2.631 4.31l-3.099-3.099a5.25 5.25 0 00-6.71-6.71L7.759 4.577a11.217 11.217 0 014.242-.827c4.97 0 9.185 3.223 10.675 7.69.12.362.12.752 0 1.113z" }),
3468
+ /* @__PURE__ */ jsx("path", { d: "M15.75 12c0 .18-.013.357-.037.53l-4.244-4.243A3.75 3.75 0 0115.75 12zM12.53 15.713l-4.243-4.244a3.75 3.75 0 004.243 4.243z" }),
3469
+ /* @__PURE__ */ jsx("path", { d: "M6.75 12c0-.619.107-1.213.304-1.764l-3.1-3.1a11.25 11.25 0 00-2.63 4.31c-.12.362-.12.752 0 1.114 1.489 4.467 5.704 7.69 10.675 7.69 1.5 0 2.933-.294 4.242-.827l-2.477-2.477A5.25 5.25 0 016.75 12z" })
3470
+ ] });
3363
3471
  function Password({
3364
3472
  value,
3365
3473
  onChange,
@@ -3370,73 +3478,66 @@ function Password({
3370
3478
  name,
3371
3479
  inputStyle,
3372
3480
  style,
3373
- layout,
3481
+ layout = "vertical",
3482
+ size = "md",
3374
3483
  onBlur,
3375
3484
  errorMessage,
3376
- labelColor,
3377
- iconColor
3485
+ required,
3486
+ showIcon,
3487
+ hideIcon
3378
3488
  }) {
3379
- const [passwordVisible, setPasswordVisible] = useState(false);
3489
+ const [visible, setVisible] = useState(false);
3380
3490
  const errorId = useId();
3381
3491
  const hasError = errorMessage != null;
3382
- return /* @__PURE__ */ jsxs("div", { className: "relative flex flex-col items-center justify-center", style: style ?? {}, children: [
3383
- /* @__PURE__ */ jsxs("div", { className: `flex ${layout === "vertical" ? "flex-col" : "flex-row items-center gap-2"}`, children: [
3384
- label && /* @__PURE__ */ jsx(
3385
- "label",
3492
+ return /* @__PURE__ */ jsx(
3493
+ Field,
3494
+ {
3495
+ label,
3496
+ htmlFor,
3497
+ errorId,
3498
+ errorMessage,
3499
+ layout,
3500
+ required,
3501
+ children: /* @__PURE__ */ jsxs(
3502
+ "div",
3386
3503
  {
3387
- style: { color: labelColor || void 0 },
3388
- className: `text-sm font-medium ml-1 max-content ${!labelColor && "text-foreground"}`,
3389
- htmlFor,
3390
- children: label
3391
- }
3392
- ),
3393
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
3394
- /* @__PURE__ */ jsx(
3395
- "input",
3396
- {
3397
- autoComplete: "off",
3398
- disabled,
3399
- value,
3400
- onChange,
3401
- onBlur,
3402
- type: passwordVisible ? "text" : "password",
3403
- name,
3404
- id: htmlFor,
3405
- "aria-invalid": hasError || void 0,
3406
- "aria-describedby": hasError ? errorId : void 0,
3407
- className: `${hasError ? "border border-status-error" : "border border-border"} bg-surface text-foreground p-2 h-9 w-52 mt-1 rounded-lg disabled:bg-surface-raised disabled:text-foreground-muted disabled:cursor-not-allowed focus:outline-none focus:border-transparent focus:ring-2 focus:ring-accent transition-colors`,
3408
- style: inputStyle ?? {},
3409
- placeholder: placeholder ?? ""
3410
- }
3411
- ),
3412
- /* @__PURE__ */ jsx(
3413
- "button",
3414
- {
3415
- type: "button",
3416
- className: "cursor-pointer p-1 text-foreground-secondary hover:text-foreground rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-accent",
3417
- style: iconColor ? { color: iconColor } : void 0,
3418
- onClick: () => setPasswordVisible(!passwordVisible),
3419
- "aria-label": passwordVisible ? "Hide password" : "Show password",
3420
- children: passwordVisible ? (
3421
- /* EyeSlash */
3422
- /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-6 h-6", children: [
3423
- /* @__PURE__ */ jsx("path", { d: "M3.53 2.47a.75.75 0 00-1.06 1.06l18 18a.75.75 0 101.06-1.06l-18-18zM22.676 12.553a11.249 11.249 0 01-2.631 4.31l-3.099-3.099a5.25 5.25 0 00-6.71-6.71L7.759 4.577a11.217 11.217 0 014.242-.827c4.97 0 9.185 3.223 10.675 7.69.12.362.12.752 0 1.113z" }),
3424
- /* @__PURE__ */ jsx("path", { d: "M15.75 12c0 .18-.013.357-.037.53l-4.244-4.243A3.75 3.75 0 0115.75 12zM12.53 15.713l-4.243-4.244a3.75 3.75 0 004.243 4.243z" }),
3425
- /* @__PURE__ */ jsx("path", { d: "M6.75 12c0-.619.107-1.213.304-1.764l-3.1-3.1a11.25 11.25 0 00-2.63 4.31c-.12.362-.12.752 0 1.114 1.489 4.467 5.704 7.69 10.675 7.69 1.5 0 2.933-.294 4.242-.827l-2.477-2.477A5.25 5.25 0 016.75 12z" })
3426
- ] })
3427
- ) : (
3428
- /* Eye */
3429
- /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-6 h-6", children: [
3430
- /* @__PURE__ */ jsx("path", { d: "M12 15a3 3 0 100-6 3 3 0 000 6z" }),
3431
- /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M1.323 11.447C2.811 6.976 7.028 3.75 12.001 3.75c4.97 0 9.185 3.223 10.675 7.69.12.362.12.752 0 1.113-1.487 4.471-5.705 7.697-10.677 7.697-4.97 0-9.186-3.223-10.675-7.69a1.762 1.762 0 010-1.113zM17.25 12a5.25 5.25 0 11-10.5 0 5.25 5.25 0 0110.5 0z", clipRule: "evenodd" })
3432
- ] })
3504
+ className: `flex items-center ${fieldShell({ size, hasError, disabled, focusWithin: true })}`,
3505
+ style,
3506
+ children: [
3507
+ /* @__PURE__ */ jsx(
3508
+ "input",
3509
+ {
3510
+ autoComplete: "off",
3511
+ disabled,
3512
+ value,
3513
+ onChange,
3514
+ onBlur,
3515
+ type: visible ? "text" : "password",
3516
+ name,
3517
+ id: htmlFor,
3518
+ "aria-invalid": hasError || void 0,
3519
+ "aria-describedby": hasError ? errorId : void 0,
3520
+ placeholder: placeholder ?? "",
3521
+ className: "min-w-0 flex-1 bg-transparent outline-none disabled:cursor-not-allowed placeholder:text-foreground-muted",
3522
+ style: inputStyle
3523
+ }
3524
+ ),
3525
+ /* @__PURE__ */ jsx(
3526
+ "button",
3527
+ {
3528
+ type: "button",
3529
+ disabled,
3530
+ className: `flex-shrink-0 ml-2 ${FIELD_SIZE[size].gap} rounded text-foreground-muted hover:text-foreground transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-accent disabled:cursor-not-allowed`,
3531
+ onClick: () => setVisible((v) => !v),
3532
+ "aria-label": visible ? "Hide password" : "Show password",
3533
+ children: visible ? hideIcon ?? EyeSlashIcon : showIcon ?? EyeIcon
3534
+ }
3433
3535
  )
3434
- }
3435
- )
3436
- ] })
3437
- ] }),
3438
- hasError && /* @__PURE__ */ jsx("div", { id: errorId, className: "text-center text-status-error text-xs mt-1", children: errorMessage })
3439
- ] });
3536
+ ]
3537
+ }
3538
+ )
3539
+ }
3540
+ );
3440
3541
  }
3441
3542
  function Checkbox({
3442
3543
  checked,
@@ -3517,6 +3618,96 @@ function Checkbox({
3517
3618
  errorMessage && /* @__PURE__ */ jsx("span", { className: "text-xs text-status-error pl-[26px]", children: errorMessage })
3518
3619
  ] });
3519
3620
  }
3621
+ var DOT_SIZE = {
3622
+ sm: "h-3.5 w-3.5",
3623
+ md: "h-4 w-4",
3624
+ lg: "h-5 w-5"
3625
+ };
3626
+ var TEXT_SIZE = {
3627
+ sm: "text-xs",
3628
+ md: "text-sm",
3629
+ lg: "text-base"
3630
+ };
3631
+ function RadioGroup({
3632
+ options,
3633
+ value,
3634
+ defaultValue,
3635
+ onChange,
3636
+ name,
3637
+ label,
3638
+ orientation = "vertical",
3639
+ size = "md",
3640
+ disabled,
3641
+ required,
3642
+ errorMessage
3643
+ }) {
3644
+ const errorId = useId();
3645
+ const groupId = useId();
3646
+ const hasError = errorMessage != null;
3647
+ return /* @__PURE__ */ jsx(
3648
+ Field,
3649
+ {
3650
+ label,
3651
+ htmlFor: groupId,
3652
+ errorId,
3653
+ errorMessage,
3654
+ required,
3655
+ children: /* @__PURE__ */ jsx(
3656
+ RadioGroupPrimitive.Root,
3657
+ {
3658
+ id: groupId,
3659
+ name,
3660
+ value,
3661
+ defaultValue,
3662
+ onValueChange: onChange,
3663
+ disabled,
3664
+ required,
3665
+ "aria-invalid": hasError || void 0,
3666
+ "aria-describedby": hasError ? errorId : void 0,
3667
+ orientation,
3668
+ className: orientation === "horizontal" ? "flex flex-row flex-wrap gap-5" : "flex flex-col gap-3",
3669
+ children: options.map((opt) => {
3670
+ const itemId = `${groupId}-${opt.value}`;
3671
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2.5", children: [
3672
+ /* @__PURE__ */ jsx(
3673
+ RadioGroupPrimitive.Item,
3674
+ {
3675
+ id: itemId,
3676
+ value: opt.value,
3677
+ disabled: opt.disabled,
3678
+ className: [
3679
+ DOT_SIZE[size],
3680
+ "mt-0.5 flex-shrink-0 rounded-full border bg-surface transition-colors duration-150",
3681
+ "border-border-strong",
3682
+ "hover:border-accent",
3683
+ "data-[state=checked]:border-accent",
3684
+ "focus:outline-none focus-visible:ring-[3px] focus-visible:ring-focus-ring",
3685
+ "disabled:cursor-not-allowed disabled:opacity-50"
3686
+ ].join(" "),
3687
+ children: /* @__PURE__ */ jsx(RadioGroupPrimitive.Indicator, { className: "flex h-full w-full items-center justify-center", children: /* @__PURE__ */ jsx("span", { className: "block h-1/2 w-1/2 rounded-full bg-accent" }) })
3688
+ }
3689
+ ),
3690
+ /* @__PURE__ */ jsxs(
3691
+ "label",
3692
+ {
3693
+ htmlFor: itemId,
3694
+ className: [
3695
+ "select-none",
3696
+ opt.disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer"
3697
+ ].join(" "),
3698
+ children: [
3699
+ /* @__PURE__ */ jsx("span", { className: `block ${TEXT_SIZE[size]} text-foreground`, children: opt.label }),
3700
+ opt.description && /* @__PURE__ */ jsx("span", { className: "block text-xs text-foreground-secondary mt-0.5", children: opt.description })
3701
+ ]
3702
+ }
3703
+ )
3704
+ ] }, opt.value);
3705
+ })
3706
+ }
3707
+ )
3708
+ }
3709
+ );
3710
+ }
3520
3711
  function Switch({
3521
3712
  checked = false,
3522
3713
  onChange,
@@ -3549,29 +3740,70 @@ function AutoComplete({
3549
3740
  inputStyle,
3550
3741
  style,
3551
3742
  layout = "vertical",
3552
- items = [],
3743
+ items,
3744
+ onSearch,
3745
+ debounce = 250,
3553
3746
  onItemClick,
3554
- emptyText = "No results found"
3747
+ emptyText = "No results found",
3748
+ loadingText = "Searching\u2026",
3749
+ size = "md",
3750
+ icon
3555
3751
  }) {
3556
3752
  const [term, setTerm] = useState("");
3557
3753
  const [open, setOpen] = useState(false);
3558
- const foundItems = term.trim() ? items.filter(
3754
+ const [asyncItems, setAsyncItems] = useState([]);
3755
+ const [loading, setLoading] = useState(false);
3756
+ const isAsync = typeof onSearch === "function";
3757
+ const debounceRef = useRef(null);
3758
+ const requestIdRef = useRef(0);
3759
+ const staticFiltered = isAsync || !items ? [] : term.trim() ? items.filter(
3559
3760
  ({ key, label: label2 }) => label2.toLowerCase().includes(term.toLowerCase()) || key.toLowerCase().includes(term.toLowerCase())
3560
3761
  ) : [];
3762
+ useEffect(() => {
3763
+ if (!isAsync) return;
3764
+ if (debounceRef.current) clearTimeout(debounceRef.current);
3765
+ if (!term.trim()) {
3766
+ setAsyncItems([]);
3767
+ setLoading(false);
3768
+ return;
3769
+ }
3770
+ const myId = ++requestIdRef.current;
3771
+ setLoading(true);
3772
+ debounceRef.current = setTimeout(async () => {
3773
+ try {
3774
+ const res = await onSearch(term);
3775
+ if (myId === requestIdRef.current) {
3776
+ setAsyncItems(res);
3777
+ }
3778
+ } catch {
3779
+ if (myId === requestIdRef.current) {
3780
+ setAsyncItems([]);
3781
+ }
3782
+ } finally {
3783
+ if (myId === requestIdRef.current) {
3784
+ setLoading(false);
3785
+ }
3786
+ }
3787
+ }, debounce);
3788
+ return () => {
3789
+ if (debounceRef.current) clearTimeout(debounceRef.current);
3790
+ };
3791
+ }, [term, isAsync, debounce, onSearch]);
3792
+ const foundItems = isAsync ? asyncItems : staticFiltered;
3561
3793
  const handleSelect = (item) => {
3562
3794
  setTerm(`${item.label} (${item.value})`);
3563
3795
  onItemClick?.(item.value);
3564
3796
  setOpen(false);
3565
3797
  };
3566
- return /* @__PURE__ */ jsx("div", { className: "relative flex flex-col items-center justify-center", children: /* @__PURE__ */ jsxs(
3798
+ return /* @__PURE__ */ jsxs(
3567
3799
  "div",
3568
3800
  {
3569
- className: `flex ${layout === "vertical" ? "flex-col" : "flex-row items-center gap-2"}`,
3570
- style: style ?? {},
3801
+ className: `flex ${layout === "vertical" ? "flex-col gap-1.5" : "flex-row items-start gap-3"}`,
3802
+ style,
3571
3803
  children: [
3572
- label && /* @__PURE__ */ jsx("label", { className: "text-sm font-medium ml-1 max-content text-foreground", children: label }),
3573
- /* @__PURE__ */ jsxs(Popover.Root, { open: open && !disabled, onOpenChange: (o) => !disabled && setOpen(o), children: [
3574
- /* @__PURE__ */ jsx(Popover.Anchor, { asChild: true, children: /* @__PURE__ */ jsxs("div", { className: "bg-surface text-foreground flex items-center gap-1 rounded-lg border border-border pr-2 focus-within:border-transparent focus-within:ring-2 focus-within:ring-accent transition-colors", children: [
3804
+ label && /* @__PURE__ */ jsx("label", { className: `text-sm font-medium text-foreground select-none ${layout === "horizontal" ? "mt-2 flex-shrink-0" : ""}`, children: label }),
3805
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col min-w-0 flex-1", children: /* @__PURE__ */ jsxs(Popover.Root, { open: open && !disabled, onOpenChange: (o) => !disabled && setOpen(o), children: [
3806
+ /* @__PURE__ */ jsx(Popover.Anchor, { asChild: true, children: /* @__PURE__ */ jsxs("div", { className: `flex items-center ${fieldShell({ size, disabled, focusWithin: true })}`, children: [
3575
3807
  /* @__PURE__ */ jsx(
3576
3808
  "input",
3577
3809
  {
@@ -3584,16 +3816,17 @@ function AutoComplete({
3584
3816
  onFocus: () => setOpen(true),
3585
3817
  type: "text",
3586
3818
  name,
3587
- className: "bg-transparent focus:outline-none pl-2 h-9 w-56 rounded-lg disabled:cursor-not-allowed",
3588
- style: inputStyle ?? {},
3819
+ className: "min-w-0 flex-1 bg-transparent outline-none disabled:cursor-not-allowed placeholder:text-foreground-muted",
3820
+ style: inputStyle,
3589
3821
  placeholder: placeholder ?? "",
3590
3822
  autoComplete: "off",
3591
3823
  "aria-haspopup": "listbox",
3592
3824
  "aria-expanded": open,
3593
- "aria-autocomplete": "list"
3825
+ "aria-autocomplete": "list",
3826
+ "aria-busy": loading || void 0
3594
3827
  }
3595
3828
  ),
3596
- /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-5 h-5 flex-shrink-0 text-foreground-muted", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M10.5 3.75a6.75 6.75 0 100 13.5 6.75 6.75 0 000-13.5zM2.25 10.5a8.25 8.25 0 1114.59 5.28l4.69 4.69a.75.75 0 11-1.06 1.06l-4.69-4.69A8.25 8.25 0 012.25 10.5z", clipRule: "evenodd" }) })
3829
+ loading ? /* @__PURE__ */ jsx("span", { className: "ml-2 w-4 h-4 flex-shrink-0 flex items-center justify-center text-accent", "aria-hidden": "true", children: /* @__PURE__ */ jsx(LoadingSpinner, { inline: true, size: "xs", spinnerColor: "currentColor" }) }) : /* @__PURE__ */ jsx("span", { className: "ml-2 flex-shrink-0 text-foreground-muted", children: icon ?? /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-4 h-4", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M10.5 3.75a6.75 6.75 0 100 13.5 6.75 6.75 0 000-13.5zM2.25 10.5a8.25 8.25 0 1114.59 5.28l4.69 4.69a.75.75 0 11-1.06 1.06l-4.69-4.69A8.25 8.25 0 012.25 10.5z", clipRule: "evenodd" }) }) })
3597
3830
  ] }) }),
3598
3831
  /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(
3599
3832
  Popover.Content,
@@ -3602,43 +3835,40 @@ function AutoComplete({
3602
3835
  sideOffset: 4,
3603
3836
  onOpenAutoFocus: (e) => e.preventDefault(),
3604
3837
  className: "w-64 bg-surface border border-border rounded-lg mt-1 shadow-md z-50 overflow-y-auto max-h-36 animate-in fade-in-0 zoom-in-95",
3605
- children: foundItems.length === 0 ? /* @__PURE__ */ jsx("div", { className: "h-full w-full flex flex-col items-center justify-center py-4 text-sm text-foreground-secondary", children: emptyText }) : /* @__PURE__ */ jsx("div", { role: "listbox", children: foundItems.map((item) => (
3606
- // tabIndex + Enter/Space onKeyDown
3607
- // makes each option keyboard-activatable.
3608
- // Full roving-tabindex / arrow-key nav
3609
- // is deferred to the Phase-5 rewrite.
3610
- /* @__PURE__ */ jsxs(
3611
- "div",
3612
- {
3613
- role: "option",
3614
- tabIndex: 0,
3615
- className: "text-sm flex items-center gap-2 p-2 transition-colors duration-150 hover:bg-surface-raised cursor-pointer text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-accent",
3616
- onClick: () => handleSelect(item),
3617
- onKeyDown: (e) => {
3618
- if (e.key === "Enter" || e.key === " ") {
3619
- e.preventDefault();
3620
- handleSelect(item);
3621
- }
3622
- },
3623
- children: [
3624
- item.icon,
3625
- /* @__PURE__ */ jsxs("span", { children: [
3626
- item.label,
3627
- " (",
3628
- item.value,
3629
- ")"
3630
- ] })
3631
- ]
3838
+ children: loading ? /* @__PURE__ */ jsxs("div", { className: "h-full w-full flex items-center justify-center gap-2 py-4 text-sm text-foreground-secondary", children: [
3839
+ /* @__PURE__ */ jsx(LoadingSpinner, { inline: true, size: "xs" }),
3840
+ /* @__PURE__ */ jsx("span", { children: loadingText })
3841
+ ] }) : foundItems.length === 0 ? /* @__PURE__ */ jsx("div", { className: "h-full w-full flex flex-col items-center justify-center py-4 text-sm text-foreground-secondary", children: emptyText }) : /* @__PURE__ */ jsx("div", { role: "listbox", children: foundItems.map((item) => /* @__PURE__ */ jsxs(
3842
+ "div",
3843
+ {
3844
+ role: "option",
3845
+ tabIndex: 0,
3846
+ className: "text-sm flex items-center gap-2 p-2 transition-colors duration-150 hover:bg-surface-raised cursor-pointer text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-accent",
3847
+ onClick: () => handleSelect(item),
3848
+ onKeyDown: (e) => {
3849
+ if (e.key === "Enter" || e.key === " ") {
3850
+ e.preventDefault();
3851
+ handleSelect(item);
3852
+ }
3632
3853
  },
3633
- item.key
3634
- )
3854
+ children: [
3855
+ item.icon,
3856
+ /* @__PURE__ */ jsxs("span", { children: [
3857
+ item.label,
3858
+ " (",
3859
+ item.value,
3860
+ ")"
3861
+ ] })
3862
+ ]
3863
+ },
3864
+ item.key
3635
3865
  )) })
3636
3866
  }
3637
3867
  ) })
3638
- ] })
3868
+ ] }) })
3639
3869
  ]
3640
3870
  }
3641
- ) });
3871
+ );
3642
3872
  }
3643
3873
  function flattenVisible(items, expanded, depth = 0, out = []) {
3644
3874
  for (const node of items) {
@@ -3673,7 +3903,8 @@ function TreeSelect({
3673
3903
  items = [],
3674
3904
  placeholder = "Select\u2026",
3675
3905
  parentsSelectable = true,
3676
- defaultExpandedKeys = []
3906
+ defaultExpandedKeys = [],
3907
+ size = "md"
3677
3908
  }) {
3678
3909
  const errorId = useId();
3679
3910
  const hasError = errorMessage != null;
@@ -3772,7 +4003,7 @@ function TreeSelect({
3772
4003
  "aria-invalid": hasError || void 0,
3773
4004
  "aria-describedby": hasError ? errorId : void 0,
3774
4005
  disabled,
3775
- className: `flex items-center justify-between h-9 rounded-lg border ${hasError ? "border-status-error" : "border-border"} px-3 cursor-pointer select-none focus:outline-none focus-visible:border-transparent focus-visible:ring-2 focus-visible:ring-accent ${disabled ? "cursor-not-allowed bg-surface-raised text-foreground-muted" : "bg-surface text-foreground"} ${!style?.width ? "min-w-[240px]" : ""}`,
4006
+ className: `flex items-center justify-between cursor-pointer select-none ${!style?.width ? "min-w-[240px]" : ""} ${fieldShell({ size, hasError, disabled })}`,
3776
4007
  children: [
3777
4008
  /* @__PURE__ */ jsx("span", { className: "text-sm truncate text-left", children: selectedNode ? selectedNode.label : /* @__PURE__ */ jsx("span", { className: "text-foreground-muted", children: placeholder }) }),
3778
4009
  /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: `h-4 w-4 flex-shrink-0 transition-transform duration-200 ${open ? "rotate-180" : ""}`, "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })
@@ -3899,111 +4130,162 @@ function TreeNodeRow({
3899
4130
  }
3900
4131
  );
3901
4132
  }
4133
+ function formatBytes(bytes) {
4134
+ if (bytes === 0) return "0 B";
4135
+ const units = ["B", "KB", "MB", "GB"];
4136
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
4137
+ return `${(bytes / Math.pow(1024, i)).toFixed(i === 0 ? 0 : 1)} ${units[i]}`;
4138
+ }
4139
+ var UploadGlyph = /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.5, className: "w-6 h-6", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 16V4m0 0L8 8m4-4l4 4M4 16v2a2 2 0 002 2h12a2 2 0 002-2v-2" }) });
4140
+ var FileGlyph = /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.5, className: "w-5 h-5", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M14 3v4a1 1 0 001 1h4M5 3h9l5 5v11a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2z" }) });
3902
4141
  function FileInput({
3903
4142
  allowMultiple = false,
3904
4143
  onChange,
3905
4144
  name,
3906
- accept = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,.xlsx"
4145
+ htmlFor,
4146
+ label,
4147
+ accept,
4148
+ prompt = "Click to upload or drag and drop",
4149
+ hint,
4150
+ maxSize,
4151
+ errorMessage,
4152
+ disabled,
4153
+ required,
4154
+ icon
3907
4155
  }) {
3908
- const fileInput = useRef(null);
4156
+ const inputRef = useRef(null);
4157
+ const errorId = useId();
3909
4158
  const [files, setFiles] = useState([]);
4159
+ const [dragging, setDragging] = useState(false);
4160
+ const [sizeError, setSizeError] = useState(null);
4161
+ const effectiveError = errorMessage ?? sizeError ?? void 0;
3910
4162
  const openPicker = () => {
3911
- fileInput.current?.click();
4163
+ if (!disabled) inputRef.current?.click();
3912
4164
  };
3913
- const handleFiles = (list) => {
4165
+ const commit = (list) => {
4166
+ if (maxSize != null) {
4167
+ const tooBig = list.find((f) => f.size > maxSize);
4168
+ if (tooBig) {
4169
+ setSizeError(`"${tooBig.name}" exceeds the ${formatBytes(maxSize)} limit`);
4170
+ return;
4171
+ }
4172
+ }
4173
+ setSizeError(null);
3914
4174
  setFiles(list);
3915
- onChange?.({ target: { files: list } });
4175
+ onChange?.({ target: { files: list, name, id: htmlFor ?? name } });
3916
4176
  };
3917
4177
  const onDrop = (e) => {
3918
4178
  e.preventDefault();
3919
- const fileList = [];
3920
- if (e.dataTransfer.items) {
3921
- for (let i = 0; i < e.dataTransfer.items.length; i++) {
3922
- if (e.dataTransfer.items[i].kind === "file") {
3923
- const f = e.dataTransfer.items[i].getAsFile();
3924
- if (f) fileList.push(f);
3925
- }
3926
- }
3927
- } else {
3928
- for (let i = 0; i < e.dataTransfer.files.length; i++) {
3929
- fileList.push(e.dataTransfer.files[i]);
3930
- }
3931
- }
3932
- handleFiles(fileList);
4179
+ setDragging(false);
4180
+ if (disabled) return;
4181
+ const dropped = Array.from(e.dataTransfer.files ?? []);
4182
+ commit(allowMultiple ? dropped : dropped.slice(0, 1));
3933
4183
  };
3934
- const localOnChange = (e) => {
3935
- handleFiles(Array.from(e.target.files ?? []));
4184
+ const removeFile = (idx) => {
4185
+ const next = files.filter((_, i) => i !== idx);
4186
+ setFiles(next);
4187
+ setSizeError(null);
4188
+ onChange?.({ target: { files: next, name, id: htmlFor ?? name, value: "" } });
4189
+ if (next.length === 0 && inputRef.current) inputRef.current.value = "";
3936
4190
  };
3937
- const removeFile = (e) => {
3938
- e.stopPropagation();
3939
- setFiles([]);
3940
- onChange?.({ target: { files: [], name, id: name, value: "" } });
3941
- if (fileInput.current) fileInput.current.value = "";
3942
- };
3943
- return (
3944
- // Dropzone is keyboard-activatable: role="button", focusable via
3945
- // tabIndex, and Space/Enter trigger the file picker. Without these
3946
- // a keyboard-only user could not upload a file.
3947
- /* @__PURE__ */ jsxs(
3948
- "div",
3949
- {
3950
- role: "button",
3951
- tabIndex: 0,
3952
- "aria-label": "Upload file \u2014 click or drop",
3953
- onClick: openPicker,
3954
- onKeyDown: (e) => {
3955
- if (e.key === "Enter" || e.key === " ") {
3956
- e.preventDefault();
3957
- openPicker();
3958
- }
3959
- },
3960
- className: "border-2 border-dashed border-border hover:border-accent w-full h-full rounded-md transition-colors duration-200 cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-accent text-foreground-secondary hover:text-foreground",
3961
- onDragOver: (e) => e.preventDefault(),
3962
- onDrop,
3963
- children: [
3964
- /* @__PURE__ */ jsx(
3965
- "input",
3966
- {
3967
- id: name,
3968
- name,
3969
- onChange: localOnChange,
3970
- ref: fileInput,
3971
- hidden: true,
3972
- type: "file",
3973
- accept,
3974
- multiple: allowMultiple
3975
- }
3976
- ),
3977
- files.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full items-center justify-center gap-2", children: [
3978
- /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-16 h-16", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M11.47 2.47a.75.75 0 011.06 0l4.5 4.5a.75.75 0 01-1.06 1.06l-3.22-3.22V16.5a.75.75 0 01-1.5 0V4.81L8.03 8.03a.75.75 0 01-1.06-1.06l4.5-4.5zM3 15.75a.75.75 0 01.75.75v2.25a1.5 1.5 0 001.5 1.5h13.5a1.5 1.5 0 001.5-1.5V16.5a.75.75 0 011.5 0v2.25a3 3 0 01-3 3H5.25a3 3 0 01-3-3V16.5a.75.75 0 01.75-.75z", clipRule: "evenodd" }) }),
3979
- /* @__PURE__ */ jsx("div", { className: "text-sm", children: "Click or Drop a file" })
3980
- ] }) : /* @__PURE__ */ jsx("div", { className: "flex gap-3 items-center justify-center w-full h-full p-3", children: files.map((file, id) => /* @__PURE__ */ jsxs(
3981
- "div",
3982
- {
3983
- className: "text-xs flex flex-col items-center w-20 h-24 text-center bg-surface-raised text-foreground p-4 rounded-md relative",
3984
- children: [
3985
- /* @__PURE__ */ jsx(
3986
- "button",
3987
- {
3988
- type: "button",
3989
- onClick: removeFile,
3990
- className: "bg-status-error rounded-full w-4 h-4 absolute right-[-5px] top-[-5px] cursor-pointer flex items-center justify-center focus:outline-none focus-visible:ring-2 focus-visible:ring-accent",
3991
- "aria-label": "Remove file",
3992
- children: /* @__PURE__ */ jsx("svg", { width: "10", height: "10", viewBox: "0 0 20 20", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M15 5L5 15M5 5l10 10", stroke: "#fff", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
3993
- }
3994
- ),
3995
- /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-10 h-10", "aria-hidden": "true", children: [
3996
- /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M5.625 1.5c-1.036 0-1.875.84-1.875 1.875v17.25c0 1.035.84 1.875 1.875 1.875h12.75c1.035 0 1.875-.84 1.875-1.875V12.75A3.75 3.75 0 0016.5 9h-1.875a1.875 1.875 0 01-1.875-1.875V5.25A3.75 3.75 0 009 1.5H5.625z", clipRule: "evenodd" }),
3997
- /* @__PURE__ */ jsx("path", { d: "M12.971 1.816A5.23 5.23 0 0114.25 5.25v1.875c0 .207.168.375.375.375H16.5a5.23 5.23 0 013.434 1.279 9.768 9.768 0 00-6.963-6.963z" })
3998
- ] }),
3999
- /* @__PURE__ */ jsx("span", { className: "text-ellipsis whitespace-nowrap overflow-hidden w-full", children: file.name })
4000
- ]
4191
+ return /* @__PURE__ */ jsxs(
4192
+ Field,
4193
+ {
4194
+ label,
4195
+ htmlFor,
4196
+ errorId,
4197
+ errorMessage: effectiveError,
4198
+ required,
4199
+ children: [
4200
+ /* @__PURE__ */ jsxs(
4201
+ "div",
4202
+ {
4203
+ role: "button",
4204
+ tabIndex: disabled ? -1 : 0,
4205
+ "aria-label": typeof prompt === "string" ? prompt : "Upload file",
4206
+ "aria-disabled": disabled || void 0,
4207
+ "aria-invalid": effectiveError != null || void 0,
4208
+ "aria-describedby": effectiveError != null ? errorId : void 0,
4209
+ onClick: openPicker,
4210
+ onKeyDown: (e) => {
4211
+ if (disabled) return;
4212
+ if (e.key === "Enter" || e.key === " ") {
4213
+ e.preventDefault();
4214
+ openPicker();
4215
+ }
4001
4216
  },
4002
- `${id}${file.name}`
4003
- )) })
4004
- ]
4005
- }
4006
- )
4217
+ onDragOver: (e) => {
4218
+ e.preventDefault();
4219
+ if (!disabled) setDragging(true);
4220
+ },
4221
+ onDragLeave: () => setDragging(false),
4222
+ onDrop,
4223
+ className: [
4224
+ "group flex flex-col items-center justify-center gap-3 w-full rounded-xl border border-dashed px-6 py-8 text-center",
4225
+ "transition-colors duration-150 focus:outline-none focus-visible:ring-[3px] focus-visible:ring-focus-ring",
4226
+ disabled ? "border-border bg-surface-raised cursor-not-allowed opacity-60" : effectiveError != null ? "border-status-error bg-surface cursor-pointer" : dragging ? "border-accent bg-surface-raised cursor-copy" : "border-border bg-surface hover:border-border-strong cursor-pointer"
4227
+ ].join(" "),
4228
+ children: [
4229
+ /* @__PURE__ */ jsx(
4230
+ "input",
4231
+ {
4232
+ id: htmlFor ?? name,
4233
+ name,
4234
+ onChange: (e) => commit(Array.from(e.target.files ?? [])),
4235
+ ref: inputRef,
4236
+ hidden: true,
4237
+ type: "file",
4238
+ accept,
4239
+ multiple: allowMultiple,
4240
+ disabled
4241
+ }
4242
+ ),
4243
+ /* @__PURE__ */ jsx(
4244
+ "span",
4245
+ {
4246
+ className: [
4247
+ "flex h-11 w-11 items-center justify-center rounded-full transition-colors duration-150",
4248
+ dragging ? "bg-accent text-accent-fg" : "bg-surface-raised text-foreground-secondary group-hover:text-foreground"
4249
+ ].join(" "),
4250
+ children: icon ?? UploadGlyph
4251
+ }
4252
+ ),
4253
+ /* @__PURE__ */ jsxs("div", { className: "space-y-0.5", children: [
4254
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-foreground", children: dragging ? "Drop to upload" : prompt }),
4255
+ hint && /* @__PURE__ */ jsx("div", { className: "text-xs text-foreground-muted", children: hint })
4256
+ ] })
4257
+ ]
4258
+ }
4259
+ ),
4260
+ files.length > 0 && /* @__PURE__ */ jsx("ul", { className: "mt-3 flex flex-col gap-2", children: files.map((file, idx) => /* @__PURE__ */ jsxs(
4261
+ "li",
4262
+ {
4263
+ className: "flex items-center gap-3 rounded-lg border border-border bg-surface px-3 py-2",
4264
+ children: [
4265
+ /* @__PURE__ */ jsx("span", { className: "flex-shrink-0 text-foreground-muted", children: FileGlyph }),
4266
+ /* @__PURE__ */ jsxs("span", { className: "flex-1 min-w-0", children: [
4267
+ /* @__PURE__ */ jsx("span", { className: "block text-sm text-foreground truncate", children: file.name }),
4268
+ /* @__PURE__ */ jsx("span", { className: "block text-xs text-foreground-muted", children: formatBytes(file.size) })
4269
+ ] }),
4270
+ /* @__PURE__ */ jsx(
4271
+ "button",
4272
+ {
4273
+ type: "button",
4274
+ onClick: (e) => {
4275
+ e.stopPropagation();
4276
+ removeFile(idx);
4277
+ },
4278
+ "aria-label": `Remove ${file.name}`,
4279
+ className: "flex-shrink-0 w-7 h-7 inline-flex items-center justify-center rounded-md text-foreground-muted hover:text-status-error hover:bg-surface-raised transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-accent",
4280
+ children: /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 20 20", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M15 5L5 15M5 5l10 10", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" }) })
4281
+ }
4282
+ )
4283
+ ]
4284
+ },
4285
+ `${idx}-${file.name}`
4286
+ )) })
4287
+ ]
4288
+ }
4007
4289
  );
4008
4290
  }
4009
4291
  var MONTH_NAMES = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
@@ -4058,7 +4340,8 @@ function DatePicker({
4058
4340
  style,
4059
4341
  format = defaultFormat,
4060
4342
  weekStartsOn = 0,
4061
- clearable = true
4343
+ clearable = true,
4344
+ size = "md"
4062
4345
  }) {
4063
4346
  const errorId = useId();
4064
4347
  const hasError = errorMessage != null;
@@ -4161,7 +4444,7 @@ function DatePicker({
4161
4444
  "aria-describedby": hasError ? errorId : void 0,
4162
4445
  "aria-haspopup": "dialog",
4163
4446
  "aria-expanded": open,
4164
- className: `flex items-center justify-between h-9 rounded-lg border px-3 cursor-pointer select-none focus:outline-none focus-visible:border-transparent focus-visible:ring-2 focus-visible:ring-accent ${hasError ? "border-status-error" : "border-border"} ${disabled ? "cursor-not-allowed bg-surface-raised text-foreground-muted" : "bg-surface text-foreground"} ${!style?.width ? "min-w-[200px]" : ""}`,
4447
+ className: `flex items-center justify-between cursor-pointer select-none ${!style?.width ? "min-w-[200px]" : ""} ${fieldShell({ size, hasError, disabled })}`,
4165
4448
  children: [
4166
4449
  /* @__PURE__ */ jsx("span", { className: `text-sm truncate ${displayValue ? "" : "text-foreground-muted"}`, children: displayValue || placeholder }),
4167
4450
  /* @__PURE__ */ jsx(CalendarIcon, {})
@@ -4347,6 +4630,6 @@ function ChevronRight3() {
4347
4630
  return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "w-4 h-4", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) });
4348
4631
  }
4349
4632
 
4350
- export { AppShell, AutoComplete, Avatar, Box, Button, Catalog, CatalogCarousel, CatalogGrid, Checkbox, ContextMenu, Drawer, Dropdown, FadingBase, FileInput, Flex, Grid2 as Grid, GridCard, icons_default as Icon, IconButton, List2 as List, LoadingSpinner, Modal, NotificationProvider, NumberInput, OpaqueGridCard, Password, Portal, ScalableContainer, SearchInput_default as SearchInput, Sidebar, SkeletonBox, SkeletonCard, SkeletonCircle, SkeletonText, Switch, Table, Tabs, DatePicker as Temporal, TextInput, ThemeProvider, ThemeSwitch, ToggleButton, Tooltip, TooltipProvider, TopBar, Tree, TreeSelect, Typography, Wizard, useNotification };
4633
+ export { AppShell, AutoComplete, Avatar, Box, Button, Catalog, CatalogCarousel, CatalogGrid, Checkbox, ContextMenu, Drawer, Dropdown, FadingBase, Field, FileInput, Flex, Grid2 as Grid, GridCard, icons_default as Icon, IconButton, List2 as List, LoadingSpinner, Modal, NotificationProvider, NumberInput, OpaqueGridCard, Password, Portal, RadioGroup, ScalableContainer, SearchInput_default as SearchInput, Sidebar, SkeletonBox, SkeletonCard, SkeletonCircle, SkeletonText, Switch, Table, Tabs, DatePicker as Temporal, TextInput, ThemeProvider, ThemeSwitch, ToggleButton, Tooltip, TooltipProvider, TopBar, Tree, TreeSelect, Typography, Wizard, fieldShell, useNotification };
4351
4634
  //# sourceMappingURL=index.js.map
4352
4635
  //# sourceMappingURL=index.js.map