@geomak/ui 5.2.0 → 5.4.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.cjs CHANGED
@@ -15,6 +15,8 @@ var ContextMenuPrimitive = require('@radix-ui/react-context-menu');
15
15
  var Popover = require('@radix-ui/react-popover');
16
16
  var SwitchPrimitive = require('@radix-ui/react-switch');
17
17
  var CheckboxPrimitive = require('@radix-ui/react-checkbox');
18
+ var RadioGroupPrimitive = require('@radix-ui/react-radio-group');
19
+ var SliderPrimitive = require('@radix-ui/react-slider');
18
20
 
19
21
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
20
22
 
@@ -47,6 +49,8 @@ var ContextMenuPrimitive__namespace = /*#__PURE__*/_interopNamespace(ContextMenu
47
49
  var Popover__namespace = /*#__PURE__*/_interopNamespace(Popover);
48
50
  var SwitchPrimitive__namespace = /*#__PURE__*/_interopNamespace(SwitchPrimitive);
49
51
  var CheckboxPrimitive__namespace = /*#__PURE__*/_interopNamespace(CheckboxPrimitive);
52
+ var RadioGroupPrimitive__namespace = /*#__PURE__*/_interopNamespace(RadioGroupPrimitive);
53
+ var SliderPrimitive__namespace = /*#__PURE__*/_interopNamespace(SliderPrimitive);
50
54
 
51
55
  var Moon = ({ color = "gray" }) => /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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" }) });
52
56
  var Sun = ({ color = "yellow" }) => /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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" }) });
@@ -2165,46 +2169,112 @@ function Wizard({
2165
2169
  ] }) })
2166
2170
  ] });
2167
2171
  }
2168
- var SearchInput = React8__default.default.forwardRef(function SearchInput2({
2169
- value,
2170
- onChange,
2171
- disabled,
2172
+ var FIELD_SIZE = {
2173
+ sm: { control: "h-control-sm", text: "text-xs", padX: "px-2.5", gap: "gap-1.5" },
2174
+ md: { control: "h-control-md", text: "text-sm", padX: "px-3", gap: "gap-2" },
2175
+ lg: { control: "h-control-lg", text: "text-sm", padX: "px-3.5", gap: "gap-2.5" }
2176
+ };
2177
+ var FOCUS_WITHIN = "focus-within:outline-none focus-within:border-accent focus-within:ring-[3px] focus-within:ring-focus-ring";
2178
+ var FOCUS_VISIBLE = "focus-visible:outline-none focus-visible:border-accent focus-visible:ring-[3px] focus-visible:ring-focus-ring";
2179
+ var FOCUS_WITHIN_ERROR = "focus-within:border-status-error focus-within:ring-focus-ring-error";
2180
+ var FOCUS_VISIBLE_ERROR = "focus-visible:border-status-error focus-visible:ring-focus-ring-error";
2181
+ function fieldShell({
2182
+ size = "md",
2183
+ hasError = false,
2184
+ disabled = false,
2185
+ focusWithin = false,
2186
+ sized = true
2187
+ } = {}) {
2188
+ const s = FIELD_SIZE[size];
2189
+ return [
2190
+ "w-full rounded-lg border bg-surface text-foreground",
2191
+ "transition-[color,box-shadow,border-color] duration-150",
2192
+ s.text,
2193
+ sized ? `${s.control} ${s.padX}` : "",
2194
+ // resting border
2195
+ hasError ? "border-status-error" : "border-border",
2196
+ // hover (only when interactive + no error)
2197
+ disabled ? "bg-surface-raised text-foreground-muted cursor-not-allowed" : hasError ? "" : "hover:border-border-strong",
2198
+ // focus
2199
+ focusWithin ? FOCUS_WITHIN : FOCUS_VISIBLE,
2200
+ hasError ? focusWithin ? FOCUS_WITHIN_ERROR : FOCUS_VISIBLE_ERROR : "",
2201
+ // placeholder colour for native inputs
2202
+ "placeholder:text-foreground-muted"
2203
+ ].filter(Boolean).join(" ");
2204
+ }
2205
+ function Field({
2172
2206
  label,
2173
2207
  htmlFor,
2174
- placeholder,
2175
- name,
2176
- inputStyle,
2177
- style,
2178
- layout = "vertical"
2179
- }, ref) {
2180
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex flex-col items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs(
2208
+ errorId,
2209
+ errorMessage,
2210
+ layout = "vertical",
2211
+ required,
2212
+ labelStyle,
2213
+ labelWidth,
2214
+ className = "",
2215
+ children
2216
+ }) {
2217
+ const hasError = errorMessage != null;
2218
+ const horizontal = layout === "horizontal";
2219
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2181
2220
  "div",
2182
2221
  {
2183
- className: `flex ${layout === "vertical" ? "flex-col" : "flex-row items-center gap-2"}`,
2184
- style: style ?? {},
2222
+ className: [
2223
+ "flex",
2224
+ horizontal ? "flex-row items-start gap-3" : "flex-col gap-1.5",
2225
+ className
2226
+ ].filter(Boolean).join(" "),
2185
2227
  children: [
2186
- label && /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium ml-1 max-content text-foreground", htmlFor, children: label }),
2187
- /* @__PURE__ */ jsxRuntime.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: [
2188
- /* @__PURE__ */ jsxRuntime.jsx(
2189
- "input",
2190
- {
2191
- ref,
2192
- disabled,
2193
- value,
2194
- onChange,
2195
- type: "search",
2196
- enterKeyHint: "search",
2197
- name,
2198
- id: htmlFor,
2199
- className: "bg-transparent focus:outline-none pl-2 h-9 w-56 rounded-lg disabled:cursor-not-allowed",
2200
- style: inputStyle ?? {},
2201
- placeholder: placeholder ?? ""
2202
- }
2203
- ),
2204
- /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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" }) })
2228
+ label && /* @__PURE__ */ jsxRuntime.jsxs(
2229
+ "label",
2230
+ {
2231
+ htmlFor,
2232
+ style: { width: horizontal ? labelWidth : void 0, ...labelStyle },
2233
+ className: [
2234
+ "text-sm font-medium text-foreground select-none",
2235
+ horizontal ? "mt-2 flex-shrink-0" : ""
2236
+ ].filter(Boolean).join(" "),
2237
+ children: [
2238
+ label,
2239
+ required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-status-error ml-0.5", "aria-hidden": "true", children: "*" })
2240
+ ]
2241
+ }
2242
+ ),
2243
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col min-w-0 flex-1", children: [
2244
+ children,
2245
+ hasError && /* @__PURE__ */ jsxRuntime.jsx("div", { id: errorId, className: "text-status-error text-xs mt-1", children: errorMessage })
2205
2246
  ] })
2206
2247
  ]
2207
2248
  }
2249
+ );
2250
+ }
2251
+ var SearchIcon = /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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" }) });
2252
+ var SearchInput = React8__default.default.forwardRef(function SearchInput2({ value, onChange, disabled, label, htmlFor, placeholder, name, inputStyle, style, layout = "vertical", size = "md", icon }, ref) {
2253
+ return /* @__PURE__ */ jsxRuntime.jsx(Field, { label, htmlFor, layout, children: /* @__PURE__ */ jsxRuntime.jsxs(
2254
+ "div",
2255
+ {
2256
+ className: `flex items-center ${fieldShell({ size, disabled, focusWithin: true })}`,
2257
+ style,
2258
+ children: [
2259
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0 mr-2 text-foreground-muted", children: icon ?? SearchIcon }),
2260
+ /* @__PURE__ */ jsxRuntime.jsx(
2261
+ "input",
2262
+ {
2263
+ ref,
2264
+ disabled,
2265
+ value,
2266
+ onChange,
2267
+ type: "search",
2268
+ enterKeyHint: "search",
2269
+ name,
2270
+ id: htmlFor,
2271
+ className: "min-w-0 flex-1 bg-transparent outline-none disabled:cursor-not-allowed placeholder:text-foreground-muted",
2272
+ style: inputStyle,
2273
+ placeholder: placeholder ?? ""
2274
+ }
2275
+ )
2276
+ ]
2277
+ }
2208
2278
  ) });
2209
2279
  });
2210
2280
  var SearchInput_default = SearchInput;
@@ -2232,7 +2302,8 @@ function Dropdown({
2232
2302
  items = [],
2233
2303
  labelStyle = {},
2234
2304
  placeholder,
2235
- showSelectedCount = false
2305
+ showSelectedCount = false,
2306
+ size = "md"
2236
2307
  }) {
2237
2308
  const [open, setOpen] = React8.useState(false);
2238
2309
  const [selectedItems, setSelectedItems] = React8.useState([]);
@@ -2295,7 +2366,7 @@ function Dropdown({
2295
2366
  "aria-invalid": hasError || void 0,
2296
2367
  "aria-describedby": hasError ? errorId : void 0,
2297
2368
  style,
2298
- 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" : ""}`,
2369
+ className: `flex items-center justify-between cursor-pointer select-none ${fieldShell({ size, hasError, disabled })}`,
2299
2370
  tabIndex: disabled ? -1 : 0,
2300
2371
  onKeyDown: (e) => {
2301
2372
  if (disabled) return;
@@ -2308,7 +2379,7 @@ function Dropdown({
2308
2379
  /* @__PURE__ */ jsxRuntime.jsx(
2309
2380
  "div",
2310
2381
  {
2311
- className: `h-7 pl-2 ${!style?.width ? "min-w-[240px]" : ""} flex items-center gap-1 overflow-hidden`,
2382
+ className: `${!style?.width ? "min-w-[200px]" : ""} flex items-center gap-1 overflow-hidden`,
2312
2383
  children: !value || Array.isArray(value) && value.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-foreground-muted text-sm", children: placeholder }) : Array.isArray(value) ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2313
2384
  value.slice(0, 1).map((val) => /* @__PURE__ */ jsxRuntime.jsx(
2314
2385
  DropdownPill,
@@ -2322,7 +2393,7 @@ function Dropdown({
2322
2393
  ] }) : /* @__PURE__ */ jsxRuntime.jsx(DropdownPill, { value: innerItems.find((it) => it.key === value)?.label })
2323
2394
  }
2324
2395
  ),
2325
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: `transition-transform duration-200 mr-2 ${open ? "rotate-180" : "rotate-0"}`, "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) }) })
2396
+ /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) }) })
2326
2397
  ]
2327
2398
  }
2328
2399
  ) }),
@@ -3246,58 +3317,61 @@ function TextInput({
3246
3317
  htmlFor,
3247
3318
  placeholder,
3248
3319
  name,
3320
+ type = "text",
3249
3321
  inputStyle,
3250
3322
  style,
3251
- layout,
3323
+ layout = "vertical",
3324
+ size = "md",
3252
3325
  onBlur,
3253
3326
  errorMessage,
3254
- labelColor
3327
+ required,
3328
+ prefix,
3329
+ suffix
3255
3330
  }) {
3256
3331
  const errorId = React8.useId();
3257
3332
  const hasError = errorMessage != null;
3258
- return (
3259
- // In horizontal mode the row layout is [label, input-with-error-column].
3260
- // The error sits under the input ONLY, not spanning the label too.
3261
- // In vertical mode the whole thing is a column.
3262
- /* @__PURE__ */ jsxRuntime.jsxs(
3263
- "div",
3264
- {
3265
- className: `flex ${layout === "vertical" ? "flex-col gap-1" : "flex-row items-start gap-2"}`,
3266
- style: style ?? {},
3267
- children: [
3268
- label && /* @__PURE__ */ jsxRuntime.jsx(
3269
- "label",
3270
- {
3271
- style: { color: labelColor || void 0 },
3272
- className: `text-sm font-medium ${layout === "horizontal" ? "mt-2" : ""} max-content ${!labelColor && "text-foreground"}`,
3273
- htmlFor,
3274
- children: label
3275
- }
3276
- ),
3277
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
3278
- /* @__PURE__ */ jsxRuntime.jsx(
3279
- "input",
3280
- {
3281
- autoComplete: "off",
3282
- disabled,
3283
- value,
3284
- onChange,
3285
- onBlur,
3286
- type: "text",
3287
- name,
3288
- id: htmlFor,
3289
- "aria-invalid": hasError || void 0,
3290
- "aria-describedby": hasError ? errorId : void 0,
3291
- className: `${hasError ? "border border-status-error" : "border border-border"} bg-surface text-foreground p-2 h-9 w-60 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`,
3292
- style: inputStyle ?? {},
3293
- placeholder: placeholder ?? ""
3294
- }
3295
- ),
3296
- hasError && /* @__PURE__ */ jsxRuntime.jsx("div", { id: errorId, className: "text-status-error text-xs mt-1", children: errorMessage })
3297
- ] })
3298
- ]
3299
- }
3300
- )
3333
+ const hasAdornment = prefix != null || suffix != null;
3334
+ const input = /* @__PURE__ */ jsxRuntime.jsx(
3335
+ "input",
3336
+ {
3337
+ autoComplete: "off",
3338
+ disabled,
3339
+ value,
3340
+ onChange,
3341
+ onBlur,
3342
+ type,
3343
+ name,
3344
+ id: htmlFor,
3345
+ "aria-invalid": hasError || void 0,
3346
+ "aria-describedby": hasError ? errorId : void 0,
3347
+ placeholder: placeholder ?? "",
3348
+ className: hasAdornment ? "min-w-0 flex-1 bg-transparent outline-none disabled:cursor-not-allowed placeholder:text-foreground-muted" : fieldShell({ size, hasError, disabled }),
3349
+ style: inputStyle
3350
+ }
3351
+ );
3352
+ return /* @__PURE__ */ jsxRuntime.jsx(
3353
+ Field,
3354
+ {
3355
+ label,
3356
+ htmlFor,
3357
+ errorId,
3358
+ errorMessage,
3359
+ layout,
3360
+ required,
3361
+ className: style ? void 0 : void 0,
3362
+ children: hasAdornment ? /* @__PURE__ */ jsxRuntime.jsxs(
3363
+ "div",
3364
+ {
3365
+ className: `flex items-center ${fieldShell({ size, hasError, disabled, focusWithin: true })}`,
3366
+ style,
3367
+ children: [
3368
+ prefix && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0 mr-2 text-foreground-muted", children: prefix }),
3369
+ input,
3370
+ suffix && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0 ml-2 text-foreground-muted", children: suffix })
3371
+ ]
3372
+ }
3373
+ ) : input
3374
+ }
3301
3375
  );
3302
3376
  }
3303
3377
  function NumberInput({
@@ -3308,8 +3382,10 @@ function NumberInput({
3308
3382
  htmlFor,
3309
3383
  name,
3310
3384
  disabled,
3311
- layout = "horizontal",
3385
+ layout = "vertical",
3386
+ size = "md",
3312
3387
  errorMessage,
3388
+ required,
3313
3389
  inputStyle,
3314
3390
  labelStyle,
3315
3391
  placeholder,
@@ -3350,22 +3426,21 @@ function NumberInput({
3350
3426
  if (Number.isNaN(parsed)) return;
3351
3427
  onChange?.({ target: { value: round(parsed), id: htmlFor, name } });
3352
3428
  };
3353
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
3354
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex ${layout === "vertical" ? "flex-col gap-1" : "flex-row items-center gap-2"}`, children: [
3355
- label && /* @__PURE__ */ jsxRuntime.jsx(
3356
- "label",
3357
- {
3358
- className: "text-sm font-medium ml-1 max-content select-none text-foreground",
3359
- style: labelStyle,
3360
- htmlFor,
3361
- children: label
3362
- }
3363
- ),
3364
- /* @__PURE__ */ jsxRuntime.jsxs(
3429
+ return /* @__PURE__ */ jsxRuntime.jsx(
3430
+ Field,
3431
+ {
3432
+ label,
3433
+ htmlFor,
3434
+ errorId,
3435
+ errorMessage,
3436
+ layout,
3437
+ required,
3438
+ labelStyle,
3439
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
3365
3440
  "div",
3366
3441
  {
3367
3442
  style,
3368
- 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`,
3443
+ className: `flex items-center overflow-hidden pr-0 ${fieldShell({ size, hasError, disabled, focusWithin: true })}`,
3369
3444
  children: [
3370
3445
  /* @__PURE__ */ jsxRuntime.jsx(
3371
3446
  "input",
@@ -3382,13 +3457,13 @@ function NumberInput({
3382
3457
  type: "number",
3383
3458
  "aria-invalid": hasError || void 0,
3384
3459
  "aria-describedby": hasError ? errorId : void 0,
3385
- 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",
3386
- style: inputStyle ?? {},
3460
+ 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",
3461
+ style: inputStyle,
3387
3462
  placeholder: placeholder ?? "",
3388
3463
  readOnly
3389
3464
  }
3390
3465
  ),
3391
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col border-l border-border h-9", children: [
3466
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col self-stretch border-l border-border flex-shrink-0", children: [
3392
3467
  /* @__PURE__ */ jsxRuntime.jsx(
3393
3468
  "button",
3394
3469
  {
@@ -3397,7 +3472,7 @@ function NumberInput({
3397
3472
  onClick: onIncrement,
3398
3473
  disabled: disabled || readOnly || max !== void 0 && numeric >= max,
3399
3474
  "aria-label": "Increase value",
3400
- 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",
3475
+ 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",
3401
3476
  children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, className: "h-3 w-3", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 15l7-7 7 7" }) })
3402
3477
  }
3403
3478
  ),
@@ -3409,7 +3484,7 @@ function NumberInput({
3409
3484
  onClick: onDecrement,
3410
3485
  disabled: disabled || readOnly || min !== void 0 && numeric <= min,
3411
3486
  "aria-label": "Decrease value",
3412
- 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",
3487
+ 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",
3413
3488
  children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, className: "h-3 w-3", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })
3414
3489
  }
3415
3490
  )
@@ -3417,10 +3492,18 @@ function NumberInput({
3417
3492
  ]
3418
3493
  }
3419
3494
  )
3420
- ] }),
3421
- hasError && /* @__PURE__ */ jsxRuntime.jsx("div", { id: errorId, className: "text-xs text-status-error ml-1", children: errorMessage })
3422
- ] });
3495
+ }
3496
+ );
3423
3497
  }
3498
+ var EyeIcon = /* @__PURE__ */ jsxRuntime.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: [
3499
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 15a3 3 0 100-6 3 3 0 000 6z" }),
3500
+ /* @__PURE__ */ jsxRuntime.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" })
3501
+ ] });
3502
+ var EyeSlashIcon = /* @__PURE__ */ jsxRuntime.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: [
3503
+ /* @__PURE__ */ jsxRuntime.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" }),
3504
+ /* @__PURE__ */ jsxRuntime.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" }),
3505
+ /* @__PURE__ */ jsxRuntime.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" })
3506
+ ] });
3424
3507
  function Password({
3425
3508
  value,
3426
3509
  onChange,
@@ -3431,32 +3514,32 @@ function Password({
3431
3514
  name,
3432
3515
  inputStyle,
3433
3516
  style,
3434
- layout,
3517
+ layout = "vertical",
3518
+ size = "md",
3435
3519
  onBlur,
3436
3520
  errorMessage,
3437
- labelColor,
3438
- iconColor
3521
+ required,
3522
+ showIcon,
3523
+ hideIcon
3439
3524
  }) {
3440
- const [passwordVisible, setPasswordVisible] = React8.useState(false);
3525
+ const [visible, setVisible] = React8.useState(false);
3441
3526
  const errorId = React8.useId();
3442
3527
  const hasError = errorMessage != null;
3443
- return /* @__PURE__ */ jsxRuntime.jsxs(
3444
- "div",
3528
+ return /* @__PURE__ */ jsxRuntime.jsx(
3529
+ Field,
3445
3530
  {
3446
- className: `flex ${layout === "vertical" ? "flex-col gap-1" : "flex-row items-start gap-2"}`,
3447
- style: style ?? {},
3448
- children: [
3449
- label && /* @__PURE__ */ jsxRuntime.jsx(
3450
- "label",
3451
- {
3452
- style: { color: labelColor || void 0 },
3453
- className: `text-sm font-medium ${layout === "horizontal" ? "mt-2" : ""} max-content ${!labelColor && "text-foreground"}`,
3454
- htmlFor,
3455
- children: label
3456
- }
3457
- ),
3458
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
3459
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
3531
+ label,
3532
+ htmlFor,
3533
+ errorId,
3534
+ errorMessage,
3535
+ layout,
3536
+ required,
3537
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
3538
+ "div",
3539
+ {
3540
+ className: `flex items-center ${fieldShell({ size, hasError, disabled, focusWithin: true })}`,
3541
+ style,
3542
+ children: [
3460
3543
  /* @__PURE__ */ jsxRuntime.jsx(
3461
3544
  "input",
3462
3545
  {
@@ -3465,44 +3548,30 @@ function Password({
3465
3548
  value,
3466
3549
  onChange,
3467
3550
  onBlur,
3468
- type: passwordVisible ? "text" : "password",
3551
+ type: visible ? "text" : "password",
3469
3552
  name,
3470
3553
  id: htmlFor,
3471
3554
  "aria-invalid": hasError || void 0,
3472
3555
  "aria-describedby": hasError ? errorId : void 0,
3473
- className: `${hasError ? "border border-status-error" : "border border-border"} bg-surface text-foreground p-2 h-9 w-52 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`,
3474
- style: inputStyle ?? {},
3475
- placeholder: placeholder ?? ""
3556
+ placeholder: placeholder ?? "",
3557
+ className: "min-w-0 flex-1 bg-transparent outline-none disabled:cursor-not-allowed placeholder:text-foreground-muted",
3558
+ style: inputStyle
3476
3559
  }
3477
3560
  ),
3478
3561
  /* @__PURE__ */ jsxRuntime.jsx(
3479
3562
  "button",
3480
3563
  {
3481
3564
  type: "button",
3482
- className: "cursor-pointer p-1 text-foreground-secondary hover:text-foreground rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-accent",
3483
- style: iconColor ? { color: iconColor } : void 0,
3484
- onClick: () => setPasswordVisible(!passwordVisible),
3485
- "aria-label": passwordVisible ? "Hide password" : "Show password",
3486
- children: passwordVisible ? (
3487
- /* EyeSlash */
3488
- /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-6 h-6", children: [
3489
- /* @__PURE__ */ jsxRuntime.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" }),
3490
- /* @__PURE__ */ jsxRuntime.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" }),
3491
- /* @__PURE__ */ jsxRuntime.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" })
3492
- ] })
3493
- ) : (
3494
- /* Eye */
3495
- /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-6 h-6", children: [
3496
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 15a3 3 0 100-6 3 3 0 000 6z" }),
3497
- /* @__PURE__ */ jsxRuntime.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" })
3498
- ] })
3499
- )
3565
+ disabled,
3566
+ 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`,
3567
+ onClick: () => setVisible((v) => !v),
3568
+ "aria-label": visible ? "Hide password" : "Show password",
3569
+ children: visible ? hideIcon ?? EyeSlashIcon : showIcon ?? EyeIcon
3500
3570
  }
3501
3571
  )
3502
- ] }),
3503
- hasError && /* @__PURE__ */ jsxRuntime.jsx("div", { id: errorId, className: "text-status-error text-xs mt-1", children: errorMessage })
3504
- ] })
3505
- ]
3572
+ ]
3573
+ }
3574
+ )
3506
3575
  }
3507
3576
  );
3508
3577
  }
@@ -3585,6 +3654,96 @@ function Checkbox({
3585
3654
  errorMessage && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-status-error pl-[26px]", children: errorMessage })
3586
3655
  ] });
3587
3656
  }
3657
+ var DOT_SIZE = {
3658
+ sm: "h-3.5 w-3.5",
3659
+ md: "h-4 w-4",
3660
+ lg: "h-5 w-5"
3661
+ };
3662
+ var TEXT_SIZE = {
3663
+ sm: "text-xs",
3664
+ md: "text-sm",
3665
+ lg: "text-base"
3666
+ };
3667
+ function RadioGroup({
3668
+ options,
3669
+ value,
3670
+ defaultValue,
3671
+ onChange,
3672
+ name,
3673
+ label,
3674
+ orientation = "vertical",
3675
+ size = "md",
3676
+ disabled,
3677
+ required,
3678
+ errorMessage
3679
+ }) {
3680
+ const errorId = React8.useId();
3681
+ const groupId = React8.useId();
3682
+ const hasError = errorMessage != null;
3683
+ return /* @__PURE__ */ jsxRuntime.jsx(
3684
+ Field,
3685
+ {
3686
+ label,
3687
+ htmlFor: groupId,
3688
+ errorId,
3689
+ errorMessage,
3690
+ required,
3691
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3692
+ RadioGroupPrimitive__namespace.Root,
3693
+ {
3694
+ id: groupId,
3695
+ name,
3696
+ value,
3697
+ defaultValue,
3698
+ onValueChange: onChange,
3699
+ disabled,
3700
+ required,
3701
+ "aria-invalid": hasError || void 0,
3702
+ "aria-describedby": hasError ? errorId : void 0,
3703
+ orientation,
3704
+ className: orientation === "horizontal" ? "flex flex-row flex-wrap gap-5" : "flex flex-col gap-3",
3705
+ children: options.map((opt) => {
3706
+ const itemId = `${groupId}-${opt.value}`;
3707
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2.5", children: [
3708
+ /* @__PURE__ */ jsxRuntime.jsx(
3709
+ RadioGroupPrimitive__namespace.Item,
3710
+ {
3711
+ id: itemId,
3712
+ value: opt.value,
3713
+ disabled: opt.disabled,
3714
+ className: [
3715
+ DOT_SIZE[size],
3716
+ "mt-0.5 flex-shrink-0 rounded-full border bg-surface transition-colors duration-150",
3717
+ "border-border-strong",
3718
+ "hover:border-accent",
3719
+ "data-[state=checked]:border-accent",
3720
+ "focus:outline-none focus-visible:ring-[3px] focus-visible:ring-focus-ring",
3721
+ "disabled:cursor-not-allowed disabled:opacity-50"
3722
+ ].join(" "),
3723
+ children: /* @__PURE__ */ jsxRuntime.jsx(RadioGroupPrimitive__namespace.Indicator, { className: "flex h-full w-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block h-1/2 w-1/2 rounded-full bg-accent" }) })
3724
+ }
3725
+ ),
3726
+ /* @__PURE__ */ jsxRuntime.jsxs(
3727
+ "label",
3728
+ {
3729
+ htmlFor: itemId,
3730
+ className: [
3731
+ "select-none",
3732
+ opt.disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer"
3733
+ ].join(" "),
3734
+ children: [
3735
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `block ${TEXT_SIZE[size]} text-foreground`, children: opt.label }),
3736
+ opt.description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block text-xs text-foreground-secondary mt-0.5", children: opt.description })
3737
+ ]
3738
+ }
3739
+ )
3740
+ ] }, opt.value);
3741
+ })
3742
+ }
3743
+ )
3744
+ }
3745
+ );
3746
+ }
3588
3747
  function Switch({
3589
3748
  checked = false,
3590
3749
  onChange,
@@ -3622,7 +3781,9 @@ function AutoComplete({
3622
3781
  debounce = 250,
3623
3782
  onItemClick,
3624
3783
  emptyText = "No results found",
3625
- loadingText = "Searching\u2026"
3784
+ loadingText = "Searching\u2026",
3785
+ size = "md",
3786
+ icon
3626
3787
  }) {
3627
3788
  const [term, setTerm] = React8.useState("");
3628
3789
  const [open, setOpen] = React8.useState(false);
@@ -3670,15 +3831,15 @@ function AutoComplete({
3670
3831
  onItemClick?.(item.value);
3671
3832
  setOpen(false);
3672
3833
  };
3673
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex flex-col items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs(
3834
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3674
3835
  "div",
3675
3836
  {
3676
- className: `flex ${layout === "vertical" ? "flex-col" : "flex-row items-center gap-2"}`,
3677
- style: style ?? {},
3837
+ className: `flex ${layout === "vertical" ? "flex-col gap-1.5" : "flex-row items-start gap-3"}`,
3838
+ style,
3678
3839
  children: [
3679
- label && /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium ml-1 max-content text-foreground", children: label }),
3680
- /* @__PURE__ */ jsxRuntime.jsxs(Popover__namespace.Root, { open: open && !disabled, onOpenChange: (o) => !disabled && setOpen(o), children: [
3681
- /* @__PURE__ */ jsxRuntime.jsx(Popover__namespace.Anchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.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: [
3840
+ label && /* @__PURE__ */ jsxRuntime.jsx("label", { className: `text-sm font-medium text-foreground select-none ${layout === "horizontal" ? "mt-2 flex-shrink-0" : ""}`, children: label }),
3841
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col min-w-0 flex-1", children: /* @__PURE__ */ jsxRuntime.jsxs(Popover__namespace.Root, { open: open && !disabled, onOpenChange: (o) => !disabled && setOpen(o), children: [
3842
+ /* @__PURE__ */ jsxRuntime.jsx(Popover__namespace.Anchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center ${fieldShell({ size, disabled, focusWithin: true })}`, children: [
3682
3843
  /* @__PURE__ */ jsxRuntime.jsx(
3683
3844
  "input",
3684
3845
  {
@@ -3691,8 +3852,8 @@ function AutoComplete({
3691
3852
  onFocus: () => setOpen(true),
3692
3853
  type: "text",
3693
3854
  name,
3694
- className: "bg-transparent focus:outline-none pl-2 h-9 w-56 rounded-lg disabled:cursor-not-allowed",
3695
- style: inputStyle ?? {},
3855
+ className: "min-w-0 flex-1 bg-transparent outline-none disabled:cursor-not-allowed placeholder:text-foreground-muted",
3856
+ style: inputStyle,
3696
3857
  placeholder: placeholder ?? "",
3697
3858
  autoComplete: "off",
3698
3859
  "aria-haspopup": "listbox",
@@ -3701,7 +3862,7 @@ function AutoComplete({
3701
3862
  "aria-busy": loading || void 0
3702
3863
  }
3703
3864
  ),
3704
- loading ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-5 h-5 flex-shrink-0 flex items-center justify-center text-accent", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner, { inline: true, size: "xs", spinnerColor: "currentColor" }) }) : /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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" }) })
3865
+ loading ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-2 w-4 h-4 flex-shrink-0 flex items-center justify-center text-accent", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner, { inline: true, size: "xs", spinnerColor: "currentColor" }) }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-2 flex-shrink-0 text-foreground-muted", children: icon ?? /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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" }) }) })
3705
3866
  ] }) }),
3706
3867
  /* @__PURE__ */ jsxRuntime.jsx(Popover__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
3707
3868
  Popover__namespace.Content,
@@ -3740,10 +3901,10 @@ function AutoComplete({
3740
3901
  )) })
3741
3902
  }
3742
3903
  ) })
3743
- ] })
3904
+ ] }) })
3744
3905
  ]
3745
3906
  }
3746
- ) });
3907
+ );
3747
3908
  }
3748
3909
  function flattenVisible(items, expanded, depth = 0, out = []) {
3749
3910
  for (const node of items) {
@@ -3778,7 +3939,8 @@ function TreeSelect({
3778
3939
  items = [],
3779
3940
  placeholder = "Select\u2026",
3780
3941
  parentsSelectable = true,
3781
- defaultExpandedKeys = []
3942
+ defaultExpandedKeys = [],
3943
+ size = "md"
3782
3944
  }) {
3783
3945
  const errorId = React8.useId();
3784
3946
  const hasError = errorMessage != null;
@@ -3877,7 +4039,7 @@ function TreeSelect({
3877
4039
  "aria-invalid": hasError || void 0,
3878
4040
  "aria-describedby": hasError ? errorId : void 0,
3879
4041
  disabled,
3880
- 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]" : ""}`,
4042
+ className: `flex items-center justify-between cursor-pointer select-none ${!style?.width ? "min-w-[240px]" : ""} ${fieldShell({ size, hasError, disabled })}`,
3881
4043
  children: [
3882
4044
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm truncate text-left", children: selectedNode ? selectedNode.label : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-foreground-muted", children: placeholder }) }),
3883
4045
  /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })
@@ -4004,111 +4166,162 @@ function TreeNodeRow({
4004
4166
  }
4005
4167
  );
4006
4168
  }
4169
+ function formatBytes(bytes) {
4170
+ if (bytes === 0) return "0 B";
4171
+ const units = ["B", "KB", "MB", "GB"];
4172
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
4173
+ return `${(bytes / Math.pow(1024, i)).toFixed(i === 0 ? 0 : 1)} ${units[i]}`;
4174
+ }
4175
+ var UploadGlyph = /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.5, className: "w-6 h-6", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 16V4m0 0L8 8m4-4l4 4M4 16v2a2 2 0 002 2h12a2 2 0 002-2v-2" }) });
4176
+ var FileGlyph = /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.5, className: "w-5 h-5", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.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" }) });
4007
4177
  function FileInput({
4008
4178
  allowMultiple = false,
4009
4179
  onChange,
4010
4180
  name,
4011
- accept = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,.xlsx"
4181
+ htmlFor,
4182
+ label,
4183
+ accept,
4184
+ prompt = "Click to upload or drag and drop",
4185
+ hint,
4186
+ maxSize,
4187
+ errorMessage,
4188
+ disabled,
4189
+ required,
4190
+ icon
4012
4191
  }) {
4013
- const fileInput = React8.useRef(null);
4192
+ const inputRef = React8.useRef(null);
4193
+ const errorId = React8.useId();
4014
4194
  const [files, setFiles] = React8.useState([]);
4195
+ const [dragging, setDragging] = React8.useState(false);
4196
+ const [sizeError, setSizeError] = React8.useState(null);
4197
+ const effectiveError = errorMessage ?? sizeError ?? void 0;
4015
4198
  const openPicker = () => {
4016
- fileInput.current?.click();
4199
+ if (!disabled) inputRef.current?.click();
4017
4200
  };
4018
- const handleFiles = (list) => {
4201
+ const commit = (list) => {
4202
+ if (maxSize != null) {
4203
+ const tooBig = list.find((f) => f.size > maxSize);
4204
+ if (tooBig) {
4205
+ setSizeError(`"${tooBig.name}" exceeds the ${formatBytes(maxSize)} limit`);
4206
+ return;
4207
+ }
4208
+ }
4209
+ setSizeError(null);
4019
4210
  setFiles(list);
4020
- onChange?.({ target: { files: list } });
4211
+ onChange?.({ target: { files: list, name, id: htmlFor ?? name } });
4021
4212
  };
4022
4213
  const onDrop = (e) => {
4023
4214
  e.preventDefault();
4024
- const fileList = [];
4025
- if (e.dataTransfer.items) {
4026
- for (let i = 0; i < e.dataTransfer.items.length; i++) {
4027
- if (e.dataTransfer.items[i].kind === "file") {
4028
- const f = e.dataTransfer.items[i].getAsFile();
4029
- if (f) fileList.push(f);
4030
- }
4031
- }
4032
- } else {
4033
- for (let i = 0; i < e.dataTransfer.files.length; i++) {
4034
- fileList.push(e.dataTransfer.files[i]);
4035
- }
4036
- }
4037
- handleFiles(fileList);
4038
- };
4039
- const localOnChange = (e) => {
4040
- handleFiles(Array.from(e.target.files ?? []));
4215
+ setDragging(false);
4216
+ if (disabled) return;
4217
+ const dropped = Array.from(e.dataTransfer.files ?? []);
4218
+ commit(allowMultiple ? dropped : dropped.slice(0, 1));
4041
4219
  };
4042
- const removeFile = (e) => {
4043
- e.stopPropagation();
4044
- setFiles([]);
4045
- onChange?.({ target: { files: [], name, id: name, value: "" } });
4046
- if (fileInput.current) fileInput.current.value = "";
4220
+ const removeFile = (idx) => {
4221
+ const next = files.filter((_, i) => i !== idx);
4222
+ setFiles(next);
4223
+ setSizeError(null);
4224
+ onChange?.({ target: { files: next, name, id: htmlFor ?? name, value: "" } });
4225
+ if (next.length === 0 && inputRef.current) inputRef.current.value = "";
4047
4226
  };
4048
- return (
4049
- // Dropzone is keyboard-activatable: role="button", focusable via
4050
- // tabIndex, and Space/Enter trigger the file picker. Without these
4051
- // a keyboard-only user could not upload a file.
4052
- /* @__PURE__ */ jsxRuntime.jsxs(
4053
- "div",
4054
- {
4055
- role: "button",
4056
- tabIndex: 0,
4057
- "aria-label": "Upload file \u2014 click or drop",
4058
- onClick: openPicker,
4059
- onKeyDown: (e) => {
4060
- if (e.key === "Enter" || e.key === " ") {
4061
- e.preventDefault();
4062
- openPicker();
4063
- }
4064
- },
4065
- 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",
4066
- onDragOver: (e) => e.preventDefault(),
4067
- onDrop,
4068
- children: [
4069
- /* @__PURE__ */ jsxRuntime.jsx(
4070
- "input",
4071
- {
4072
- id: name,
4073
- name,
4074
- onChange: localOnChange,
4075
- ref: fileInput,
4076
- hidden: true,
4077
- type: "file",
4078
- accept,
4079
- multiple: allowMultiple
4080
- }
4081
- ),
4082
- files.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col h-full items-center justify-center gap-2", children: [
4083
- /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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" }) }),
4084
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm", children: "Click or Drop a file" })
4085
- ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-3 items-center justify-center w-full h-full p-3", children: files.map((file, id) => /* @__PURE__ */ jsxRuntime.jsxs(
4086
- "div",
4087
- {
4088
- className: "text-xs flex flex-col items-center w-20 h-24 text-center bg-surface-raised text-foreground p-4 rounded-md relative",
4089
- children: [
4090
- /* @__PURE__ */ jsxRuntime.jsx(
4091
- "button",
4092
- {
4093
- type: "button",
4094
- onClick: removeFile,
4095
- 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",
4096
- "aria-label": "Remove file",
4097
- children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "10", height: "10", viewBox: "0 0 20 20", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15 5L5 15M5 5l10 10", stroke: "#fff", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
4098
- }
4099
- ),
4100
- /* @__PURE__ */ jsxRuntime.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: [
4101
- /* @__PURE__ */ jsxRuntime.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" }),
4102
- /* @__PURE__ */ jsxRuntime.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" })
4103
- ] }),
4104
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-ellipsis whitespace-nowrap overflow-hidden w-full", children: file.name })
4105
- ]
4227
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4228
+ Field,
4229
+ {
4230
+ label,
4231
+ htmlFor,
4232
+ errorId,
4233
+ errorMessage: effectiveError,
4234
+ required,
4235
+ children: [
4236
+ /* @__PURE__ */ jsxRuntime.jsxs(
4237
+ "div",
4238
+ {
4239
+ role: "button",
4240
+ tabIndex: disabled ? -1 : 0,
4241
+ "aria-label": typeof prompt === "string" ? prompt : "Upload file",
4242
+ "aria-disabled": disabled || void 0,
4243
+ "aria-invalid": effectiveError != null || void 0,
4244
+ "aria-describedby": effectiveError != null ? errorId : void 0,
4245
+ onClick: openPicker,
4246
+ onKeyDown: (e) => {
4247
+ if (disabled) return;
4248
+ if (e.key === "Enter" || e.key === " ") {
4249
+ e.preventDefault();
4250
+ openPicker();
4251
+ }
4106
4252
  },
4107
- `${id}${file.name}`
4108
- )) })
4109
- ]
4110
- }
4111
- )
4253
+ onDragOver: (e) => {
4254
+ e.preventDefault();
4255
+ if (!disabled) setDragging(true);
4256
+ },
4257
+ onDragLeave: () => setDragging(false),
4258
+ onDrop,
4259
+ className: [
4260
+ "group flex flex-col items-center justify-center gap-3 w-full rounded-xl border border-dashed px-6 py-8 text-center",
4261
+ "transition-colors duration-150 focus:outline-none focus-visible:ring-[3px] focus-visible:ring-focus-ring",
4262
+ 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"
4263
+ ].join(" "),
4264
+ children: [
4265
+ /* @__PURE__ */ jsxRuntime.jsx(
4266
+ "input",
4267
+ {
4268
+ id: htmlFor ?? name,
4269
+ name,
4270
+ onChange: (e) => commit(Array.from(e.target.files ?? [])),
4271
+ ref: inputRef,
4272
+ hidden: true,
4273
+ type: "file",
4274
+ accept,
4275
+ multiple: allowMultiple,
4276
+ disabled
4277
+ }
4278
+ ),
4279
+ /* @__PURE__ */ jsxRuntime.jsx(
4280
+ "span",
4281
+ {
4282
+ className: [
4283
+ "flex h-11 w-11 items-center justify-center rounded-full transition-colors duration-150",
4284
+ dragging ? "bg-accent text-accent-fg" : "bg-surface-raised text-foreground-secondary group-hover:text-foreground"
4285
+ ].join(" "),
4286
+ children: icon ?? UploadGlyph
4287
+ }
4288
+ ),
4289
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
4290
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium text-foreground", children: dragging ? "Drop to upload" : prompt }),
4291
+ hint && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-foreground-muted", children: hint })
4292
+ ] })
4293
+ ]
4294
+ }
4295
+ ),
4296
+ files.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "mt-3 flex flex-col gap-2", children: files.map((file, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
4297
+ "li",
4298
+ {
4299
+ className: "flex items-center gap-3 rounded-lg border border-border bg-surface px-3 py-2",
4300
+ children: [
4301
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0 text-foreground-muted", children: FileGlyph }),
4302
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex-1 min-w-0", children: [
4303
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block text-sm text-foreground truncate", children: file.name }),
4304
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block text-xs text-foreground-muted", children: formatBytes(file.size) })
4305
+ ] }),
4306
+ /* @__PURE__ */ jsxRuntime.jsx(
4307
+ "button",
4308
+ {
4309
+ type: "button",
4310
+ onClick: (e) => {
4311
+ e.stopPropagation();
4312
+ removeFile(idx);
4313
+ },
4314
+ "aria-label": `Remove ${file.name}`,
4315
+ 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",
4316
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 20 20", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15 5L5 15M5 5l10 10", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" }) })
4317
+ }
4318
+ )
4319
+ ]
4320
+ },
4321
+ `${idx}-${file.name}`
4322
+ )) })
4323
+ ]
4324
+ }
4112
4325
  );
4113
4326
  }
4114
4327
  var MONTH_NAMES = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
@@ -4163,7 +4376,8 @@ function DatePicker({
4163
4376
  style,
4164
4377
  format = defaultFormat,
4165
4378
  weekStartsOn = 0,
4166
- clearable = true
4379
+ clearable = true,
4380
+ size = "md"
4167
4381
  }) {
4168
4382
  const errorId = React8.useId();
4169
4383
  const hasError = errorMessage != null;
@@ -4266,7 +4480,7 @@ function DatePicker({
4266
4480
  "aria-describedby": hasError ? errorId : void 0,
4267
4481
  "aria-haspopup": "dialog",
4268
4482
  "aria-expanded": open,
4269
- 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]" : ""}`,
4483
+ className: `flex items-center justify-between cursor-pointer select-none ${!style?.width ? "min-w-[200px]" : ""} ${fieldShell({ size, hasError, disabled })}`,
4270
4484
  children: [
4271
4485
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-sm truncate ${displayValue ? "" : "text-foreground-muted"}`, children: displayValue || placeholder }),
4272
4486
  /* @__PURE__ */ jsxRuntime.jsx(CalendarIcon, {})
@@ -4451,6 +4665,377 @@ function ChevronLeft() {
4451
4665
  function ChevronRight3() {
4452
4666
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "w-4 h-4", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) });
4453
4667
  }
4668
+ var LINE_HEIGHT_PX = 21;
4669
+ function TextArea({
4670
+ value,
4671
+ onChange,
4672
+ onBlur,
4673
+ disabled,
4674
+ label,
4675
+ htmlFor,
4676
+ placeholder,
4677
+ name,
4678
+ layout = "vertical",
4679
+ size = "md",
4680
+ rows = 4,
4681
+ autoGrow = false,
4682
+ maxRows = 12,
4683
+ maxLength,
4684
+ showCount = false,
4685
+ resize,
4686
+ errorMessage,
4687
+ required,
4688
+ style,
4689
+ inputStyle
4690
+ }) {
4691
+ const errorId = React8.useId();
4692
+ const hasError = errorMessage != null;
4693
+ const ref = React8.useRef(null);
4694
+ React8.useLayoutEffect(() => {
4695
+ if (!autoGrow) return;
4696
+ const el = ref.current;
4697
+ if (!el) return;
4698
+ el.style.height = "auto";
4699
+ const maxH = maxRows * LINE_HEIGHT_PX + 16;
4700
+ el.style.height = `${Math.min(el.scrollHeight, maxH)}px`;
4701
+ el.style.overflowY = el.scrollHeight > maxH ? "auto" : "hidden";
4702
+ }, [value, autoGrow, maxRows]);
4703
+ const count = typeof value === "string" ? value.length : 0;
4704
+ const resizeClass = (resize ?? (autoGrow ? "none" : "vertical")) === "none" ? "resize-none" : (resize ?? "vertical") === "horizontal" ? "resize-x" : (resize ?? "vertical") === "both" ? "resize" : "resize-y";
4705
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4706
+ Field,
4707
+ {
4708
+ label,
4709
+ htmlFor,
4710
+ errorId,
4711
+ errorMessage,
4712
+ layout,
4713
+ required,
4714
+ children: [
4715
+ /* @__PURE__ */ jsxRuntime.jsx(
4716
+ "textarea",
4717
+ {
4718
+ ref,
4719
+ disabled,
4720
+ value,
4721
+ onChange,
4722
+ onBlur,
4723
+ name,
4724
+ id: htmlFor,
4725
+ rows,
4726
+ maxLength,
4727
+ placeholder: placeholder ?? "",
4728
+ "aria-invalid": hasError || void 0,
4729
+ "aria-describedby": hasError ? errorId : void 0,
4730
+ className: `${fieldShell({ size, hasError, disabled, sized: false })} px-3 py-2 leading-normal ${resizeClass}`,
4731
+ style: { ...style, ...inputStyle }
4732
+ }
4733
+ ),
4734
+ showCount && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 text-right text-xs text-foreground-muted tabular-nums", children: maxLength != null ? `${count} / ${maxLength}` : count })
4735
+ ]
4736
+ }
4737
+ );
4738
+ }
4739
+ var SIZE = {
4740
+ sm: { h: "h-control-sm", text: "text-xs", pad: "px-2.5" },
4741
+ md: { h: "h-control-md", text: "text-sm", pad: "px-3.5" },
4742
+ lg: { h: "h-control-lg", text: "text-sm", pad: "px-4" }
4743
+ };
4744
+ function SegmentedControl({
4745
+ options,
4746
+ value,
4747
+ defaultValue,
4748
+ onChange,
4749
+ size = "md",
4750
+ fullWidth = false,
4751
+ disabled,
4752
+ "aria-label": ariaLabel
4753
+ }) {
4754
+ const sz = SIZE[size];
4755
+ return /* @__PURE__ */ jsxRuntime.jsx(
4756
+ ToggleGroup__namespace.Root,
4757
+ {
4758
+ type: "single",
4759
+ value,
4760
+ defaultValue,
4761
+ onValueChange: (v) => {
4762
+ if (v) onChange?.(v);
4763
+ },
4764
+ disabled,
4765
+ "aria-label": ariaLabel,
4766
+ className: [
4767
+ "inline-flex items-center gap-1 rounded-lg border border-border bg-surface-raised p-1",
4768
+ sz.h,
4769
+ fullWidth ? "flex w-full" : "",
4770
+ disabled ? "opacity-60 cursor-not-allowed" : ""
4771
+ ].filter(Boolean).join(" "),
4772
+ children: options.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
4773
+ ToggleGroup__namespace.Item,
4774
+ {
4775
+ value: opt.value,
4776
+ disabled: opt.disabled,
4777
+ className: [
4778
+ "inline-flex items-center justify-center gap-1.5 rounded-md select-none whitespace-nowrap",
4779
+ "transition-colors duration-150 h-full",
4780
+ sz.text,
4781
+ sz.pad,
4782
+ fullWidth ? "flex-1" : "",
4783
+ // Resting: muted text, transparent. Hover lifts the text.
4784
+ "text-foreground-secondary hover:text-foreground",
4785
+ // Active: surface-white pill + accent text + subtle shadow.
4786
+ "data-[state=on]:bg-surface data-[state=on]:text-accent data-[state=on]:shadow-sm",
4787
+ "focus:outline-none focus-visible:ring-[3px] focus-visible:ring-focus-ring",
4788
+ "disabled:opacity-40 disabled:cursor-not-allowed"
4789
+ ].filter(Boolean).join(" "),
4790
+ children: [
4791
+ opt.icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", children: opt.icon }),
4792
+ opt.label
4793
+ ]
4794
+ },
4795
+ opt.value
4796
+ ))
4797
+ }
4798
+ );
4799
+ }
4800
+ var TRACK_H = { sm: "h-1", md: "h-1.5", lg: "h-2" };
4801
+ var THUMB = { sm: "h-3.5 w-3.5", md: "h-4 w-4", lg: "h-5 w-5" };
4802
+ var toArray = (v) => v == null ? void 0 : Array.isArray(v) ? v : [v];
4803
+ function Slider({
4804
+ value,
4805
+ defaultValue,
4806
+ onChange,
4807
+ onChangeEnd,
4808
+ min = 0,
4809
+ max = 100,
4810
+ step = 1,
4811
+ label,
4812
+ showValue = false,
4813
+ formatValue = (n) => String(n),
4814
+ marks,
4815
+ tooltip = false,
4816
+ size = "md",
4817
+ disabled,
4818
+ errorMessage,
4819
+ name,
4820
+ htmlFor
4821
+ }) {
4822
+ const errorId = React8.useId();
4823
+ const hasError = errorMessage != null;
4824
+ const isRange = Array.isArray(value ?? defaultValue);
4825
+ const [internal, setInternal] = React8.useState(
4826
+ () => toArray(value) ?? toArray(defaultValue) ?? [min]
4827
+ );
4828
+ const current = toArray(value) ?? internal;
4829
+ const [dragging, setDragging] = React8.useState(false);
4830
+ const emit = (arr) => {
4831
+ setInternal(arr);
4832
+ const next = isRange ? [arr[0], arr[1]] : arr[0];
4833
+ onChange?.(next);
4834
+ };
4835
+ const valueText = current.map(formatValue).join(" \u2013 ");
4836
+ return /* @__PURE__ */ jsxRuntime.jsxs(Field, { label: void 0, errorId, errorMessage, children: [
4837
+ (label || showValue) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
4838
+ label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor, className: "text-sm font-medium text-foreground select-none", children: label }),
4839
+ showValue && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-foreground-secondary tabular-nums", children: valueText })
4840
+ ] }),
4841
+ /* @__PURE__ */ jsxRuntime.jsxs(
4842
+ SliderPrimitive__namespace.Root,
4843
+ {
4844
+ id: htmlFor,
4845
+ name,
4846
+ value: toArray(value),
4847
+ defaultValue: toArray(defaultValue),
4848
+ min,
4849
+ max,
4850
+ step,
4851
+ disabled,
4852
+ "aria-invalid": hasError || void 0,
4853
+ "aria-describedby": hasError ? errorId : void 0,
4854
+ onValueChange: (v) => {
4855
+ emit(v);
4856
+ setDragging(true);
4857
+ },
4858
+ onValueCommit: (v) => {
4859
+ setDragging(false);
4860
+ onChangeEnd?.(isRange ? [v[0], v[1]] : v[0]);
4861
+ },
4862
+ className: "relative flex items-center select-none touch-none w-full h-5",
4863
+ children: [
4864
+ /* @__PURE__ */ jsxRuntime.jsx(SliderPrimitive__namespace.Track, { className: `relative grow rounded-full bg-surface-raised border border-border ${TRACK_H[size]}`, children: /* @__PURE__ */ jsxRuntime.jsx(SliderPrimitive__namespace.Range, { className: "absolute h-full rounded-full bg-accent" }) }),
4865
+ current.map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
4866
+ SliderPrimitive__namespace.Thumb,
4867
+ {
4868
+ className: `group relative block ${THUMB[size]} rounded-full border-2 border-accent bg-surface shadow-sm transition-shadow
4869
+ focus:outline-none focus-visible:ring-[3px] focus-visible:ring-focus-ring
4870
+ disabled:cursor-not-allowed`,
4871
+ children: tooltip && /* @__PURE__ */ jsxRuntime.jsx(
4872
+ "span",
4873
+ {
4874
+ className: `pointer-events-none absolute -top-8 left-1/2 -translate-x-1/2 rounded-md bg-foreground px-1.5 py-0.5 text-xs text-background tabular-nums whitespace-nowrap transition-opacity ${dragging ? "opacity-100" : "opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100"}`,
4875
+ children: formatValue(current[i])
4876
+ }
4877
+ )
4878
+ },
4879
+ i
4880
+ ))
4881
+ ]
4882
+ }
4883
+ ),
4884
+ marks && marks.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative mt-2 h-4", children: marks.map((m) => {
4885
+ const pct = (m.value - min) / (max - min) * 100;
4886
+ return /* @__PURE__ */ jsxRuntime.jsx(
4887
+ "span",
4888
+ {
4889
+ className: "absolute -translate-x-1/2 text-xs text-foreground-muted tabular-nums",
4890
+ style: { left: `${pct}%` },
4891
+ children: m.label ?? m.value
4892
+ },
4893
+ m.value
4894
+ );
4895
+ }) })
4896
+ ] });
4897
+ }
4898
+ function TagsInput({
4899
+ value,
4900
+ defaultValue,
4901
+ onChange,
4902
+ label,
4903
+ htmlFor,
4904
+ name,
4905
+ placeholder = "Add and press Enter",
4906
+ layout = "vertical",
4907
+ size = "md",
4908
+ disabled,
4909
+ errorMessage,
4910
+ required,
4911
+ maxTags,
4912
+ dedupe = true,
4913
+ validate,
4914
+ separators = ["Enter", ","]
4915
+ }) {
4916
+ const errorId = React8.useId();
4917
+ const inputRef = React8.useRef(null);
4918
+ const [internal, setInternal] = React8.useState(defaultValue ?? []);
4919
+ const [draft, setDraft] = React8.useState("");
4920
+ const [localError, setLocalError] = React8.useState(null);
4921
+ const tags = value ?? internal;
4922
+ const hasError = errorMessage != null || localError != null;
4923
+ const errorText = errorMessage ?? localError ?? void 0;
4924
+ const commitTags = (next) => {
4925
+ setInternal(next);
4926
+ onChange?.(next);
4927
+ };
4928
+ const addTag = (raw) => {
4929
+ const tag = raw.trim();
4930
+ if (!tag) return false;
4931
+ if (maxTags != null && tags.length >= maxTags) return false;
4932
+ if (dedupe && tags.some((t) => t.toLowerCase() === tag.toLowerCase())) {
4933
+ setLocalError(`"${tag}" is already added`);
4934
+ return false;
4935
+ }
4936
+ if (validate) {
4937
+ const res = validate(tag, tags);
4938
+ if (res !== true) {
4939
+ setLocalError(typeof res === "string" ? res : `"${tag}" is not valid`);
4940
+ return false;
4941
+ }
4942
+ }
4943
+ setLocalError(null);
4944
+ commitTags([...tags, tag]);
4945
+ return true;
4946
+ };
4947
+ const removeTag = (idx) => {
4948
+ commitTags(tags.filter((_, i) => i !== idx));
4949
+ setLocalError(null);
4950
+ };
4951
+ const onKeyDown = (e) => {
4952
+ if (separators.includes(e.key)) {
4953
+ e.preventDefault();
4954
+ if (addTag(draft)) setDraft("");
4955
+ } else if (e.key === "Backspace" && draft === "" && tags.length > 0) {
4956
+ removeTag(tags.length - 1);
4957
+ }
4958
+ };
4959
+ const onPaste = (e) => {
4960
+ const text = e.clipboardData.getData("text");
4961
+ const parts = text.split(/[\n,;]+/).map((p) => p.trim()).filter(Boolean);
4962
+ if (parts.length > 1) {
4963
+ e.preventDefault();
4964
+ let added = false;
4965
+ for (const p of parts) added = addTag(p) || added;
4966
+ if (added) setDraft("");
4967
+ }
4968
+ };
4969
+ const atMax = maxTags != null && tags.length >= maxTags;
4970
+ return /* @__PURE__ */ jsxRuntime.jsx(
4971
+ Field,
4972
+ {
4973
+ label,
4974
+ htmlFor,
4975
+ errorId,
4976
+ errorMessage: errorText,
4977
+ layout,
4978
+ required,
4979
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
4980
+ "div",
4981
+ {
4982
+ className: `flex flex-wrap items-center gap-1.5 min-h-[36px] ${fieldShell({ size, hasError, disabled, focusWithin: true, sized: false })} px-2 py-1.5`,
4983
+ onClick: () => inputRef.current?.focus(),
4984
+ children: [
4985
+ tags.map((tag, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
4986
+ "span",
4987
+ {
4988
+ className: "inline-flex items-center gap-1 rounded-md bg-surface-raised text-foreground text-xs pl-2 pr-1 py-0.5 border border-border",
4989
+ children: [
4990
+ tag,
4991
+ /* @__PURE__ */ jsxRuntime.jsx(
4992
+ "button",
4993
+ {
4994
+ type: "button",
4995
+ disabled,
4996
+ onClick: (e) => {
4997
+ e.stopPropagation();
4998
+ removeTag(idx);
4999
+ },
5000
+ "aria-label": `Remove ${tag}`,
5001
+ className: "inline-flex items-center justify-center w-4 h-4 rounded text-foreground-muted hover:text-status-error hover:bg-surface transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-accent",
5002
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "10", height: "10", viewBox: "0 0 20 20", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15 5L5 15M5 5l10 10", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) })
5003
+ }
5004
+ )
5005
+ ]
5006
+ },
5007
+ `${tag}-${idx}`
5008
+ )),
5009
+ /* @__PURE__ */ jsxRuntime.jsx(
5010
+ "input",
5011
+ {
5012
+ ref: inputRef,
5013
+ id: htmlFor,
5014
+ name,
5015
+ disabled: disabled || atMax,
5016
+ value: draft,
5017
+ onChange: (e) => {
5018
+ setDraft(e.target.value);
5019
+ if (localError) setLocalError(null);
5020
+ },
5021
+ onKeyDown,
5022
+ onPaste,
5023
+ onBlur: () => {
5024
+ if (addTag(draft)) setDraft("");
5025
+ },
5026
+ placeholder: atMax ? "" : tags.length === 0 ? placeholder : "",
5027
+ "aria-invalid": hasError || void 0,
5028
+ "aria-describedby": hasError ? errorId : void 0,
5029
+ autoComplete: "off",
5030
+ className: "flex-1 min-w-[6rem] bg-transparent outline-none text-sm placeholder:text-foreground-muted disabled:cursor-not-allowed"
5031
+ }
5032
+ )
5033
+ ]
5034
+ }
5035
+ )
5036
+ }
5037
+ );
5038
+ }
4454
5039
 
4455
5040
  Object.defineProperty(exports, "COLORS", {
4456
5041
  enumerable: true,
@@ -4481,6 +5066,7 @@ exports.ContextMenu = ContextMenu;
4481
5066
  exports.Drawer = Drawer;
4482
5067
  exports.Dropdown = Dropdown;
4483
5068
  exports.FadingBase = FadingBase;
5069
+ exports.Field = Field;
4484
5070
  exports.FileInput = FileInput;
4485
5071
  exports.Flex = Flex;
4486
5072
  exports.Grid = Grid2;
@@ -4495,17 +5081,22 @@ exports.NumberInput = NumberInput;
4495
5081
  exports.OpaqueGridCard = OpaqueGridCard;
4496
5082
  exports.Password = Password;
4497
5083
  exports.Portal = Portal;
5084
+ exports.RadioGroup = RadioGroup;
4498
5085
  exports.ScalableContainer = ScalableContainer;
4499
5086
  exports.SearchInput = SearchInput_default;
5087
+ exports.SegmentedControl = SegmentedControl;
4500
5088
  exports.Sidebar = Sidebar;
4501
5089
  exports.SkeletonBox = SkeletonBox;
4502
5090
  exports.SkeletonCard = SkeletonCard;
4503
5091
  exports.SkeletonCircle = SkeletonCircle;
4504
5092
  exports.SkeletonText = SkeletonText;
5093
+ exports.Slider = Slider;
4505
5094
  exports.Switch = Switch;
4506
5095
  exports.Table = Table;
4507
5096
  exports.Tabs = Tabs;
5097
+ exports.TagsInput = TagsInput;
4508
5098
  exports.Temporal = DatePicker;
5099
+ exports.TextArea = TextArea;
4509
5100
  exports.TextInput = TextInput;
4510
5101
  exports.ThemeProvider = ThemeProvider;
4511
5102
  exports.ThemeSwitch = ThemeSwitch;
@@ -4517,6 +5108,7 @@ exports.Tree = Tree;
4517
5108
  exports.TreeSelect = TreeSelect;
4518
5109
  exports.Typography = Typography;
4519
5110
  exports.Wizard = Wizard;
5111
+ exports.fieldShell = fieldShell;
4520
5112
  exports.useNotification = useNotification;
4521
5113
  //# sourceMappingURL=index.cjs.map
4522
5114
  //# sourceMappingURL=index.cjs.map