orynn 0.2.0 → 0.5.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
@@ -3,6 +3,7 @@
3
3
  var react = require('react');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  var reactDom = require('react-dom');
6
+ var radixUi = require('radix-ui');
6
7
 
7
8
  var __typeError = (msg) => {
8
9
  throw TypeError(msg);
@@ -30,8 +31,8 @@ var base = (props) => ({
30
31
  });
31
32
  var CheckIcon = (p) => /* @__PURE__ */ jsxRuntime.jsx("svg", { ...base(p), children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 6 9 17l-5-5" }) });
32
33
  var MinusIcon = (p) => /* @__PURE__ */ jsxRuntime.jsx("svg", { ...base(p), children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 12h14" }) });
33
- var PlusIcon = (p) => /* @__PURE__ */ jsxRuntime.jsx("svg", { ...base(p), children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 5v14M5 12h14" }) });
34
34
  var ChevronDownIcon = (p) => /* @__PURE__ */ jsxRuntime.jsx("svg", { ...base(p), children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 9 6 6 6-6" }) });
35
+ var ChevronUpIcon = (p) => /* @__PURE__ */ jsxRuntime.jsx("svg", { ...base(p), children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 15 6-6 6 6" }) });
35
36
  var ChevronLeftIcon = (p) => /* @__PURE__ */ jsxRuntime.jsx("svg", { ...base(p), children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m15 18-6-6 6-6" }) });
36
37
  var ChevronRightIcon = (p) => /* @__PURE__ */ jsxRuntime.jsx("svg", { ...base(p), children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m9 18 6-6-6-6" }) });
37
38
  var XIcon = (p) => /* @__PURE__ */ jsxRuntime.jsx("svg", { ...base(p), children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6 6 18M6 6l12 12" }) });
@@ -48,11 +49,15 @@ var CalendarIcon = (p) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...base(p), c
48
49
  /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }),
49
50
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16 2v4M8 2v4M3 10h18" })
50
51
  ] });
52
+ var CircleIcon = (p) => /* @__PURE__ */ jsxRuntime.jsx("svg", { ...base({ strokeWidth: 0, fill: "currentColor", ...p }), children: /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "6" }) });
51
53
  var CheckboxGroupContext = react.createContext(null);
52
54
  var sizePx = {
53
- sm: "0.95rem",
54
- md: "1.125rem",
55
- lg: "1.35rem"
55
+ sm: "0.875rem",
56
+ // 14px
57
+ md: "1rem",
58
+ // 16px — shadcn size-4
59
+ lg: "1.125rem"
60
+ // 18px
56
61
  };
57
62
  var Checkbox = /* @__PURE__ */ react.forwardRef(
58
63
  function Checkbox2(props, ref) {
@@ -210,15 +215,7 @@ function Field(props) {
210
215
  description != null && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "orynn-field__description", id: descriptionId, children: description }),
211
216
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "orynn-field__control", children: typeof children === "function" ? children(ctx) : children }),
212
217
  invalid && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "orynn-field__error", id: errorId, role: "alert", children: messages.length === 1 ? messages[0] : messages.map((m) => /* @__PURE__ */ jsxRuntime.jsx("span", { children: m }, m)) }),
213
- successMsg && /* @__PURE__ */ jsxRuntime.jsx(
214
- "p",
215
- {
216
- className: "orynn-field__error",
217
- id: successId,
218
- style: { color: "var(--orynn-color-success)" },
219
- children: successMsg
220
- }
221
- )
218
+ successMsg && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "orynn-field__success", id: successId, children: successMsg })
222
219
  ]
223
220
  }
224
221
  );
@@ -237,8 +234,11 @@ function CheckboxGroup(props) {
237
234
  defaultValue,
238
235
  onChange,
239
236
  orientation = "vertical",
237
+ columns,
240
238
  min,
241
239
  max,
240
+ showSelectAll = false,
241
+ selectAllLabel = "Select all",
242
242
  options,
243
243
  children,
244
244
  className,
@@ -263,6 +263,19 @@ function CheckboxGroup(props) {
263
263
  },
264
264
  [value, setValue, onChange]
265
265
  );
266
+ const selectableValues = (options ?? []).filter((o) => !o.disabled).map((o) => o.value);
267
+ const allSelected = selectableValues.length > 0 && selectableValues.every((v) => value.includes(v));
268
+ const someSelected = selectableValues.some((v) => value.includes(v));
269
+ const toggleAll = () => {
270
+ if (allSelected) {
271
+ setValue([]);
272
+ onChange?.([]);
273
+ } else {
274
+ const next = max != null ? selectableValues.slice(0, max) : selectableValues;
275
+ setValue(next);
276
+ onChange?.(next);
277
+ }
278
+ };
266
279
  const ctx = react.useMemo(
267
280
  () => ({
268
281
  name,
@@ -286,23 +299,40 @@ function CheckboxGroup(props) {
286
299
  disabled,
287
300
  id,
288
301
  className,
289
- children: () => /* @__PURE__ */ jsxRuntime.jsx(CheckboxGroupContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxRuntime.jsx(
302
+ children: () => /* @__PURE__ */ jsxRuntime.jsx(CheckboxGroupContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxRuntime.jsxs(
290
303
  "div",
291
304
  {
292
305
  role: "group",
293
306
  "aria-labelledby": label != null ? `${id}-label` : void 0,
294
307
  className: cx("orynn-choice-group"),
295
- "data-orientation": orientation,
296
- children: options ? options.map((o) => /* @__PURE__ */ jsxRuntime.jsx(
297
- Checkbox,
298
- {
299
- value: o.value,
300
- label: o.label,
301
- description: o.description,
302
- disabled: o.disabled
303
- },
304
- o.value
305
- )) : children
308
+ "data-orientation": columns ? void 0 : orientation,
309
+ "data-columns": columns ? "" : void 0,
310
+ style: columns ? { "--orynn-choice-columns": columns } : void 0,
311
+ children: [
312
+ showSelectAll && selectableValues.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "orynn-choice-group__selectall", children: /* @__PURE__ */ jsxRuntime.jsx(
313
+ Checkbox,
314
+ {
315
+ checked: allSelected,
316
+ indeterminate: !allSelected && someSelected,
317
+ disabled,
318
+ label: selectAllLabel,
319
+ onChange: toggleAll
320
+ }
321
+ ) }),
322
+ options ? options.map((o) => /* @__PURE__ */ jsxRuntime.jsx(
323
+ Checkbox,
324
+ {
325
+ value: o.value,
326
+ label: o.icon != null ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
327
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-choice__icon", children: o.icon }),
328
+ o.label
329
+ ] }) : o.label,
330
+ description: o.description,
331
+ disabled: o.disabled
332
+ },
333
+ o.value
334
+ )) : children
335
+ ]
306
336
  }
307
337
  ) })
308
338
  }
@@ -323,6 +353,8 @@ var FieldBox = /* @__PURE__ */ react.forwardRef(
323
353
  floatingLabel,
324
354
  left,
325
355
  right,
356
+ addonBefore,
357
+ addonAfter,
326
358
  textarea,
327
359
  className,
328
360
  children,
@@ -342,7 +374,8 @@ var FieldBox = /* @__PURE__ */ react.forwardRef(
342
374
  }
343
375
  onBlur?.(e);
344
376
  };
345
- return /* @__PURE__ */ jsxRuntime.jsxs(
377
+ const hasAddon = addonBefore != null || addonAfter != null;
378
+ const box = /* @__PURE__ */ jsxRuntime.jsxs(
346
379
  "div",
347
380
  {
348
381
  ref,
@@ -361,6 +394,8 @@ var FieldBox = /* @__PURE__ */ react.forwardRef(
361
394
  "data-focused": isFocused || void 0,
362
395
  "data-active": active || isFocused || void 0,
363
396
  "data-open": open || void 0,
397
+ "data-addon-before": addonBefore != null || void 0,
398
+ "data-addon-after": addonAfter != null || void 0,
364
399
  onFocus: handleFocus,
365
400
  onBlur: handleBlur,
366
401
  ...rest,
@@ -372,8 +407,137 @@ var FieldBox = /* @__PURE__ */ react.forwardRef(
372
407
  ]
373
408
  }
374
409
  );
410
+ if (!hasAddon) return box;
411
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "orynn-box__group", "data-size": size, "data-disabled": disabled || void 0, children: [
412
+ addonBefore != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-box__addon", "data-side": "before", children: addonBefore }),
413
+ box,
414
+ addonAfter != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-box__addon", "data-side": "after", children: addonAfter })
415
+ ] });
375
416
  }
376
417
  );
418
+
419
+ // src/theme/theme.ts
420
+ var len = (v) => typeof v === "number" ? `${v}px` : v;
421
+ var RADIUS_KEYWORDS = {
422
+ none: "0px",
423
+ sm: "0.375rem",
424
+ md: "0.5rem",
425
+ lg: "0.625rem",
426
+ xl: "0.875rem",
427
+ pill: "9999px"
428
+ };
429
+ var DARK_FG = "#0b0d12";
430
+ var LIGHT_FG = "#ffffff";
431
+ function contrastFor(color) {
432
+ const c = color.trim().toLowerCase();
433
+ const hex = /^#?([\da-f]{3}|[\da-f]{6})$/.exec(c)?.[1];
434
+ if (hex) {
435
+ const full = hex.length === 3 ? hex.replace(/./g, "$&$&") : hex;
436
+ const n = Number.parseInt(full, 16);
437
+ const toLin = (v) => {
438
+ const x = v / 255;
439
+ return x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4;
440
+ };
441
+ const lum = 0.2126 * toLin(n >> 16 & 255) + 0.7152 * toLin(n >> 8 & 255) + 0.0722 * toLin(n & 255);
442
+ return lum > 0.42 ? DARK_FG : LIGHT_FG;
443
+ }
444
+ const okl = /^oklch\(\s*([\d.]+)(%?)/.exec(c);
445
+ if (okl) {
446
+ const l = Number.parseFloat(okl[1]) / (okl[2] ? 100 : 1);
447
+ return l >= 0.62 ? DARK_FG : LIGHT_FG;
448
+ }
449
+ const hsl = /^hsla?\(\s*[\d.]+(?:deg)?\s*[, ]\s*[\d.]+%\s*[, ]\s*([\d.]+)%/.exec(c)?.[1];
450
+ if (hsl) return Number.parseFloat(hsl) >= 60 ? DARK_FG : LIGHT_FG;
451
+ if (c === "white") return DARK_FG;
452
+ if (c === "black") return LIGHT_FG;
453
+ return LIGHT_FG;
454
+ }
455
+ function normalizeTheme(input) {
456
+ return typeof input === "string" ? { preset: input } : input ?? {};
457
+ }
458
+ function resolveThemeVars(config) {
459
+ const vars = {};
460
+ const primary = config.primary ?? config.accent;
461
+ if (primary) {
462
+ const p = primary;
463
+ vars["--orynn-color-primary"] = p;
464
+ vars["--orynn-color-primary-foreground"] = config.primaryForeground ?? config.accentContrast ?? contrastFor(p);
465
+ if (!config.ring) vars["--orynn-color-ring"] = p;
466
+ }
467
+ if (config.ring) vars["--orynn-color-ring"] = config.ring;
468
+ if (config.font) vars["--orynn-font-family"] = config.font;
469
+ if (config.fontSize != null) vars["--orynn-font-size"] = len(config.fontSize);
470
+ if (config.controlWidth != null) {
471
+ vars["--orynn-control-width"] = len(config.controlWidth);
472
+ vars["--orynn-control-max-width"] = "none";
473
+ }
474
+ if (config.controlMaxWidth != null)
475
+ vars["--orynn-control-max-width"] = len(config.controlMaxWidth);
476
+ if (config.radius != null) {
477
+ const r = config.radius;
478
+ vars["--orynn-radius"] = typeof r === "string" && r in RADIUS_KEYWORDS ? RADIUS_KEYWORDS[r] : len(r);
479
+ }
480
+ Object.assign(vars, config.vars ?? {});
481
+ return vars;
482
+ }
483
+ var OrynnContext = react.createContext({
484
+ preset: "default",
485
+ density: "comfortable",
486
+ config: {},
487
+ portalContainer: null
488
+ });
489
+ function syncThemeEl(el, preset, density, config, style) {
490
+ const attr = (name, value) => value == null ? el.removeAttribute(name) : el.setAttribute(name, value);
491
+ attr("data-orynn-theme", preset === "default" ? null : preset);
492
+ attr("data-orynn-density", density === "comfortable" ? null : density);
493
+ attr("data-orynn-color-scheme", config.colorScheme ?? null);
494
+ el.classList.toggle("dark", config.colorScheme === "dark");
495
+ el.removeAttribute("style");
496
+ for (const [k, v] of Object.entries(style)) {
497
+ if (v != null) el.style.setProperty(k, String(v));
498
+ }
499
+ }
500
+ function OrynnProvider({ theme, as = "div", className, children }) {
501
+ const config = react.useMemo(() => normalizeTheme(theme), [theme]);
502
+ const preset = config.preset ?? "default";
503
+ const density = config.density ?? "comfortable";
504
+ const style = react.useMemo(() => resolveThemeVars(config), [config]);
505
+ const [portalContainer, setPortalContainer] = react.useState(null);
506
+ react.useEffect(() => {
507
+ if (typeof document === "undefined") return;
508
+ const el = document.createElement("div");
509
+ el.className = "orynn-portal";
510
+ document.body.appendChild(el);
511
+ setPortalContainer(el);
512
+ return () => {
513
+ el.remove();
514
+ setPortalContainer(null);
515
+ };
516
+ }, []);
517
+ react.useEffect(() => {
518
+ if (portalContainer)
519
+ syncThemeEl(portalContainer, preset, density, config, style);
520
+ }, [portalContainer, preset, density, config, style]);
521
+ const ctx = react.useMemo(
522
+ () => ({ preset, density, config, portalContainer }),
523
+ [preset, density, config, portalContainer]
524
+ );
525
+ const dataProps = {
526
+ "data-orynn-theme": preset === "default" ? void 0 : preset,
527
+ "data-orynn-density": density === "comfortable" ? void 0 : density,
528
+ "data-orynn-color-scheme": config.colorScheme
529
+ };
530
+ const darkClass = config.colorScheme === "dark" ? "dark" : void 0;
531
+ return /* @__PURE__ */ jsxRuntime.jsx(OrynnContext.Provider, { value: ctx, children: as === "contents" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: darkClass, style: { display: "contents", ...style }, ...dataProps, children }) : as === "span" ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cx("orynn-root", className, darkClass), style, ...dataProps, children }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: cx("orynn-root", className, darkClass), style, ...dataProps, children }) });
532
+ }
533
+ function useOrynnTheme() {
534
+ return react.useContext(OrynnContext);
535
+ }
536
+ function usePortalContainer() {
537
+ const { portalContainer } = react.useContext(OrynnContext);
538
+ if (portalContainer) return portalContainer;
539
+ return typeof document === "undefined" ? null : document.body;
540
+ }
377
541
  var useIsoLayoutEffect = typeof window === "undefined" ? react.useEffect : react.useLayoutEffect;
378
542
  function Popover({
379
543
  open,
@@ -382,6 +546,7 @@ function Popover({
382
546
  children,
383
547
  matchWidth = true,
384
548
  offset = 6,
549
+ maxHeight,
385
550
  className,
386
551
  popoverRef,
387
552
  role,
@@ -393,6 +558,7 @@ function Popover({
393
558
  if (popoverRef) popoverRef.current = el;
394
559
  };
395
560
  const [pos, setPos] = react.useState(null);
561
+ const container = usePortalContainer();
396
562
  useIsoLayoutEffect(() => {
397
563
  if (!open) return;
398
564
  const anchor = anchorRef.current;
@@ -404,12 +570,13 @@ function Popover({
404
570
  const above = r.top - offset;
405
571
  const wanted = innerRef.current?.scrollHeight ?? 280;
406
572
  const placeTop = below < Math.min(wanted, 240) && above > below;
573
+ const avail = Math.max(140, (placeTop ? above : below) - 4);
407
574
  setPos({
408
575
  left: r.left,
409
576
  top: placeTop ? r.top - offset : r.bottom + offset,
410
577
  width: r.width,
411
578
  placement: placeTop ? "top" : "bottom",
412
- maxHeight: Math.max(140, (placeTop ? above : below) - 4)
579
+ maxHeight: maxHeight != null ? Math.min(maxHeight, avail) : avail
413
580
  });
414
581
  };
415
582
  update();
@@ -422,7 +589,7 @@ function Popover({
422
589
  window.removeEventListener("scroll", update, true);
423
590
  window.removeEventListener("resize", update);
424
591
  };
425
- }, [open, anchorRef, offset]);
592
+ }, [open, anchorRef, offset, maxHeight]);
426
593
  react.useEffect(() => {
427
594
  if (!open) return;
428
595
  const onPointerDown = (e) => {
@@ -443,7 +610,7 @@ function Popover({
443
610
  document.removeEventListener("keydown", onKey, true);
444
611
  };
445
612
  }, [open, anchorRef, onClose]);
446
- if (!open || typeof document === "undefined" || !pos) return null;
613
+ if (!open || !pos || !container) return null;
447
614
  return reactDom.createPortal(
448
615
  /* @__PURE__ */ jsxRuntime.jsx(
449
616
  "div",
@@ -463,7 +630,7 @@ function Popover({
463
630
  children
464
631
  }
465
632
  ),
466
- document.body
633
+ container
467
634
  );
468
635
  }
469
636
 
@@ -488,27 +655,82 @@ var clampDate = (d, min, max) => {
488
655
  return d;
489
656
  };
490
657
  var pad = (n, len2 = 2) => String(n).padStart(len2, "0");
491
- function formatDate(d, fmt, locale) {
492
- const monthLong = new Intl.DateTimeFormat(locale, { month: "long" }).format(d);
493
- const monthShort = new Intl.DateTimeFormat(locale, { month: "short" }).format(d);
494
- return fmt.replace(/yyyy/g, String(d.getFullYear())).replace(/MMMM/g, monthLong).replace(/MMM/g, monthShort).replace(/MM/g, pad(d.getMonth() + 1)).replace(/dd/g, pad(d.getDate())).replace(/\bM\b/g, String(d.getMonth() + 1)).replace(/\bd\b/g, String(d.getDate()));
495
- }
496
- function parseDate(input, fmt) {
497
- const nums = input.match(/\d+/g);
498
- if (!nums) return null;
499
- const order = (fmt.match(/yyyy|MM|dd|M|d/g) ?? ["yyyy", "MM", "dd"]).map((t) => t[0]);
658
+ var FORMAT_TOKENS = /(?<![A-Za-z])(yyyy|yy|MMMM|MMM|MM|M|dd|d|EEEE|EEE|HH|H|hh|h|mm|m|ss|s|aa|a|A)(?![A-Za-z])/g;
659
+ function formatDate(d, fmt = "yyyy-MM-dd", locale) {
660
+ if (typeof fmt !== "string") return new Intl.DateTimeFormat(locale, fmt).format(d);
661
+ const h12 = d.getHours() % 12 || 12;
662
+ const map = {
663
+ yyyy: String(d.getFullYear()),
664
+ yy: pad(d.getFullYear() % 100),
665
+ MMMM: new Intl.DateTimeFormat(locale, { month: "long" }).format(d),
666
+ MMM: new Intl.DateTimeFormat(locale, { month: "short" }).format(d),
667
+ MM: pad(d.getMonth() + 1),
668
+ M: String(d.getMonth() + 1),
669
+ dd: pad(d.getDate()),
670
+ d: String(d.getDate()),
671
+ EEEE: new Intl.DateTimeFormat(locale, { weekday: "long" }).format(d),
672
+ EEE: new Intl.DateTimeFormat(locale, { weekday: "short" }).format(d),
673
+ HH: pad(d.getHours()),
674
+ H: String(d.getHours()),
675
+ hh: pad(h12),
676
+ h: String(h12),
677
+ mm: pad(d.getMinutes()),
678
+ m: String(d.getMinutes()),
679
+ ss: pad(d.getSeconds()),
680
+ s: String(d.getSeconds()),
681
+ aa: d.getHours() < 12 ? "am" : "pm",
682
+ a: d.getHours() < 12 ? "am" : "pm",
683
+ A: d.getHours() < 12 ? "AM" : "PM"
684
+ };
685
+ return fmt.replace(FORMAT_TOKENS, (t) => map[t] ?? t);
686
+ }
687
+ function matchMonthName(input, locale) {
688
+ for (let m = 0; m < 12; m += 1) {
689
+ for (const style of ["long", "short"]) {
690
+ const name = new Intl.DateTimeFormat(locale, { month: style }).format(new Date(2021, m, 1));
691
+ const i = input.toLowerCase().indexOf(name.toLowerCase());
692
+ if (i !== -1) return { month: m, rest: input.slice(0, i) + input.slice(i + name.length) };
693
+ }
694
+ }
695
+ return null;
696
+ }
697
+ function parseDate(input, fmt = "yyyy-MM-dd", locale) {
698
+ const pattern = typeof fmt === "string" ? fmt : "yyyy-MM-dd";
699
+ let text = input;
700
+ let monthFromName;
701
+ if (/MMM/.test(pattern)) {
702
+ const hit = matchMonthName(text, locale);
703
+ if (hit) {
704
+ monthFromName = hit.month;
705
+ text = hit.rest;
706
+ }
707
+ }
708
+ const nums = text.match(/\d+/g);
709
+ if (!nums && monthFromName === void 0) return null;
710
+ const order = (pattern.match(/yyyy|yy|MM|dd|HH|hh|mm|ss|M|d|H|h|m|s/g) ?? ["yyyy", "MM", "dd"]).map((t) => t.length === 2 ? t.charAt(0) + t.charAt(0) : t);
500
711
  let y;
501
- let m;
502
- let dd;
503
- order.forEach((t, i) => {
504
- const n = Number(nums[i]);
505
- if (Number.isNaN(n)) return;
506
- if (t === "y") y = n < 100 ? 2e3 + n : n;
507
- else if (t === "M") m = n - 1;
508
- else if (t === "d") dd = n;
509
- });
510
- if (y == null || m == null || dd == null) return null;
511
- const d = new Date(y, m, dd);
712
+ let mo = monthFromName;
713
+ let day;
714
+ let hh = 0;
715
+ let mm = 0;
716
+ let ss = 0;
717
+ let ni = 0;
718
+ for (const tok of order) {
719
+ if ((tok === "MM" || tok === "M") && monthFromName !== void 0) continue;
720
+ const n = Number((nums ?? [])[ni]);
721
+ ni += 1;
722
+ if (Number.isNaN(n)) continue;
723
+ if (tok === "yyyy" || tok === "yy") y = n < 100 ? 2e3 + n : n;
724
+ else if (tok === "MM" || tok === "M") mo = n - 1;
725
+ else if (tok === "dd" || tok === "d") day = n;
726
+ else if (tok === "HH" || tok === "H" || tok === "hh" || tok === "h") hh = n;
727
+ else if (tok === "mm" || tok === "m") mm = n;
728
+ else if (tok === "ss" || tok === "s") ss = n;
729
+ }
730
+ if (/pm/i.test(input) && hh < 12) hh += 12;
731
+ if (/am/i.test(input) && hh === 12) hh = 0;
732
+ if (y == null && day == null && mo == null) return null;
733
+ const d = new Date(y ?? (/* @__PURE__ */ new Date()).getFullYear(), mo ?? 0, day ?? 1, hh, mm, ss);
512
734
  return Number.isNaN(d.getTime()) ? null : d;
513
735
  }
514
736
  function toDate(v) {
@@ -517,11 +739,14 @@ function toDate(v) {
517
739
  const d = new Date(v);
518
740
  return Number.isNaN(d.getTime()) ? null : startOfDay(d);
519
741
  }
520
- function monthMatrix(viewYear, viewMonth, firstDayOfWeek) {
742
+ function monthMatrix(viewYear, viewMonth, firstDayOfWeek, fixedWeeks = true) {
521
743
  const first = new Date(viewYear, viewMonth, 1);
522
744
  const offset = (first.getDay() - firstDayOfWeek + 7) % 7;
523
745
  const start = addDays(first, -offset);
524
- return Array.from({ length: 42 }, (_, i) => addDays(start, i));
746
+ if (fixedWeeks) return Array.from({ length: 42 }, (_, i) => addDays(start, i));
747
+ const last = new Date(viewYear, viewMonth + 1, 0);
748
+ const used = Math.ceil((offset + last.getDate()) / 7);
749
+ return Array.from({ length: used * 7 }, (_, i) => addDays(start, i));
525
750
  }
526
751
  function weekdayLabels(firstDayOfWeek, locale) {
527
752
  const fmt = new Intl.DateTimeFormat(locale, { weekday: "short" });
@@ -530,6 +755,10 @@ function weekdayLabels(firstDayOfWeek, locale) {
530
755
  (_, i) => fmt.format(new Date(2023, 0, 1 + (firstDayOfWeek + i) % 7))
531
756
  );
532
757
  }
758
+ function decadeGrid(year) {
759
+ const start = Math.floor(year / 10) * 10;
760
+ return { start, years: Array.from({ length: 12 }, (_, i) => start - 1 + i) };
761
+ }
533
762
  function isoWeek(d) {
534
763
  const date = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
535
764
  const dayNum = (date.getUTCDay() + 6) % 7;
@@ -538,9 +767,40 @@ function isoWeek(d) {
538
767
  const diff = date.getTime() - firstThursday.getTime();
539
768
  return 1 + Math.round(diff / (7 * 24 * 3600 * 1e3));
540
769
  }
770
+
771
+ // src/controls/DatePicker/calendar-range.ts
772
+ function normalizeRange(r) {
773
+ if (r.start && r.end && startOfDay(r.start) > startOfDay(r.end)) {
774
+ return { start: r.end, end: r.start };
775
+ }
776
+ return r;
777
+ }
778
+ function isRangeComplete(r) {
779
+ return !!r && !!r.start && !!r.end;
780
+ }
781
+ function isWithinRange(d, r) {
782
+ if (!isRangeComplete(r)) return false;
783
+ const t = startOfDay(d).getTime();
784
+ return t > startOfDay(r.start).getTime() && t < startOfDay(r.end).getTime();
785
+ }
786
+ var isRangeStart = (d, r) => !!r && isSameDay(d, r.start);
787
+ var isRangeEnd = (d, r) => !!r && isSameDay(d, r.end);
788
+ function previewRange(anchor, hover) {
789
+ if (!anchor || !hover) return null;
790
+ return normalizeRange({ start: anchor, end: hover });
791
+ }
792
+ function nextRange(current, d) {
793
+ if (!current.start || current.start && current.end) return { start: startOfDay(d), end: null };
794
+ return normalizeRange({ start: current.start, end: startOfDay(d) });
795
+ }
541
796
  function Calendar({
542
797
  value,
543
798
  onSelect,
799
+ mode = "single",
800
+ selectedDates,
801
+ range,
802
+ previewRange: previewRange2,
803
+ onHoverDate,
544
804
  minDate,
545
805
  maxDate,
546
806
  isDisabled,
@@ -550,16 +810,33 @@ function Calendar({
550
810
  showToday,
551
811
  showClear,
552
812
  onClear,
553
- autoFocus
813
+ autoFocus,
814
+ view: viewProp,
815
+ defaultView,
816
+ onViewChange,
817
+ precision = "day",
818
+ fixedWeeks = true,
819
+ defaultMonth,
820
+ numberOfMonths = 1
554
821
  }) {
822
+ const panels = Math.max(1, Math.floor(numberOfMonths));
555
823
  const today = startOfDay(/* @__PURE__ */ new Date());
556
- const initial = value ?? clampDate(today, minDate, maxDate);
557
- const [view, setView] = react.useState({ y: initial.getFullYear(), m: initial.getMonth() });
824
+ const anchor = value ?? (mode === "range" ? range?.start ?? range?.end ?? null : null) ?? (mode === "multiple" ? selectedDates?.[0] ?? null : null) ?? defaultMonth ?? null;
825
+ const initial = anchor ?? clampDate(today, minDate, maxDate);
826
+ const [nav, setNav] = react.useState({ y: initial.getFullYear(), m: initial.getMonth() });
558
827
  const [focused, setFocused] = react.useState(initial);
828
+ const [innerView, setInnerView] = react.useState(
829
+ defaultView ?? (precision === "day" ? "day" : precision)
830
+ );
831
+ const view = viewProp ?? innerView;
832
+ const setView = (v) => {
833
+ if (viewProp === void 0) setInnerView(v);
834
+ onViewChange?.(v);
835
+ };
559
836
  const gridRef = react.useRef(null);
560
837
  react.useEffect(() => {
561
838
  if (value) {
562
- setView({ y: value.getFullYear(), m: value.getMonth() });
839
+ setNav({ y: value.getFullYear(), m: value.getMonth() });
563
840
  setFocused(value);
564
841
  }
565
842
  }, [value]);
@@ -568,94 +845,118 @@ function Calendar({
568
845
  gridRef.current?.querySelector('[data-focused="true"]')?.focus();
569
846
  }
570
847
  }, [autoFocus]);
571
- const cells = monthMatrix(view.y, view.m, firstDayOfWeek);
572
- const weekdays = weekdayLabels(firstDayOfWeek, locale);
573
- const monthName = new Intl.DateTimeFormat(locale, { month: "long", year: "numeric" }).format(
574
- new Date(view.y, view.m, 1)
575
- );
576
848
  const outOfRange = (d) => minDate != null && d < startOfDay(minDate) || maxDate != null && d > startOfDay(maxDate) || Boolean(isDisabled?.(d));
577
- const goto = (d) => {
578
- const c = clampDate(d, minDate, maxDate);
579
- setFocused(c);
580
- setView({ y: c.getFullYear(), m: c.getMonth() });
849
+ const monthOutOfRange = (y, m) => {
850
+ const first = new Date(y, m, 1);
851
+ const last = new Date(y, m + 1, 0);
852
+ return maxDate != null && first > startOfDay(maxDate) || minDate != null && last < startOfDay(minDate);
853
+ };
854
+ const yearOutOfRange = (y) => maxDate != null && new Date(y, 0, 1) > startOfDay(maxDate) || minDate != null && new Date(y, 11, 31) < startOfDay(minDate);
855
+ const focusGrid = () => {
581
856
  requestAnimationFrame(() => {
582
857
  gridRef.current?.querySelector('[data-focused="true"]')?.focus();
583
858
  });
584
859
  };
585
- const onKeyDown = (e) => {
586
- const k = e.key;
587
- let next = null;
588
- if (k === "ArrowLeft") next = addDays(focused, -1);
589
- else if (k === "ArrowRight") next = addDays(focused, 1);
590
- else if (k === "ArrowUp") next = addDays(focused, -7);
591
- else if (k === "ArrowDown") next = addDays(focused, 7);
592
- else if (k === "Home") next = addDays(focused, -((focused.getDay() - firstDayOfWeek + 7) % 7));
593
- else if (k === "End")
594
- next = addDays(focused, 6 - (focused.getDay() - firstDayOfWeek + 7) % 7);
595
- else if (k === "PageUp") next = addMonths(focused, e.shiftKey ? -12 : -1);
596
- else if (k === "PageDown") next = addMonths(focused, e.shiftKey ? 12 : 1);
597
- else if (k === "Enter" || k === " ") {
598
- e.preventDefault();
599
- if (!outOfRange(focused)) onSelect(focused);
600
- return;
601
- } else return;
602
- e.preventDefault();
603
- goto(next);
860
+ const navIndex = nav.y * 12 + nav.m;
861
+ const goto = (d) => {
862
+ const c = clampDate(d, minDate, maxDate);
863
+ setFocused(c);
864
+ const ci = c.getFullYear() * 12 + c.getMonth();
865
+ if (ci < navIndex || ci > navIndex + panels - 1) {
866
+ setNav({ y: c.getFullYear(), m: c.getMonth() });
867
+ }
868
+ focusGrid();
604
869
  };
605
- const years = [];
606
- const minY = minDate ? minDate.getFullYear() : view.y - 10;
607
- const maxY = maxDate ? maxDate.getFullYear() : view.y + 10;
608
- for (let y = minY; y <= maxY; y += 1) years.push(y);
609
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "orynn-calendar", children: [
610
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "orynn-calendar__header", children: [
611
- /* @__PURE__ */ jsxRuntime.jsx(
612
- "button",
613
- {
614
- type: "button",
615
- className: "orynn-calendar__nav",
616
- "aria-label": "Previous month",
617
- onClick: () => setView((v) => ({ y: v.m === 0 ? v.y - 1 : v.y, m: (v.m + 11) % 12 })),
618
- children: /* @__PURE__ */ jsxRuntime.jsx(ChevronLeftIcon, {})
619
- }
620
- ),
621
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "orynn-calendar__title", children: [
622
- /* @__PURE__ */ jsxRuntime.jsx(
623
- "select",
624
- {
625
- className: "orynn-calendar__select",
626
- "aria-label": "Month",
627
- value: view.m,
628
- onChange: (e) => setView((v) => ({ ...v, m: Number(e.target.value) })),
629
- children: Array.from({ length: 12 }, (_, i) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: i, children: new Intl.DateTimeFormat(locale, { month: "long" }).format(new Date(2023, i, 1)) }, i))
630
- }
631
- ),
632
- /* @__PURE__ */ jsxRuntime.jsx(
633
- "select",
634
- {
635
- className: "orynn-calendar__select",
636
- "aria-label": "Year",
637
- value: view.y,
638
- onChange: (e) => setView((v) => ({ ...v, y: Number(e.target.value) })),
639
- children: years.map((y) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: y, children: y }, y))
640
- }
641
- )
642
- ] }),
643
- /* @__PURE__ */ jsxRuntime.jsx(
644
- "button",
645
- {
646
- type: "button",
647
- className: "orynn-calendar__nav",
648
- "aria-label": "Next month",
649
- onClick: () => setView((v) => ({ y: v.m === 11 ? v.y + 1 : v.y, m: (v.m + 1) % 12 })),
650
- children: /* @__PURE__ */ jsxRuntime.jsx(ChevronRightIcon, {})
651
- }
652
- )
653
- ] }),
654
- " ",
655
- /* @__PURE__ */ jsxRuntime.jsxs(
870
+ const canDrillUp = view !== "year";
871
+ const dayTitle = () => {
872
+ const first = new Date(nav.y, nav.m, 1);
873
+ if (panels <= 1) {
874
+ return new Intl.DateTimeFormat(locale, { month: "long", year: "numeric" }).format(first);
875
+ }
876
+ const last = new Date(nav.y, nav.m + panels - 1, 1);
877
+ const f = new Intl.DateTimeFormat(locale, { month: "short" }).format(first);
878
+ const l = new Intl.DateTimeFormat(locale, { month: "short", year: "numeric" }).format(last);
879
+ return first.getFullYear() === last.getFullYear() ? `${f} \u2013 ${l}` : `${f} ${first.getFullYear()} \u2013 ${l}`;
880
+ };
881
+ const titleText = view === "day" ? dayTitle() : view === "month" ? String(nav.y) : (() => {
882
+ const g = decadeGrid(nav.y);
883
+ return `${g.start} \u2013 ${g.start + 9}`;
884
+ })();
885
+ const step = (dir) => {
886
+ if (view === "day") setNav((v) => ({ ...addMonthNav(v, dir) }));
887
+ else if (view === "month") setNav((v) => ({ ...v, y: v.y + dir }));
888
+ else setNav((v) => ({ ...v, y: v.y + dir * 10 }));
889
+ };
890
+ const header = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "orynn-calendar__header", children: [
891
+ /* @__PURE__ */ jsxRuntime.jsx(
892
+ "button",
893
+ {
894
+ type: "button",
895
+ className: "orynn-calendar__nav",
896
+ "aria-label": view === "day" ? "Previous month" : view === "month" ? "Previous year" : "Previous decade",
897
+ onClick: () => step(-1),
898
+ children: /* @__PURE__ */ jsxRuntime.jsx(ChevronLeftIcon, {})
899
+ }
900
+ ),
901
+ /* @__PURE__ */ jsxRuntime.jsx(
902
+ "button",
903
+ {
904
+ type: "button",
905
+ className: "orynn-calendar__title",
906
+ "aria-live": "polite",
907
+ disabled: !canDrillUp,
908
+ onClick: () => {
909
+ if (!canDrillUp) return;
910
+ setView(view === "day" ? "month" : "year");
911
+ },
912
+ children: titleText
913
+ }
914
+ ),
915
+ /* @__PURE__ */ jsxRuntime.jsx(
916
+ "button",
917
+ {
918
+ type: "button",
919
+ className: "orynn-calendar__nav",
920
+ "aria-label": view === "day" ? "Next month" : view === "month" ? "Next year" : "Next decade",
921
+ onClick: () => step(1),
922
+ children: /* @__PURE__ */ jsxRuntime.jsx(ChevronRightIcon, {})
923
+ }
924
+ )
925
+ ] });
926
+ const dayGrid = (monthOffset = 0) => {
927
+ const panelMonth = new Date(nav.y, nav.m + monthOffset, 1);
928
+ const py = panelMonth.getFullYear();
929
+ const pm = panelMonth.getMonth();
930
+ const cells = monthMatrix(py, pm, firstDayOfWeek, fixedWeeks);
931
+ const weeks = cells.length / 7;
932
+ const weekdays = weekdayLabels(firstDayOfWeek, locale);
933
+ const monthName = new Intl.DateTimeFormat(locale, { month: "long", year: "numeric" }).format(
934
+ panelMonth
935
+ );
936
+ const onKeyDown = (e) => {
937
+ const k = e.key;
938
+ let next = null;
939
+ if (k === "ArrowLeft") next = addDays(focused, -1);
940
+ else if (k === "ArrowRight") next = addDays(focused, 1);
941
+ else if (k === "ArrowUp") next = addDays(focused, -7);
942
+ else if (k === "ArrowDown") next = addDays(focused, 7);
943
+ else if (k === "Home")
944
+ next = addDays(focused, -((focused.getDay() - firstDayOfWeek + 7) % 7));
945
+ else if (k === "End")
946
+ next = addDays(focused, 6 - (focused.getDay() - firstDayOfWeek + 7) % 7);
947
+ else if (k === "PageUp") next = addMonths(focused, e.shiftKey ? -12 : -1);
948
+ else if (k === "PageDown") next = addMonths(focused, e.shiftKey ? 12 : 1);
949
+ else if (k === "Enter" || k === " ") {
950
+ e.preventDefault();
951
+ if (!outOfRange(focused)) onSelect(focused);
952
+ return;
953
+ } else return;
954
+ e.preventDefault();
955
+ goto(next);
956
+ };
957
+ return /* @__PURE__ */ jsxRuntime.jsxs(
656
958
  "table",
657
959
  {
658
- ref: gridRef,
659
960
  className: "orynn-calendar__grid",
660
961
  role: "grid",
661
962
  "aria-label": monthName,
@@ -665,16 +966,29 @@ function Calendar({
665
966
  showWeekNumbers && /* @__PURE__ */ jsxRuntime.jsx("th", { "aria-hidden": "true" }),
666
967
  weekdays.map((w) => /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", children: w }, w))
667
968
  ] }) }),
668
- /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: Array.from({ length: 6 }, (_, week) => {
969
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { onMouseLeave: onHoverDate ? () => onHoverDate(null) : void 0, children: Array.from({ length: weeks }, (_, week) => {
669
970
  const row = cells.slice(week * 7, week * 7 + 7);
670
971
  const firstOfRow = row[0];
671
972
  return /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
672
973
  showWeekNumbers && /* @__PURE__ */ jsxRuntime.jsx("td", { className: "orynn-calendar__weeknum", children: isoWeek(firstOfRow) }),
673
974
  row.map((d) => {
674
- const inMonth = d.getMonth() === view.m;
975
+ const inMonth = d.getMonth() === pm;
675
976
  const disabled = outOfRange(d);
676
- const selected = isSameDay(d, value);
677
977
  const isFocused = isSameDay(d, focused);
978
+ let selected;
979
+ let rangeStart = false;
980
+ let rangeEnd = false;
981
+ let inRange = false;
982
+ if (mode === "multiple") {
983
+ selected = (selectedDates ?? []).some((x) => isSameDay(x, d));
984
+ } else if (mode === "range") {
985
+ rangeStart = isRangeStart(d, range) || !!previewRange2 && isRangeStart(d, previewRange2);
986
+ rangeEnd = isRangeEnd(d, range) || !!previewRange2 && isRangeEnd(d, previewRange2);
987
+ inRange = isWithinRange(d, range) || !!previewRange2 && isWithinRange(d, previewRange2);
988
+ selected = rangeStart || rangeEnd;
989
+ } else {
990
+ selected = isSameDay(d, value);
991
+ }
678
992
  return /* @__PURE__ */ jsxRuntime.jsx("td", { role: "gridcell", "aria-selected": selected, children: /* @__PURE__ */ jsxRuntime.jsx(
679
993
  "button",
680
994
  {
@@ -685,10 +999,15 @@ function Calendar({
685
999
  "data-outside": !inMonth || void 0,
686
1000
  "data-today": isSameDay(d, today) || void 0,
687
1001
  "data-selected": selected || void 0,
1002
+ "data-range-start": rangeStart || void 0,
1003
+ "data-range-end": rangeEnd || void 0,
1004
+ "data-in-range": inRange || void 0,
688
1005
  disabled,
689
1006
  "aria-label": new Intl.DateTimeFormat(locale, { dateStyle: "full" }).format(
690
1007
  d
691
1008
  ),
1009
+ onMouseEnter: onHoverDate ? () => onHoverDate(d) : void 0,
1010
+ onFocus: onHoverDate ? () => onHoverDate(d) : void 0,
692
1011
  onClick: () => {
693
1012
  setFocused(d);
694
1013
  onSelect(d);
@@ -700,25 +1019,187 @@ function Calendar({
700
1019
  ] }, firstOfRow.toISOString());
701
1020
  }) })
702
1021
  ]
1022
+ },
1023
+ `${py}-${pm}`
1024
+ );
1025
+ };
1026
+ const monthGrid = () => {
1027
+ const names = Array.from(
1028
+ { length: 12 },
1029
+ (_, i) => new Intl.DateTimeFormat(locale, { month: "short" }).format(new Date(2021, i, 1))
1030
+ );
1031
+ const onKeyDown = (e) => {
1032
+ let m = nav.m;
1033
+ if (e.key === "ArrowLeft") m -= 1;
1034
+ else if (e.key === "ArrowRight") m += 1;
1035
+ else if (e.key === "ArrowUp") m -= 3;
1036
+ else if (e.key === "ArrowDown") m += 3;
1037
+ else if (e.key === "Enter" || e.key === " ") {
1038
+ e.preventDefault();
1039
+ pickMonth(nav.m);
1040
+ return;
1041
+ } else return;
1042
+ e.preventDefault();
1043
+ const d = addMonths(new Date(nav.y, nav.m, 1), m - nav.m);
1044
+ setNav({ y: d.getFullYear(), m: d.getMonth() });
1045
+ focusGrid();
1046
+ };
1047
+ return /* @__PURE__ */ jsxRuntime.jsx(
1048
+ "div",
1049
+ {
1050
+ className: "orynn-calendar__pickgrid",
1051
+ role: "grid",
1052
+ "aria-label": `Months of ${nav.y}`,
1053
+ onKeyDown,
1054
+ children: [0, 3, 6, 9].map((rowStart) => /* @__PURE__ */ jsxRuntime.jsx("div", { role: "row", className: "orynn-calendar__pickrow", children: names.slice(rowStart, rowStart + 3).map((name, j) => {
1055
+ const i = rowStart + j;
1056
+ const isFocused = i === nav.m;
1057
+ const selected = value != null && value.getFullYear() === nav.y && value.getMonth() === i;
1058
+ return /* @__PURE__ */ jsxRuntime.jsx(
1059
+ "button",
1060
+ {
1061
+ type: "button",
1062
+ className: "orynn-calendar__cell",
1063
+ tabIndex: isFocused ? 0 : -1,
1064
+ "data-focused": isFocused || void 0,
1065
+ "data-selected": selected || void 0,
1066
+ disabled: monthOutOfRange(nav.y, i),
1067
+ onClick: () => pickMonth(i),
1068
+ children: name
1069
+ },
1070
+ name
1071
+ );
1072
+ }) }, rowStart))
703
1073
  }
704
- ),
705
- (showToday || showClear) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "orynn-calendar__footer", children: [
706
- showToday ? /* @__PURE__ */ jsxRuntime.jsx(
707
- "button",
708
- {
709
- type: "button",
710
- className: "orynn-calendar__action",
711
- disabled: outOfRange(today),
712
- onClick: () => onSelect(today),
713
- children: "Today"
714
- }
715
- ) : /* @__PURE__ */ jsxRuntime.jsx("span", {}),
716
- showClear && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "orynn-calendar__action", onClick: onClear, children: "Clear" })
717
- ] })
718
- ] });
1074
+ );
1075
+ };
1076
+ const pickMonth = (m) => {
1077
+ if (monthOutOfRange(nav.y, m)) return;
1078
+ setNav((v) => ({ ...v, m }));
1079
+ if (precision === "month") {
1080
+ onSelect(new Date(nav.y, m, 1));
1081
+ } else {
1082
+ setView("day");
1083
+ setFocused(clampDate(new Date(nav.y, m, 1), minDate, maxDate));
1084
+ focusGrid();
1085
+ }
1086
+ };
1087
+ const yearGrid = () => {
1088
+ const g = decadeGrid(nav.y);
1089
+ const onKeyDown = (e) => {
1090
+ let y = nav.y;
1091
+ if (e.key === "ArrowLeft") y -= 1;
1092
+ else if (e.key === "ArrowRight") y += 1;
1093
+ else if (e.key === "ArrowUp") y -= 3;
1094
+ else if (e.key === "ArrowDown") y += 3;
1095
+ else if (e.key === "Enter" || e.key === " ") {
1096
+ e.preventDefault();
1097
+ pickYear(nav.y);
1098
+ return;
1099
+ } else return;
1100
+ e.preventDefault();
1101
+ setNav((v) => ({ ...v, y }));
1102
+ focusGrid();
1103
+ };
1104
+ return /* @__PURE__ */ jsxRuntime.jsx(
1105
+ "div",
1106
+ {
1107
+ className: "orynn-calendar__pickgrid",
1108
+ role: "grid",
1109
+ "aria-label": `Years ${g.start}\u2013${g.start + 9}`,
1110
+ onKeyDown,
1111
+ children: [0, 3, 6, 9].map((rowStart) => /* @__PURE__ */ jsxRuntime.jsx("div", { role: "row", className: "orynn-calendar__pickrow", children: g.years.slice(rowStart, rowStart + 3).map((y) => {
1112
+ const isFocused = y === nav.y;
1113
+ const selected = value != null && value.getFullYear() === y;
1114
+ return /* @__PURE__ */ jsxRuntime.jsx(
1115
+ "button",
1116
+ {
1117
+ type: "button",
1118
+ className: "orynn-calendar__cell",
1119
+ tabIndex: isFocused ? 0 : -1,
1120
+ "data-focused": isFocused || void 0,
1121
+ "data-outside": y < g.start || y > g.start + 9 || void 0,
1122
+ "data-selected": selected || void 0,
1123
+ disabled: yearOutOfRange(y),
1124
+ onClick: () => pickYear(y),
1125
+ children: y
1126
+ },
1127
+ y
1128
+ );
1129
+ }) }, rowStart))
1130
+ }
1131
+ );
1132
+ };
1133
+ const pickYear = (y) => {
1134
+ if (yearOutOfRange(y)) return;
1135
+ setNav((v) => ({ ...v, y }));
1136
+ if (precision === "year") {
1137
+ onSelect(new Date(y, 0, 1));
1138
+ } else {
1139
+ setView("month");
1140
+ focusGrid();
1141
+ }
1142
+ };
1143
+ const dayView = panels <= 1 ? dayGrid() : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "orynn-calendar__panels", children: Array.from({ length: panels }, (_, i) => dayGrid(i)) });
1144
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1145
+ "div",
1146
+ {
1147
+ className: "orynn-calendar",
1148
+ ref: gridRef,
1149
+ "data-view": view,
1150
+ "data-months": panels > 1 ? panels : void 0,
1151
+ children: [
1152
+ header,
1153
+ " ",
1154
+ view === "day" ? dayView : view === "month" ? monthGrid() : yearGrid(),
1155
+ (showToday || showClear) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "orynn-calendar__footer", children: [
1156
+ showToday ? /* @__PURE__ */ jsxRuntime.jsx(
1157
+ "button",
1158
+ {
1159
+ type: "button",
1160
+ className: "orynn-calendar__action",
1161
+ disabled: outOfRange(today),
1162
+ onClick: () => onSelect(today),
1163
+ children: "Today"
1164
+ }
1165
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", {}),
1166
+ showClear && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "orynn-calendar__action", onClick: onClear, children: "Clear" })
1167
+ ] })
1168
+ ]
1169
+ }
1170
+ );
1171
+ }
1172
+ function addMonthNav(v, dir) {
1173
+ const total = v.y * 12 + v.m + dir;
1174
+ return { y: Math.floor(total / 12), m: (total % 12 + 12) % 12 };
719
1175
  }
1176
+
1177
+ // src/controls/DatePicker/time-utils.ts
1178
+ var timeParts = (d) => ({
1179
+ h: d.getHours(),
1180
+ m: d.getMinutes(),
1181
+ s: d.getSeconds()
1182
+ });
1183
+ function withTime(date, time) {
1184
+ const r = new Date(date);
1185
+ r.setHours(time.h, time.m, time.s, 0);
1186
+ return r;
1187
+ }
1188
+ function minuteOptions(step) {
1189
+ const s = Math.max(1, Math.min(30, Math.floor(step)));
1190
+ const out = [];
1191
+ for (let m = 0; m < 60; m += s) out.push(m);
1192
+ return out;
1193
+ }
1194
+ var pad2 = (n) => String(n).padStart(2, "0");
1195
+ var normDates = (v) => Array.isArray(v) ? v.map((x) => toDate(x)).filter(Boolean) : [];
1196
+ var normRange = (v) => {
1197
+ const r = v ?? {};
1198
+ return { start: toDate(r.start ?? null), end: toDate(r.end ?? null) };
1199
+ };
720
1200
  var DatePicker = /* @__PURE__ */ react.forwardRef(
721
- function DatePicker2(props, ref) {
1201
+ function DatePicker2(propsIn, ref) {
1202
+ const props = propsIn;
722
1203
  const {
723
1204
  label,
724
1205
  description,
@@ -736,6 +1217,7 @@ var DatePicker = /* @__PURE__ */ react.forwardRef(
736
1217
  id: idProp,
737
1218
  name,
738
1219
  className,
1220
+ mode = "single",
739
1221
  value: valueProp,
740
1222
  defaultValue,
741
1223
  onChange,
@@ -743,46 +1225,123 @@ var DatePicker = /* @__PURE__ */ react.forwardRef(
743
1225
  minDate,
744
1226
  maxDate,
745
1227
  disabledDates,
1228
+ isDateUnavailable,
746
1229
  firstDayOfWeek = 0,
747
1230
  locale,
748
1231
  showWeekNumbers,
749
1232
  showToday = true,
750
1233
  showClear,
1234
+ defaultView,
1235
+ precision = "day",
1236
+ fixedWeeks = true,
1237
+ numberOfMonths,
1238
+ showTime = false,
1239
+ timeStep = 5,
1240
+ defaultMonth,
1241
+ presets,
751
1242
  inline = false,
752
1243
  allowInput = true,
753
- closeOnSelect = true,
1244
+ closeOnSelect: closeOnSelectProp,
1245
+ open: openProp,
1246
+ defaultOpen = false,
1247
+ onOpenChange,
754
1248
  placeholder
755
1249
  } = props;
756
1250
  const reactId = react.useId();
757
1251
  const id = idProp ?? reactId;
758
1252
  const panelId = `${id}-cal`;
1253
+ const withTimePicker = showTime && mode === "single";
1254
+ const closeOnSelect = closeOnSelectProp ?? !withTimePicker;
1255
+ const fmt = withTimePicker && format === "yyyy-MM-dd" ? "yyyy-MM-dd HH:mm" : format;
759
1256
  const isControlled = valueProp !== void 0;
760
- const [inner, setInner] = react.useState(() => toDate(defaultValue));
761
- const value = isControlled ? toDate(valueProp) : inner;
762
- const [open, setOpen] = react.useState(false);
1257
+ const [innerRaw, setInnerRaw] = react.useState(() => defaultValue ?? null);
1258
+ const raw = isControlled ? valueProp : innerRaw;
1259
+ const toDateKeepTime = (v) => {
1260
+ if (v == null) return null;
1261
+ const d = new Date(v);
1262
+ return Number.isNaN(d.getTime()) ? null : d;
1263
+ };
1264
+ const curDate = mode === "single" ? withTimePicker ? toDateKeepTime(raw) : toDate(raw) : null;
1265
+ const curRange = mode === "range" ? normRange(raw) : { start: null, end: null };
1266
+ const curDates = mode === "multiple" ? normDates(raw) : [];
1267
+ const commit = (next) => {
1268
+ if (!isControlled) {
1269
+ setInnerRaw(next);
1270
+ }
1271
+ onChange?.(next);
1272
+ };
1273
+ const openIsControlled = openProp !== void 0;
1274
+ const [innerOpen, setInnerOpen] = react.useState(defaultOpen);
1275
+ const open = openIsControlled ? openProp : innerOpen;
1276
+ const setOpen = (next) => {
1277
+ if (!openIsControlled) setInnerOpen(next);
1278
+ onOpenChange?.(next);
1279
+ };
763
1280
  const [gridFocus, setGridFocus] = react.useState(false);
764
- const [text, setText] = react.useState(() => value ? formatDate(value, format, locale) : "");
1281
+ const [hoverDate, setHoverDate] = react.useState(null);
1282
+ const displayText = () => {
1283
+ if (mode === "single") return curDate ? formatDate(curDate, fmt, locale) : "";
1284
+ if (mode === "multiple") {
1285
+ if (curDates.length === 0) return "";
1286
+ if (curDates.length === 1) return formatDate(curDates[0], fmt, locale);
1287
+ return `${curDates.length} selected`;
1288
+ }
1289
+ if (!curRange.start && !curRange.end) return "";
1290
+ const s = curRange.start ? formatDate(curRange.start, fmt, locale) : "\u2026";
1291
+ const e = curRange.end ? formatDate(curRange.end, fmt, locale) : "\u2026";
1292
+ return `${s} \u2013 ${e}`;
1293
+ };
1294
+ const [text, setText] = react.useState(displayText);
765
1295
  const boxRef = react.useRef(null);
766
1296
  const inputRef = react.useRef(null);
1297
+ const skipFocusOpen = react.useRef(false);
1298
+ const valueKey = [
1299
+ mode,
1300
+ typeof fmt === "string" ? fmt : JSON.stringify(fmt),
1301
+ locale ?? "",
1302
+ curDate?.getTime() ?? "",
1303
+ curRange.start?.getTime() ?? "",
1304
+ curRange.end?.getTime() ?? "",
1305
+ curDates.map((d) => d.getTime()).join(",")
1306
+ ].join("|");
767
1307
  react.useEffect(() => {
768
- setText(value ? formatDate(value, format, locale) : "");
769
- }, [value, format, locale]);
1308
+ setText(displayText());
1309
+ }, [valueKey]);
770
1310
  const isDisabledDate = (d) => {
1311
+ if (isDateUnavailable?.(d)) return true;
771
1312
  if (Array.isArray(disabledDates))
772
1313
  return disabledDates.some((x) => isSameDay(startOfDay(x), d));
773
1314
  return Boolean(disabledDates?.(d));
774
1315
  };
775
- const commit = (d) => {
776
- if (!isControlled) setInner(d);
777
- onChange?.(d);
1316
+ const closeAndRefocus = () => {
1317
+ setOpen(false);
1318
+ setGridFocus(false);
1319
+ skipFocusOpen.current = true;
1320
+ inputRef.current?.focus();
778
1321
  };
779
1322
  const select = (d) => {
780
1323
  if (isDisabledDate(d)) return;
781
- commit(startOfDay(d));
782
- if (closeOnSelect) {
783
- setOpen(false);
784
- inputRef.current?.focus();
1324
+ const day = startOfDay(d);
1325
+ if (mode === "single") {
1326
+ const picked = withTimePicker ? withTime(day, curDate ? timeParts(curDate) : { h: 0, m: 0, s: 0 }) : day;
1327
+ commit(picked);
1328
+ if (closeOnSelect) closeAndRefocus();
1329
+ return;
1330
+ }
1331
+ if (mode === "multiple") {
1332
+ const has = curDates.some((x) => isSameDay(x, day));
1333
+ commit(has ? curDates.filter((x) => !isSameDay(x, day)) : [...curDates, day]);
1334
+ return;
785
1335
  }
1336
+ const next = nextRange(curRange, day);
1337
+ commit(next);
1338
+ if (isRangeComplete(next) && closeOnSelect) closeAndRefocus();
1339
+ };
1340
+ const clearValue = () => {
1341
+ if (mode === "single") commit(null);
1342
+ else if (mode === "multiple") commit([]);
1343
+ else commit({ start: null, end: null });
1344
+ onClear?.();
786
1345
  };
787
1346
  const onKeyDown = (e) => {
788
1347
  if (disabled || readOnly) return;
@@ -793,31 +1352,96 @@ var DatePicker = /* @__PURE__ */ react.forwardRef(
793
1352
  } else if (e.key === "Escape" && open) {
794
1353
  e.preventDefault();
795
1354
  setOpen(false);
796
- } else if (e.key === "Enter") {
797
- const parsed = parseDate(text, format);
1355
+ } else if (e.key === "Enter" && mode === "single") {
1356
+ const parsed = parseDate(text, fmt, locale);
798
1357
  if (parsed && !isDisabledDate(startOfDay(parsed))) select(parsed);
799
1358
  }
800
1359
  };
801
- const cal = /* @__PURE__ */ jsxRuntime.jsx(
1360
+ const preview = mode === "range" && curRange.start && !curRange.end ? previewRange(curRange.start, hoverDate) : null;
1361
+ const applyPreset = (p) => {
1362
+ const resolved = p.getValue ? p.getValue() : p.value;
1363
+ if (resolved == null) return;
1364
+ commit(resolved);
1365
+ if (closeOnSelect && !inline) closeAndRefocus();
1366
+ };
1367
+ const setTimeField = (part, n) => {
1368
+ const base2 = curDate ?? startOfDay(/* @__PURE__ */ new Date());
1369
+ const t = timeParts(base2);
1370
+ commit(withTime(base2, { ...t, [part]: n, s: 0 }));
1371
+ };
1372
+ const timeColumn = withTimePicker ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "orynn-datepicker__time", children: [
1373
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-datepicker__time-label", children: "Time" }),
1374
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "orynn-datepicker__time-fields", children: [
1375
+ /* @__PURE__ */ jsxRuntime.jsx(
1376
+ "select",
1377
+ {
1378
+ "aria-label": "Hour",
1379
+ className: "orynn-datepicker__time-select",
1380
+ value: curDate ? curDate.getHours() : 0,
1381
+ onChange: (e) => setTimeField("h", Number(e.target.value)),
1382
+ children: Array.from({ length: 24 }, (_, h) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: h, children: pad2(h) }, h))
1383
+ }
1384
+ ),
1385
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: ":" }),
1386
+ /* @__PURE__ */ jsxRuntime.jsx(
1387
+ "select",
1388
+ {
1389
+ "aria-label": "Minute",
1390
+ className: "orynn-datepicker__time-select",
1391
+ value: curDate ? curDate.getMinutes() - curDate.getMinutes() % timeStep : 0,
1392
+ onChange: (e) => setTimeField("m", Number(e.target.value)),
1393
+ children: minuteOptions(timeStep).map((m) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: m, children: pad2(m) }, m))
1394
+ }
1395
+ )
1396
+ ] })
1397
+ ] }) : null;
1398
+ const calendar = /* @__PURE__ */ jsxRuntime.jsx(
802
1399
  Calendar,
803
1400
  {
804
- value,
1401
+ value: curDate,
805
1402
  onSelect: select,
1403
+ mode,
1404
+ selectedDates: mode === "multiple" ? curDates : void 0,
1405
+ range: mode === "range" ? curRange : void 0,
1406
+ previewRange: preview,
1407
+ onHoverDate: mode === "range" ? setHoverDate : void 0,
806
1408
  minDate,
807
1409
  maxDate,
808
1410
  isDisabled: isDisabledDate,
809
1411
  firstDayOfWeek,
810
1412
  locale,
811
1413
  showWeekNumbers,
812
- showToday,
1414
+ showToday: showToday && mode === "single",
813
1415
  showClear,
1416
+ defaultView,
1417
+ precision,
1418
+ fixedWeeks,
1419
+ numberOfMonths,
1420
+ defaultMonth,
814
1421
  onClear: () => {
815
- commit(null);
1422
+ clearValue();
816
1423
  setOpen(false);
817
1424
  },
818
1425
  autoFocus: open && gridFocus || inline
819
1426
  }
820
1427
  );
1428
+ const body = timeColumn ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "orynn-datepicker__withtime", children: [
1429
+ calendar,
1430
+ timeColumn
1431
+ ] }) : calendar;
1432
+ const cal = presets && presets.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "orynn-datepicker__panel", children: [
1433
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "orynn-datepicker__presets", children: presets.map((p) => /* @__PURE__ */ jsxRuntime.jsx(
1434
+ "button",
1435
+ {
1436
+ type: "button",
1437
+ className: "orynn-datepicker__preset",
1438
+ onClick: () => applyPreset(p),
1439
+ children: p.label
1440
+ },
1441
+ p.label
1442
+ )) }),
1443
+ body
1444
+ ] }) : body;
821
1445
  if (inline) {
822
1446
  return /* @__PURE__ */ jsxRuntime.jsx(
823
1447
  Field,
@@ -834,7 +1458,8 @@ var DatePicker = /* @__PURE__ */ react.forwardRef(
834
1458
  }
835
1459
  );
836
1460
  }
837
- const hasValue = value != null;
1461
+ const hasValue = mode === "single" ? curDate != null : mode === "multiple" ? curDates.length > 0 : Boolean(curRange.start || curRange.end);
1462
+ const typingAllowed = mode === "single" && allowInput && !readOnly;
838
1463
  return /* @__PURE__ */ jsxRuntime.jsx(
839
1464
  Field,
840
1465
  {
@@ -869,10 +1494,7 @@ var DatePicker = /* @__PURE__ */ react.forwardRef(
869
1494
  className: "orynn-box__iconbtn",
870
1495
  "aria-label": "Clear",
871
1496
  tabIndex: -1,
872
- onClick: () => {
873
- commit(null);
874
- onClear?.();
875
- },
1497
+ onClick: clearValue,
876
1498
  children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, {})
877
1499
  }
878
1500
  ),
@@ -888,7 +1510,7 @@ var DatePicker = /* @__PURE__ */ react.forwardRef(
888
1510
  onClick: () => {
889
1511
  if (disabled || readOnly) return;
890
1512
  setGridFocus(true);
891
- setOpen((o) => !o);
1513
+ setOpen(!open);
892
1514
  },
893
1515
  children: /* @__PURE__ */ jsxRuntime.jsx(CalendarIcon, {})
894
1516
  }
@@ -907,7 +1529,7 @@ var DatePicker = /* @__PURE__ */ react.forwardRef(
907
1529
  name,
908
1530
  className: "orynn-box__field",
909
1531
  type: "text",
910
- inputMode: "numeric",
1532
+ inputMode: typingAllowed ? "numeric" : void 0,
911
1533
  autoComplete: "off",
912
1534
  role: "combobox",
913
1535
  "aria-expanded": open,
@@ -917,19 +1539,26 @@ var DatePicker = /* @__PURE__ */ react.forwardRef(
917
1539
  "aria-describedby": describedById,
918
1540
  value: text,
919
1541
  disabled,
920
- readOnly: !allowInput || readOnly,
1542
+ readOnly: !typingAllowed,
921
1543
  required,
922
- placeholder: floatingLabel ? void 0 : placeholder ?? format.toLowerCase(),
1544
+ placeholder: floatingLabel ? void 0 : placeholder ?? (typeof fmt === "string" ? fmt.toLowerCase() : void 0),
923
1545
  onChange: (e) => setText(e.target.value),
924
1546
  onKeyDown,
925
- onFocus: () => setOpen(true),
1547
+ onFocus: () => {
1548
+ if (skipFocusOpen.current) {
1549
+ skipFocusOpen.current = false;
1550
+ return;
1551
+ }
1552
+ setOpen(true);
1553
+ },
926
1554
  onBlur: () => {
927
- const parsed = parseDate(text, format);
1555
+ if (mode !== "single") return;
1556
+ const parsed = parseDate(text, fmt, locale);
928
1557
  if (parsed && !isDisabledDate(startOfDay(parsed))) {
929
1558
  commit(startOfDay(parsed));
930
- setText(formatDate(startOfDay(parsed), format, locale));
1559
+ setText(formatDate(startOfDay(parsed), fmt, locale));
931
1560
  } else {
932
- setText(value ? formatDate(value, format, locale) : "");
1561
+ setText(curDate ? formatDate(curDate, fmt, locale) : "");
933
1562
  }
934
1563
  }
935
1564
  }
@@ -956,7 +1585,13 @@ var DatePicker = /* @__PURE__ */ react.forwardRef(
956
1585
  );
957
1586
  }
958
1587
  );
1588
+ var CREATE_VALUE = "\0orynn-create";
959
1589
  var toArray = (v) => v == null ? [] : Array.isArray(v) ? v : [v];
1590
+ var isGrouped = (o) => {
1591
+ const first = o[0];
1592
+ return first != null && "items" in first && Array.isArray(first.items);
1593
+ };
1594
+ var flattenOptions = (o) => isGrouped(o) ? o.flatMap((g) => g.items.map((it) => ({ ...it, group: it.group ?? g.group }))) : o;
960
1595
  function Highlight({ text, query }) {
961
1596
  if (!query) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: text });
962
1597
  const i = text.toLowerCase().indexOf(query.toLowerCase());
@@ -998,52 +1633,103 @@ var Dropdown = /* @__PURE__ */ react.forwardRef(
998
1633
  highlightMatch,
999
1634
  renderOption,
1000
1635
  renderValue,
1636
+ renderGroupLabel,
1001
1637
  placeholder,
1002
1638
  emptyMessage = "No results",
1639
+ loadingMessage = "Loading\u2026",
1003
1640
  closeOnSelect = !multiple,
1004
1641
  maxSelectedLabels,
1642
+ maxValues,
1643
+ hidePickedOptions = false,
1644
+ showSelectAll = false,
1005
1645
  native = false,
1006
- startContent
1646
+ startContent,
1647
+ creatable = false,
1648
+ onCreate,
1649
+ isValidNewOption,
1650
+ formatCreateLabel,
1651
+ createPosition = "last",
1652
+ onSearch,
1653
+ searchDebounce = 0,
1654
+ open: openProp,
1655
+ defaultOpen = false,
1656
+ onOpenChange,
1657
+ maxHeight
1007
1658
  } = props;
1008
1659
  const reactId = react.useId();
1009
1660
  const id = idProp ?? reactId;
1010
1661
  const listId = `${id}-listbox`;
1011
- const isControlled = valueProp !== void 0;
1012
- const [inner, setInner] = react.useState(() => toArray(defaultValue));
1013
- const selectedValues = isControlled ? toArray(valueProp) : inner;
1014
- const commit = (next) => {
1015
- if (!isControlled) setInner(next);
1016
- onChange?.(multiple ? next : next[0] ?? null);
1662
+ const baseOptions = react.useMemo(() => flattenOptions(options), [options]);
1663
+ const [createdOptions, setCreatedOptions] = react.useState([]);
1664
+ const allOptions = react.useMemo(
1665
+ () => [...baseOptions, ...createdOptions],
1666
+ [baseOptions, createdOptions]
1667
+ );
1668
+ const [selectedValues, setSelectedValues] = useControllableState({
1669
+ value: valueProp === void 0 ? void 0 : toArray(valueProp),
1670
+ defaultValue: toArray(defaultValue),
1671
+ onChange: void 0
1672
+ });
1673
+ const commit = (next) => {
1674
+ setSelectedValues(next);
1675
+ onChange?.(multiple ? next : next[0] ?? null);
1676
+ };
1677
+ const openIsControlled = openProp !== void 0;
1678
+ const [innerOpen, setInnerOpen] = react.useState(defaultOpen);
1679
+ const open = openIsControlled ? openProp : innerOpen;
1680
+ const setOpen = (next) => {
1681
+ if (!openIsControlled) setInnerOpen(next);
1682
+ onOpenChange?.(next);
1017
1683
  };
1018
- const [open, setOpen] = react.useState(false);
1019
1684
  const [query, setQuery] = react.useState("");
1020
1685
  const [activeIndex, setActiveIndex] = react.useState(0);
1021
1686
  const boxRef = react.useRef(null);
1022
1687
  const inputRef = react.useRef(null);
1023
1688
  const listRef = react.useRef(null);
1024
- const byValue = react.useMemo(() => new Map(options.map((o) => [o.value, o])), [options]);
1025
- const selectedOptions = selectedValues.map((v) => byValue.get(v)).filter(Boolean);
1689
+ const byValue = react.useMemo(() => new Map(allOptions.map((o) => [o.value, o])), [allOptions]);
1690
+ const selectedOptions = selectedValues.map((v) => byValue.get(v) ?? { label: v, value: v }).filter(Boolean);
1691
+ react.useEffect(() => {
1692
+ if (!onSearch) return;
1693
+ const t = setTimeout(() => onSearch(query), searchDebounce);
1694
+ return () => clearTimeout(t);
1695
+ }, [query, onSearch, searchDebounce]);
1026
1696
  const filtered = react.useMemo(() => {
1027
- if (!searchable || !query) return options;
1028
- const q = query.toLowerCase();
1029
- const match = filterFn ?? ((o) => filterMode === "startsWith" ? o.label.toLowerCase().startsWith(q) : o.label.toLowerCase().includes(q));
1030
- return options.filter((o) => match(o, query));
1031
- }, [options, searchable, query, filterMode, filterFn]);
1697
+ let list = allOptions;
1698
+ if (searchable && query && !onSearch) {
1699
+ const q = query.toLowerCase();
1700
+ const match = filterFn ?? ((o) => filterMode === "startsWith" ? o.label.toLowerCase().startsWith(q) : o.label.toLowerCase().includes(q));
1701
+ list = list.filter((o) => match(o, query));
1702
+ }
1703
+ if (multiple && hidePickedOptions) {
1704
+ list = list.filter((o) => !selectedValues.includes(o.value));
1705
+ }
1706
+ return list;
1707
+ }, [
1708
+ allOptions,
1709
+ searchable,
1710
+ query,
1711
+ onSearch,
1712
+ filterMode,
1713
+ filterFn,
1714
+ multiple,
1715
+ hidePickedOptions,
1716
+ selectedValues
1717
+ ]);
1718
+ const showCreateRow = creatable && searchable && query.trim().length > 0 && (isValidNewOption ? isValidNewOption(query.trim(), allOptions) : !allOptions.some((o) => o.label.toLowerCase() === query.trim().toLowerCase()));
1719
+ const createRow = { value: CREATE_VALUE, label: query.trim() };
1720
+ const selectableOptions = showCreateRow ? createPosition === "first" ? [createRow, ...filtered] : [...filtered, createRow] : filtered;
1032
1721
  const rows = react.useMemo(() => {
1033
1722
  const out = [];
1034
1723
  let seenGroup;
1035
- let optionIndex = 0;
1036
- for (const o of filtered) {
1037
- if (o.group && o.group !== seenGroup) {
1724
+ selectableOptions.forEach((o, optionIndex) => {
1725
+ if (o.value !== CREATE_VALUE && o.group && o.group !== seenGroup) {
1038
1726
  seenGroup = o.group;
1039
1727
  out.push({ type: "group", label: o.group });
1040
1728
  }
1041
1729
  out.push({ type: "option", option: o, optionIndex });
1042
- optionIndex += 1;
1043
- }
1730
+ });
1044
1731
  return out;
1045
- }, [filtered]);
1046
- const selectableOptions = filtered;
1732
+ }, [selectableOptions]);
1047
1733
  react.useEffect(() => {
1048
1734
  setActiveIndex((i) => Math.min(Math.max(0, i), Math.max(0, selectableOptions.length - 1)));
1049
1735
  }, [selectableOptions.length]);
@@ -1064,10 +1750,34 @@ var Dropdown = /* @__PURE__ */ react.forwardRef(
1064
1750
  setActiveIndex(firstRelevantIndex());
1065
1751
  setOpen(true);
1066
1752
  };
1753
+ const handleCreate = (raw) => {
1754
+ const input = raw.trim();
1755
+ if (!input) return;
1756
+ onCreate?.(input);
1757
+ setCreatedOptions(
1758
+ (prev) => prev.some((o) => o.value === input) || baseOptions.some((o) => o.value === input) ? prev : [...prev, { label: input, value: input }]
1759
+ );
1760
+ if (multiple) {
1761
+ if (maxValues != null && selectedValues.length >= maxValues) return;
1762
+ commit([...selectedValues, input]);
1763
+ } else {
1764
+ commit([input]);
1765
+ }
1766
+ setQuery("");
1767
+ if (closeOnSelect) {
1768
+ setOpen(false);
1769
+ inputRef.current?.focus();
1770
+ }
1771
+ };
1067
1772
  const pick = (opt) => {
1773
+ if (opt.value === CREATE_VALUE) {
1774
+ handleCreate(opt.label);
1775
+ return;
1776
+ }
1068
1777
  if (opt.disabled) return;
1069
1778
  if (multiple) {
1070
1779
  const has = selectedValues.includes(opt.value);
1780
+ if (!has && maxValues != null && selectedValues.length >= maxValues) return;
1071
1781
  commit(
1072
1782
  has ? selectedValues.filter((v) => v !== opt.value) : [...selectedValues, opt.value]
1073
1783
  );
@@ -1085,6 +1795,16 @@ var Dropdown = /* @__PURE__ */ react.forwardRef(
1085
1795
  onClear?.();
1086
1796
  setQuery("");
1087
1797
  };
1798
+ const selectAllTargets = filtered.filter((o) => !o.disabled).map((o) => o.value);
1799
+ const allSelected = selectAllTargets.length > 0 && selectAllTargets.every((v) => selectedValues.includes(v));
1800
+ const toggleSelectAll = () => {
1801
+ if (allSelected) {
1802
+ commit(selectedValues.filter((v) => !selectAllTargets.includes(v)));
1803
+ } else {
1804
+ const merged = Array.from(/* @__PURE__ */ new Set([...selectedValues, ...selectAllTargets]));
1805
+ commit(maxValues != null ? merged.slice(0, maxValues) : merged);
1806
+ }
1807
+ };
1088
1808
  const onKeyDown = (e) => {
1089
1809
  if (disabled || readOnly) return;
1090
1810
  switch (e.key) {
@@ -1149,6 +1869,7 @@ var Dropdown = /* @__PURE__ */ react.forwardRef(
1149
1869
  };
1150
1870
  const hasValue = selectedValues.length > 0;
1151
1871
  const doHighlight = highlightMatch ?? searchable;
1872
+ const isLoadingEmpty = Boolean(loading) && selectableOptions.length === 0;
1152
1873
  if (native && !multiple && !searchable) {
1153
1874
  return /* @__PURE__ */ jsxRuntime.jsx(
1154
1875
  Field,
@@ -1175,7 +1896,7 @@ var Dropdown = /* @__PURE__ */ react.forwardRef(
1175
1896
  onChange: (e) => commit(e.target.value ? [e.target.value] : []),
1176
1897
  children: [
1177
1898
  placeholder != null && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: required, children: placeholder }),
1178
- options.map((o) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: o.value, disabled: o.disabled, children: o.label }, o.value))
1899
+ baseOptions.map((o) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: o.value, disabled: o.disabled, children: o.label }, o.value))
1179
1900
  ]
1180
1901
  }
1181
1902
  )
@@ -1262,7 +1983,6 @@ var Dropdown = /* @__PURE__ */ react.forwardRef(
1262
1983
  {
1263
1984
  ...ref ? { ref: setRefs(ref, inputRef) } : { ref: inputRef },
1264
1985
  id,
1265
- name,
1266
1986
  className: "orynn-select__search",
1267
1987
  role: "combobox",
1268
1988
  "aria-expanded": open,
@@ -1274,7 +1994,11 @@ var Dropdown = /* @__PURE__ */ react.forwardRef(
1274
1994
  autoComplete: "off",
1275
1995
  readOnly: !searchable || readOnly,
1276
1996
  disabled,
1277
- placeholder: !hasValue && (searchable || !floatingLabel) ? placeholder : void 0,
1997
+ placeholder: (
1998
+ // only the search input shows the placeholder; when not
1999
+ // searchable the sibling span owns it (avoids a double render)
2000
+ !hasValue && searchable ? placeholder : void 0
2001
+ ),
1278
2002
  value: query,
1279
2003
  size: 1,
1280
2004
  onChange: (e) => {
@@ -1284,7 +2008,8 @@ var Dropdown = /* @__PURE__ */ react.forwardRef(
1284
2008
  onKeyDown,
1285
2009
  onFocus: openPanel
1286
2010
  }
1287
- )
2011
+ ),
2012
+ name != null && (multiple ? selectedValues.map((v) => /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: v }, v)) : /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: selectedValues[0] ?? "" }))
1288
2013
  ] })
1289
2014
  }
1290
2015
  ),
@@ -1295,44 +2020,61 @@ var Dropdown = /* @__PURE__ */ react.forwardRef(
1295
2020
  anchorRef: boxRef,
1296
2021
  onClose: () => setOpen(false),
1297
2022
  popoverRef: listRef,
1298
- children: /* @__PURE__ */ jsxRuntime.jsx(
2023
+ maxHeight,
2024
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
1299
2025
  "div",
1300
2026
  {
1301
2027
  className: "orynn-listbox",
1302
2028
  id: listId,
1303
2029
  role: "listbox",
1304
2030
  "aria-multiselectable": multiple || void 0,
1305
- children: selectableOptions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "orynn-listbox__empty", children: emptyMessage }) : rows.map(
1306
- (row) => row.type === "group" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "orynn-listbox__group", children: row.label }, `g-${row.label}`) : /* @__PURE__ */ jsxRuntime.jsx(
1307
- "div",
1308
- {
1309
- id: `${id}-opt-${row.option.value}`,
1310
- role: "option",
1311
- "aria-selected": selectedValues.includes(row.option.value),
1312
- "aria-disabled": row.option.disabled || void 0,
1313
- className: "orynn-option",
1314
- "data-active": row.optionIndex === activeIndex || void 0,
1315
- "data-selected": selectedValues.includes(row.option.value) || void 0,
1316
- "data-disabled": row.option.disabled || void 0,
1317
- onMouseEnter: () => setActiveIndex(row.optionIndex),
1318
- onClick: () => pick(row.option),
1319
- children: renderOption ? renderOption(row.option, {
1320
- selected: selectedValues.includes(row.option.value),
1321
- active: row.optionIndex === activeIndex,
1322
- disabled: Boolean(row.option.disabled),
1323
- query
1324
- }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1325
- row.option.icon,
1326
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "orynn-option__body", children: [
1327
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-option__label", children: doHighlight ? /* @__PURE__ */ jsxRuntime.jsx(Highlight, { text: row.option.label, query }) : row.option.label }),
1328
- row.option.description != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-option__desc", children: row.option.description })
1329
- ] }),
1330
- selectedValues.includes(row.option.value) && /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { className: "orynn-option__check" })
1331
- ] })
1332
- },
1333
- row.option.value
2031
+ children: [
2032
+ multiple && showSelectAll && selectAllTargets.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "orynn-listbox__action", onClick: toggleSelectAll, children: allSelected ? "Clear all" : "Select all" }),
2033
+ isLoadingEmpty ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "orynn-listbox__empty", children: loadingMessage }) : selectableOptions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "orynn-listbox__empty", children: emptyMessage }) : rows.map(
2034
+ (row) => row.type === "group" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "orynn-listbox__group", children: renderGroupLabel ? renderGroupLabel(row.label) : row.label }, `g-${row.label}`) : row.option.value === CREATE_VALUE ? /* @__PURE__ */ jsxRuntime.jsx(
2035
+ "div",
2036
+ {
2037
+ id: `${id}-opt-${CREATE_VALUE}`,
2038
+ role: "option",
2039
+ "aria-selected": false,
2040
+ className: "orynn-option orynn-option--create",
2041
+ "data-active": row.optionIndex === activeIndex || void 0,
2042
+ onMouseEnter: () => setActiveIndex(row.optionIndex),
2043
+ onClick: () => pick(row.option),
2044
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-option__body", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-option__label", children: formatCreateLabel ? formatCreateLabel(row.option.label) : `Create "${row.option.label}"` }) })
2045
+ },
2046
+ CREATE_VALUE
2047
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
2048
+ "div",
2049
+ {
2050
+ id: `${id}-opt-${row.option.value}`,
2051
+ role: "option",
2052
+ "aria-selected": selectedValues.includes(row.option.value),
2053
+ "aria-disabled": row.option.disabled || void 0,
2054
+ className: "orynn-option",
2055
+ "data-active": row.optionIndex === activeIndex || void 0,
2056
+ "data-selected": selectedValues.includes(row.option.value) || void 0,
2057
+ "data-disabled": row.option.disabled || void 0,
2058
+ onMouseEnter: () => setActiveIndex(row.optionIndex),
2059
+ onClick: () => pick(row.option),
2060
+ children: renderOption ? renderOption(row.option, {
2061
+ selected: selectedValues.includes(row.option.value),
2062
+ active: row.optionIndex === activeIndex,
2063
+ disabled: Boolean(row.option.disabled),
2064
+ query
2065
+ }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2066
+ row.option.icon,
2067
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "orynn-option__body", children: [
2068
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-option__label", children: doHighlight ? /* @__PURE__ */ jsxRuntime.jsx(Highlight, { text: row.option.label, query }) : row.option.label }),
2069
+ row.option.description != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-option__desc", children: row.option.description })
2070
+ ] }),
2071
+ selectedValues.includes(row.option.value) && /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { className: "orynn-option__check" })
2072
+ ] })
2073
+ },
2074
+ row.option.value
2075
+ )
1334
2076
  )
1335
- )
2077
+ ]
1336
2078
  }
1337
2079
  )
1338
2080
  }
@@ -1380,8 +2122,14 @@ var Input = /* @__PURE__ */ react.forwardRef(
1380
2122
  type = "text",
1381
2123
  prefix,
1382
2124
  suffix,
2125
+ addonBefore,
2126
+ addonAfter,
1383
2127
  passwordToggle,
2128
+ passwordVisible,
2129
+ onPasswordVisibleChange,
2130
+ visibilityToggleIcon,
1384
2131
  showCount,
2132
+ debounce,
1385
2133
  maxLength,
1386
2134
  placeholder,
1387
2135
  ...rest
@@ -1393,14 +2141,32 @@ var Input = /* @__PURE__ */ react.forwardRef(
1393
2141
  defaultValue: defaultValue ?? "",
1394
2142
  onChange: void 0
1395
2143
  });
1396
- const [reveal, setReveal] = react.useState(false);
2144
+ const revealIsControlled = passwordVisible !== void 0;
2145
+ const [innerReveal, setInnerReveal] = react.useState(false);
2146
+ const reveal = revealIsControlled ? passwordVisible : innerReveal;
2147
+ const setReveal = (next) => {
2148
+ if (!revealIsControlled) setInnerReveal(next);
2149
+ onPasswordVisibleChange?.(next);
2150
+ };
2151
+ const debounceTimer = react.useRef();
2152
+ react.useEffect(() => () => clearTimeout(debounceTimer.current), []);
2153
+ const emitChange = (v, e) => {
2154
+ if (debounce && debounce > 0) {
2155
+ clearTimeout(debounceTimer.current);
2156
+ debounceTimer.current = setTimeout(() => onChange?.(v, e), debounce);
2157
+ } else {
2158
+ onChange?.(v, e);
2159
+ }
2160
+ };
1397
2161
  const showToggle = (passwordToggle ?? type === "password") && !disabled && !readOnly;
1398
2162
  const effectiveType = type === "password" && reveal ? "text" : type;
1399
2163
  const hasValue = value.length > 0;
1400
2164
  const showClear = clearable && hasValue && !disabled && !readOnly;
2165
+ const countMax = showCount && typeof showCount === "object" ? showCount.max ?? maxLength : maxLength;
2166
+ const countNode = showCount && typeof showCount === "object" && showCount.formatter ? showCount.formatter(value.length, countMax) : `${value.length}${countMax ? `/${countMax}` : ""}`;
1401
2167
  const handleChange = (e) => {
1402
2168
  setValue(e.target.value);
1403
- onChange?.(e.target.value, e);
2169
+ emitChange(e.target.value, e);
1404
2170
  };
1405
2171
  const handleKeyDown = (e) => {
1406
2172
  if (e.key === "Enter") onEnter?.(value);
@@ -1432,16 +2198,15 @@ var Input = /* @__PURE__ */ react.forwardRef(
1432
2198
  invalid,
1433
2199
  valid,
1434
2200
  active: hasValue,
2201
+ addonBefore,
2202
+ addonAfter,
1435
2203
  left: startContent != null || prefix != null ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1436
2204
  startContent,
1437
2205
  prefix != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-box__adornment--affix", children: prefix })
1438
2206
  ] }) : void 0,
1439
2207
  right: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1440
2208
  suffix != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-box__adornment--affix", children: suffix }),
1441
- showCount && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "orynn-box__adornment--affix", children: [
1442
- value.length,
1443
- maxLength ? `/${maxLength}` : ""
1444
- ] }),
2209
+ showCount && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-box__adornment--affix", children: countNode }),
1445
2210
  showClear && /* @__PURE__ */ jsxRuntime.jsx(
1446
2211
  "button",
1447
2212
  {
@@ -1461,8 +2226,8 @@ var Input = /* @__PURE__ */ react.forwardRef(
1461
2226
  "aria-label": reveal ? "Hide" : "Show",
1462
2227
  "aria-pressed": reveal,
1463
2228
  tabIndex: -1,
1464
- onClick: () => setReveal((v) => !v),
1465
- children: reveal ? /* @__PURE__ */ jsxRuntime.jsx(EyeOffIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(EyeIcon, {})
2229
+ onClick: () => setReveal(!reveal),
2230
+ children: visibilityToggleIcon ? visibilityToggleIcon(reveal) : reveal ? /* @__PURE__ */ jsxRuntime.jsx(EyeOffIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(EyeIcon, {})
1466
2231
  }
1467
2232
  ),
1468
2233
  loading && /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, {}),
@@ -1499,6 +2264,11 @@ var Input = /* @__PURE__ */ react.forwardRef(
1499
2264
  );
1500
2265
  var clamp = (n, min, max) => Math.min(max ?? Number.POSITIVE_INFINITY, Math.max(min ?? Number.NEGATIVE_INFINITY, n));
1501
2266
  var round = (n, p) => p == null ? n : Math.round(n * 10 ** p) / 10 ** p;
2267
+ var SENTINEL_GROUP = "[[g]]";
2268
+ var SENTINEL_DECIMAL = "[[d]]";
2269
+ function swapSeparators(out, seps, thousandSeparator, decimalSeparator) {
2270
+ return out.split(seps.group).join(SENTINEL_GROUP).split(seps.decimal).join(SENTINEL_DECIMAL).split(SENTINEL_GROUP).join(thousandSeparator ?? seps.group).split(SENTINEL_DECIMAL).join(decimalSeparator ?? seps.decimal);
2271
+ }
1502
2272
  var NumberField = /* @__PURE__ */ react.forwardRef(
1503
2273
  function NumberField2(props, ref) {
1504
2274
  const {
@@ -1521,19 +2291,37 @@ var NumberField = /* @__PURE__ */ react.forwardRef(
1521
2291
  value: valueProp,
1522
2292
  defaultValue = null,
1523
2293
  onChange,
2294
+ onValueChange,
1524
2295
  min,
1525
2296
  max,
1526
2297
  step = 1,
1527
2298
  shiftStep,
2299
+ smallStep,
1528
2300
  precision,
2301
+ decimalScale,
2302
+ fixedDecimalScale,
1529
2303
  buttons = "stacked",
2304
+ hideControls,
1530
2305
  grouping,
2306
+ thousandSeparator,
2307
+ decimalSeparator,
1531
2308
  locale,
2309
+ style,
1532
2310
  currency,
2311
+ unit,
2312
+ formatOptions,
2313
+ format: formatFn,
2314
+ parse: parseFn,
1533
2315
  prefix,
1534
2316
  suffix,
1535
- clampOnBlur = true,
2317
+ allowNegative = true,
2318
+ clampBehavior,
2319
+ clampOnBlur,
2320
+ emptyValue = null,
1536
2321
  allowMouseWheel,
2322
+ isWheelDisabled,
2323
+ stepHoldDelay = 300,
2324
+ stepHoldInterval = 60,
1537
2325
  placeholder,
1538
2326
  ...rest
1539
2327
  } = props;
@@ -1544,15 +2332,45 @@ var NumberField = /* @__PURE__ */ react.forwardRef(
1544
2332
  defaultValue,
1545
2333
  onChange: void 0
1546
2334
  });
1547
- const fmt = react.useMemo(
1548
- () => new Intl.NumberFormat(locale, {
1549
- ...currency ? { style: "currency", currency } : {},
1550
- useGrouping: grouping ?? Boolean(currency),
1551
- maximumFractionDigits: precision ?? (currency ? 2 : 20),
1552
- minimumFractionDigits: currency ? 2 : 0
1553
- }),
1554
- [locale, currency, grouping, precision]
1555
- );
2335
+ const resolvedStyle = style ?? (currency ? "currency" : "decimal");
2336
+ const scale = decimalScale ?? precision;
2337
+ const clampMode = clampBehavior ?? (clampOnBlur === false ? "none" : "blur");
2338
+ const effMin = allowNegative ? min : Math.max(0, min ?? 0);
2339
+ const showButtons = hideControls ? false : buttons;
2340
+ const wheelEnabled = isWheelDisabled != null ? !isWheelDisabled : Boolean(allowMouseWheel);
2341
+ const fmt = react.useMemo(() => {
2342
+ if (formatOptions) return new Intl.NumberFormat(locale, formatOptions);
2343
+ const opts = {
2344
+ useGrouping: grouping ?? (thousandSeparator != null || resolvedStyle === "currency")
2345
+ };
2346
+ if (resolvedStyle === "currency") {
2347
+ opts.style = "currency";
2348
+ opts.currency = currency ?? "USD";
2349
+ } else if (resolvedStyle === "percent") {
2350
+ opts.style = "percent";
2351
+ } else if (resolvedStyle === "unit" && unit) {
2352
+ opts.style = "unit";
2353
+ opts.unit = unit;
2354
+ }
2355
+ if (scale != null) {
2356
+ opts.maximumFractionDigits = scale;
2357
+ if (fixedDecimalScale) opts.minimumFractionDigits = scale;
2358
+ } else {
2359
+ opts.maximumFractionDigits = resolvedStyle === "currency" ? 2 : 20;
2360
+ opts.minimumFractionDigits = resolvedStyle === "currency" ? 2 : 0;
2361
+ }
2362
+ return new Intl.NumberFormat(locale, opts);
2363
+ }, [
2364
+ locale,
2365
+ formatOptions,
2366
+ grouping,
2367
+ thousandSeparator,
2368
+ resolvedStyle,
2369
+ currency,
2370
+ unit,
2371
+ scale,
2372
+ fixedDecimalScale
2373
+ ]);
1556
2374
  const seps = react.useMemo(() => {
1557
2375
  const parts = new Intl.NumberFormat(locale).formatToParts(11111.1);
1558
2376
  return {
@@ -1561,23 +2379,47 @@ var NumberField = /* @__PURE__ */ react.forwardRef(
1561
2379
  };
1562
2380
  }, [locale]);
1563
2381
  const display = react.useCallback(
1564
- (n) => n == null || Number.isNaN(n) ? "" : `${prefix ?? ""}${fmt.format(n)}${suffix ?? ""}`,
1565
- [fmt, prefix, suffix]
2382
+ (n) => {
2383
+ if (n == null || Number.isNaN(n)) return "";
2384
+ if (formatFn) return formatFn(n);
2385
+ let out = fmt.format(n);
2386
+ if (thousandSeparator != null || decimalSeparator != null) {
2387
+ out = swapSeparators(out, seps, thousandSeparator, decimalSeparator);
2388
+ }
2389
+ return `${prefix ?? ""}${out}${suffix ?? ""}`;
2390
+ },
2391
+ [fmt, formatFn, prefix, suffix, seps, thousandSeparator, decimalSeparator]
1566
2392
  );
1567
2393
  const parse = react.useCallback(
1568
2394
  (raw) => {
2395
+ if (parseFn) return parseFn(raw);
1569
2396
  let s = raw;
1570
2397
  if (prefix) s = s.split(prefix).join("");
1571
2398
  if (suffix) s = s.split(suffix).join("");
1572
- s = s.split(seps.group).join("").split(seps.decimal).join(".");
2399
+ const grp = thousandSeparator ?? seps.group;
2400
+ const dec = decimalSeparator ?? seps.decimal;
2401
+ s = s.split(grp).join("").split(dec).join(".");
1573
2402
  s = s.replace(/[^\d.\-]/g, "");
2403
+ if (!allowNegative) s = s.replace(/-/g, "");
1574
2404
  if (s === "" || s === "-" || s === ".") return null;
1575
2405
  const first = s.indexOf(".");
1576
2406
  if (first !== -1) s = s.slice(0, first + 1) + s.slice(first + 1).replace(/\./g, "");
1577
- const n = Number(s);
1578
- return Number.isNaN(n) ? null : n;
2407
+ let n = Number(s);
2408
+ if (Number.isNaN(n)) return null;
2409
+ if (resolvedStyle === "percent" && !formatFn) n = n / 100;
2410
+ return n;
1579
2411
  },
1580
- [prefix, suffix, seps]
2412
+ [
2413
+ parseFn,
2414
+ prefix,
2415
+ suffix,
2416
+ seps,
2417
+ thousandSeparator,
2418
+ decimalSeparator,
2419
+ allowNegative,
2420
+ resolvedStyle,
2421
+ formatFn
2422
+ ]
1581
2423
  );
1582
2424
  const [text, setText] = react.useState(() => display(value));
1583
2425
  const [editing, setEditing] = react.useState(false);
@@ -1587,65 +2429,76 @@ var NumberField = /* @__PURE__ */ react.forwardRef(
1587
2429
  const commit = (n) => {
1588
2430
  setValue(n);
1589
2431
  onChange?.(n);
2432
+ onValueChange?.({ value: n, formatted: display(n) });
1590
2433
  };
1591
2434
  const setNumber = (n) => {
1592
2435
  commit(n);
1593
2436
  setText(display(n));
1594
2437
  };
1595
- const bump = (dir, big = false) => {
1596
- const delta = (big ? shiftStep ?? step * 10 : step) * dir;
1597
- const base2 = value ?? (dir > 0 ? min ?? 0 : max ?? 0);
1598
- setNumber(round(clamp(base2 + delta, min, max), precision));
2438
+ const bump = (dir, mode = "n") => {
2439
+ const amount = mode === "l" ? shiftStep ?? step * 10 : mode === "s" ? smallStep ?? step / 10 : step;
2440
+ const base2 = value ?? (dir > 0 ? effMin ?? 0 : max ?? 0);
2441
+ let next = round(base2 + amount * dir, scale);
2442
+ if (clampMode !== "none") next = clamp(next, effMin, max);
2443
+ else if (!allowNegative && next < 0) next = 0;
2444
+ setNumber(next);
1599
2445
  };
1600
2446
  const innerRef = react.useRef(null);
1601
2447
  react.useEffect(() => {
1602
2448
  const el = innerRef.current;
1603
- if (!el || !allowMouseWheel) return;
2449
+ if (!el || !wheelEnabled) return;
1604
2450
  const onWheel = (e) => {
1605
2451
  if (document.activeElement !== el) return;
1606
2452
  e.preventDefault();
1607
- bump(e.deltaY < 0 ? 1 : -1, e.shiftKey);
2453
+ bump(e.deltaY < 0 ? 1 : -1, e.shiftKey ? "l" : e.altKey ? "s" : "n");
1608
2454
  };
1609
2455
  el.addEventListener("wheel", onWheel, { passive: false });
1610
2456
  return () => el.removeEventListener("wheel", onWheel);
1611
2457
  });
1612
2458
  const onKeyDown = (e) => {
1613
2459
  if (disabled || readOnly) return;
2460
+ const mode = e.shiftKey ? "l" : e.altKey ? "s" : "n";
1614
2461
  if (e.key === "ArrowUp") {
1615
2462
  e.preventDefault();
1616
- bump(1, e.shiftKey);
2463
+ bump(1, mode);
1617
2464
  } else if (e.key === "ArrowDown") {
1618
2465
  e.preventDefault();
1619
- bump(-1, e.shiftKey);
2466
+ bump(-1, mode);
1620
2467
  } else if (e.key === "PageUp") {
1621
2468
  e.preventDefault();
1622
- bump(1, true);
2469
+ bump(1, "l");
1623
2470
  } else if (e.key === "PageDown") {
1624
2471
  e.preventDefault();
1625
- bump(-1, true);
1626
- } else if (e.key === "Home" && min != null) {
2472
+ bump(-1, "l");
2473
+ } else if (e.key === "Home" && effMin != null) {
1627
2474
  e.preventDefault();
1628
- setNumber(min);
2475
+ setNumber(effMin);
1629
2476
  } else if (e.key === "End" && max != null) {
1630
2477
  e.preventDefault();
1631
2478
  setNumber(max);
1632
2479
  }
1633
2480
  };
1634
2481
  const holdTimer = react.useRef();
2482
+ const holdDelay = react.useRef();
1635
2483
  const startHold = (dir) => {
1636
2484
  bump(dir);
1637
2485
  let count = 0;
1638
- holdTimer.current = setInterval(() => {
1639
- count += 1;
1640
- bump(dir, count > 6);
1641
- }, 110);
2486
+ holdDelay.current = setTimeout(() => {
2487
+ holdTimer.current = setInterval(() => {
2488
+ count += 1;
2489
+ bump(dir, count > 6 ? "l" : "n");
2490
+ }, stepHoldInterval);
2491
+ }, stepHoldDelay);
1642
2492
  };
1643
- const stopHold = () => clearInterval(holdTimer.current);
1644
- react.useEffect(() => () => clearInterval(holdTimer.current), []);
1645
- const StepButtons = buttons && /* @__PURE__ */ jsxRuntime.jsxs(
2493
+ const stopHold = () => {
2494
+ clearTimeout(holdDelay.current);
2495
+ clearInterval(holdTimer.current);
2496
+ };
2497
+ react.useEffect(() => stopHold, []);
2498
+ const StepButtons = showButtons && /* @__PURE__ */ jsxRuntime.jsxs(
1646
2499
  "div",
1647
2500
  {
1648
- className: `orynn-number__buttons${buttons === "horizontal" ? " orynn-number__buttons--horizontal" : ""}`,
2501
+ className: `orynn-number__buttons${showButtons === "horizontal" ? " orynn-number__buttons--horizontal" : ""}`,
1649
2502
  children: [
1650
2503
  /* @__PURE__ */ jsxRuntime.jsx(
1651
2504
  "button",
@@ -1658,7 +2511,7 @@ var NumberField = /* @__PURE__ */ react.forwardRef(
1658
2511
  onPointerDown: () => startHold(1),
1659
2512
  onPointerUp: stopHold,
1660
2513
  onPointerLeave: stopHold,
1661
- children: /* @__PURE__ */ jsxRuntime.jsx(PlusIcon, {})
2514
+ children: /* @__PURE__ */ jsxRuntime.jsx(ChevronUpIcon, { className: "orynn-number__caret" })
1662
2515
  }
1663
2516
  ),
1664
2517
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1668,11 +2521,11 @@ var NumberField = /* @__PURE__ */ react.forwardRef(
1668
2521
  className: "orynn-number__btn",
1669
2522
  "aria-label": "Decrement",
1670
2523
  tabIndex: -1,
1671
- disabled: disabled || readOnly || min != null && (value ?? 0) <= min,
2524
+ disabled: disabled || readOnly || effMin != null && (value ?? 0) <= effMin,
1672
2525
  onPointerDown: () => startHold(-1),
1673
2526
  onPointerUp: stopHold,
1674
2527
  onPointerLeave: stopHold,
1675
- children: /* @__PURE__ */ jsxRuntime.jsx(MinusIcon, {})
2528
+ children: /* @__PURE__ */ jsxRuntime.jsx(ChevronDownIcon, { className: "orynn-number__caret" })
1676
2529
  }
1677
2530
  )
1678
2531
  ]
@@ -1723,7 +2576,7 @@ var NumberField = /* @__PURE__ */ react.forwardRef(
1723
2576
  inputMode: "decimal",
1724
2577
  role: "spinbutton",
1725
2578
  "aria-valuenow": value ?? void 0,
1726
- "aria-valuemin": min,
2579
+ "aria-valuemin": effMin,
1727
2580
  "aria-valuemax": max,
1728
2581
  value: text,
1729
2582
  disabled,
@@ -1735,13 +2588,16 @@ var NumberField = /* @__PURE__ */ react.forwardRef(
1735
2588
  onFocus: () => setEditing(true),
1736
2589
  onChange: (e) => {
1737
2590
  setText(e.target.value);
1738
- commit(parse(e.target.value));
2591
+ const parsed = parse(e.target.value);
2592
+ commit(
2593
+ parsed == null ? null : clampMode === "strict" ? clamp(parsed, effMin, max) : parsed
2594
+ );
1739
2595
  },
1740
2596
  onKeyDown,
1741
2597
  onBlur: () => {
1742
2598
  setEditing(false);
1743
2599
  const parsed = parse(text);
1744
- const final = parsed == null ? null : round(clampOnBlur ? clamp(parsed, min, max) : parsed, precision);
2600
+ const final = parsed == null ? emptyValue : round(clampMode !== "none" ? clamp(parsed, effMin, max) : parsed, scale);
1745
2601
  if (final !== value) commit(final);
1746
2602
  setText(display(final));
1747
2603
  }
@@ -1754,7 +2610,7 @@ var NumberField = /* @__PURE__ */ react.forwardRef(
1754
2610
  }
1755
2611
  );
1756
2612
  var RadioGroupContext = react.createContext(null);
1757
- var sizePx2 = { sm: "0.95rem", md: "1.125rem", lg: "1.35rem" };
2613
+ var sizePx2 = { sm: "0.875rem", md: "1rem", lg: "1.125rem" };
1758
2614
  var Radio = /* @__PURE__ */ react.forwardRef(
1759
2615
  function Radio2(props, ref) {
1760
2616
  const group = react.useContext(RadioGroupContext);
@@ -1831,6 +2687,7 @@ function RadioGroup(props) {
1831
2687
  defaultValue = null,
1832
2688
  onChange,
1833
2689
  orientation = "vertical",
2690
+ columns,
1834
2691
  options,
1835
2692
  children,
1836
2693
  className,
@@ -1866,12 +2723,17 @@ function RadioGroup(props) {
1866
2723
  "aria-labelledby": label != null ? `${id}-label` : void 0,
1867
2724
  "aria-required": required || void 0,
1868
2725
  className: "orynn-choice-group",
1869
- "data-orientation": orientation,
2726
+ "data-orientation": columns ? void 0 : orientation,
2727
+ "data-columns": columns ? "" : void 0,
2728
+ style: columns ? { "--orynn-choice-columns": columns } : void 0,
1870
2729
  children: options ? options.map((o) => /* @__PURE__ */ jsxRuntime.jsx(
1871
2730
  Radio,
1872
2731
  {
1873
2732
  value: o.value,
1874
- label: o.label,
2733
+ label: o.icon != null ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2734
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-choice__icon", children: o.icon }),
2735
+ o.label
2736
+ ] }) : o.label,
1875
2737
  description: o.description,
1876
2738
  disabled: o.disabled
1877
2739
  },
@@ -1898,12 +2760,17 @@ var Textarea = /* @__PURE__ */ react.forwardRef(
1898
2760
  floatingLabel,
1899
2761
  clearable,
1900
2762
  onClear,
2763
+ loading,
2764
+ endContent,
1901
2765
  id: idProp,
1902
2766
  name,
1903
2767
  className,
1904
2768
  value: valueProp,
1905
2769
  defaultValue,
1906
2770
  onChange,
2771
+ onEnter,
2772
+ submitOnEnter,
2773
+ onKeyDown,
1907
2774
  autoResize,
1908
2775
  minRows = 3,
1909
2776
  maxRows = 10,
@@ -1928,9 +2795,9 @@ var Textarea = /* @__PURE__ */ react.forwardRef(
1928
2795
  el.style.height = "auto";
1929
2796
  const cs = getComputedStyle(el);
1930
2797
  const line = Number.parseFloat(cs.lineHeight) || 20;
1931
- const pad2 = Number.parseFloat(cs.paddingTop) + Number.parseFloat(cs.paddingBottom);
1932
- const min = line * minRows + pad2;
1933
- const max = line * maxRows + pad2;
2798
+ const pad3 = Number.parseFloat(cs.paddingTop) + Number.parseFloat(cs.paddingBottom);
2799
+ const min = line * minRows + pad3;
2800
+ const max = line * maxRows + pad3;
1934
2801
  el.style.height = `${Math.min(Math.max(el.scrollHeight, min), max)}px`;
1935
2802
  el.style.overflowY = el.scrollHeight > max ? "auto" : "hidden";
1936
2803
  }, [autoResize, minRows, maxRows]);
@@ -1939,7 +2806,16 @@ var Textarea = /* @__PURE__ */ react.forwardRef(
1939
2806
  setValue(e.target.value);
1940
2807
  onChange?.(e.target.value, e);
1941
2808
  };
2809
+ const handleKeyDown = (e) => {
2810
+ if (e.key === "Enter" && !e.shiftKey && (onEnter || submitOnEnter)) {
2811
+ if (submitOnEnter) e.preventDefault();
2812
+ onEnter?.(value);
2813
+ }
2814
+ onKeyDown?.(e);
2815
+ };
1942
2816
  const hasValue = value.length > 0;
2817
+ const countMax = showCount && typeof showCount === "object" ? showCount.max ?? maxLength : maxLength;
2818
+ const countNode = showCount && typeof showCount === "object" && showCount.formatter ? showCount.formatter(value.length, countMax) : `${value.length}${countMax ? `/${countMax}` : ""}`;
1943
2819
  return /* @__PURE__ */ jsxRuntime.jsx(
1944
2820
  Field,
1945
2821
  {
@@ -1964,20 +2840,24 @@ var Textarea = /* @__PURE__ */ react.forwardRef(
1964
2840
  valid,
1965
2841
  active: hasValue,
1966
2842
  floatingLabel: floatingLabel ? /* @__PURE__ */ jsxRuntime.jsx("label", { className: "orynn-box__label", htmlFor: id, children: label }) : void 0,
1967
- right: clearable && hasValue && !disabled && !readOnly ? /* @__PURE__ */ jsxRuntime.jsx(
1968
- "button",
1969
- {
1970
- type: "button",
1971
- className: "orynn-box__iconbtn",
1972
- "aria-label": "Clear",
1973
- tabIndex: -1,
1974
- onClick: () => {
1975
- setValue("");
1976
- onClear?.();
1977
- },
1978
- children: "\u2715"
1979
- }
1980
- ) : void 0,
2843
+ right: clearable && hasValue || loading || endContent != null ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2844
+ clearable && hasValue && !disabled && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
2845
+ "button",
2846
+ {
2847
+ type: "button",
2848
+ className: "orynn-box__iconbtn",
2849
+ "aria-label": "Clear",
2850
+ tabIndex: -1,
2851
+ onClick: () => {
2852
+ setValue("");
2853
+ onClear?.();
2854
+ },
2855
+ children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, {})
2856
+ }
2857
+ ),
2858
+ loading && /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, {}),
2859
+ endContent
2860
+ ] }) : void 0,
1981
2861
  children: [
1982
2862
  /* @__PURE__ */ jsxRuntime.jsx(
1983
2863
  "textarea",
@@ -1997,13 +2877,11 @@ var Textarea = /* @__PURE__ */ react.forwardRef(
1997
2877
  placeholder: floatingLabel ? void 0 : placeholder,
1998
2878
  "aria-invalid": invalid || void 0,
1999
2879
  "aria-describedby": describedById,
2000
- onChange: handleChange
2880
+ onChange: handleChange,
2881
+ onKeyDown: handleKeyDown
2001
2882
  }
2002
2883
  ),
2003
- showCount && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "orynn-box__footer", children: [
2004
- value.length,
2005
- maxLength ? `/${maxLength}` : ""
2006
- ] })
2884
+ showCount && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-box__footer", children: countNode })
2007
2885
  ]
2008
2886
  }
2009
2887
  )
@@ -2060,24 +2938,63 @@ function registerField(type, registration) {
2060
2938
 
2061
2939
  // src/layout/resolve-columns.ts
2062
2940
  var DEFAULT_COLUMNS = { desktop: 3, tablet: 2, mobile: 1 };
2941
+ var DEFAULT_MIN_COL = 192;
2063
2942
  var clampCount = (n, fallback) => typeof n === "number" && Number.isFinite(n) && n >= 1 ? Math.floor(n) : fallback;
2064
- function resolveColumns(partial) {
2943
+ function resolveColumns(input) {
2944
+ if (typeof input === "number") {
2945
+ const n = clampCount(input, DEFAULT_COLUMNS.desktop);
2946
+ return { desktop: n, tablet: n, mobile: n };
2947
+ }
2065
2948
  return {
2066
- desktop: clampCount(partial?.desktop, DEFAULT_COLUMNS.desktop),
2067
- tablet: clampCount(partial?.tablet, DEFAULT_COLUMNS.tablet),
2068
- mobile: clampCount(partial?.mobile, DEFAULT_COLUMNS.mobile)
2949
+ desktop: clampCount(input?.desktop, DEFAULT_COLUMNS.desktop),
2950
+ tablet: clampCount(input?.tablet, DEFAULT_COLUMNS.tablet),
2951
+ mobile: clampCount(input?.mobile, DEFAULT_COLUMNS.mobile)
2069
2952
  };
2070
2953
  }
2954
+ function resolveGrid(width, cols, minColWidth = DEFAULT_MIN_COL) {
2955
+ const { desktop: d, tablet: t, mobile: m } = cols;
2956
+ if (!width || !Number.isFinite(width) || width <= 0) {
2957
+ return { count: d, tier: "desktop" };
2958
+ }
2959
+ const per = minColWidth > 0 ? minColWidth : DEFAULT_MIN_COL;
2960
+ const maxFit = Math.max(1, Math.floor(width / per));
2961
+ if (d === t && t === m) {
2962
+ const count = Math.min(d, maxFit);
2963
+ return { count, tier: count >= d ? "desktop" : count > 1 ? "tablet" : "mobile" };
2964
+ }
2965
+ if (maxFit >= d) return { count: d, tier: "desktop" };
2966
+ if (t < d && maxFit >= t) return { count: t, tier: "tablet" };
2967
+ if (maxFit >= m) return { count: m, tier: "mobile" };
2968
+ return { count: Math.min(m, maxFit), tier: "mobile" };
2969
+ }
2071
2970
  function clampSpan(span, columnCount) {
2072
2971
  const n = clampCount(span, 1);
2073
2972
  return Math.min(n, Math.max(columnCount, 1));
2074
2973
  }
2974
+ function pickSpan(layout, tier) {
2975
+ if (!layout) return 1;
2976
+ const v = layout[tier] ?? layout.desktop ?? layout.tablet ?? layout.mobile;
2977
+ return typeof v === "number" && Number.isFinite(v) && v >= 1 ? Math.floor(v) : 1;
2978
+ }
2075
2979
 
2076
2980
  // src/core/config/normalize.ts
2077
2981
  function normalizeGap(gap) {
2078
2982
  if (gap == null) return void 0;
2079
2983
  return typeof gap === "number" ? `${gap}px` : gap;
2080
2984
  }
2985
+ function normalizeMinColWidth(value) {
2986
+ if (typeof value === "number") return value > 0 ? value : DEFAULT_MIN_COL;
2987
+ if (typeof value === "string") {
2988
+ const n = Number.parseFloat(value);
2989
+ if (!Number.isFinite(n) || n <= 0) return DEFAULT_MIN_COL;
2990
+ if (/rem|em/i.test(value)) {
2991
+ const root = typeof document !== "undefined" ? Number.parseFloat(getComputedStyle(document.documentElement).fontSize) || 16 : 16;
2992
+ return n * root;
2993
+ }
2994
+ return n;
2995
+ }
2996
+ return DEFAULT_MIN_COL;
2997
+ }
2081
2998
  function normalizeConfig(config, registry2) {
2082
2999
  invariant(
2083
3000
  config != null && typeof config === "object" && Array.isArray(config.fields),
@@ -2111,18 +3028,52 @@ function normalizeConfig(config, registry2) {
2111
3028
  return {
2112
3029
  fields: config.fields,
2113
3030
  columns: resolveColumns(config.columns),
3031
+ minColWidth: normalizeMinColWidth(config.minColumnWidth),
2114
3032
  gap: normalizeGap(config.gap),
2115
3033
  legend: config.legend,
2116
3034
  id: config.id
2117
3035
  };
2118
3036
  }
2119
3037
 
3038
+ // src/core/config/conditions.ts
3039
+ function evalCondition(when, values) {
3040
+ if (when == null) return true;
3041
+ if (typeof when === "function") return Boolean(when(values));
3042
+ const actual = values[when.field];
3043
+ if ("eq" in when) return actual === when.eq;
3044
+ if ("ne" in when) return actual !== when.ne;
3045
+ if ("in" in when) return Array.isArray(when.in) && when.in.includes(actual);
3046
+ if ("notIn" in when) return Array.isArray(when.notIn) && !when.notIn.includes(actual);
3047
+ if ("truthy" in when) return Boolean(actual) === Boolean(when.truthy);
3048
+ return Boolean(actual);
3049
+ }
3050
+
2120
3051
  // src/core/validation/messages.ts
2121
3052
  var plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
3053
+ var dateStr = (p) => {
3054
+ const d = p instanceof Date ? p : new Date(String(p));
3055
+ return Number.isNaN(d.getTime()) ? String(p) : d.toLocaleDateString();
3056
+ };
2122
3057
  var defaultMessages = {
2123
3058
  required: ({ label }) => `${label} is required`,
2124
3059
  minLength: ({ param, label }) => `${label} must be at least ${plural(param, "character")}`,
2125
- maxLength: ({ param, label }) => `${label} must be at most ${plural(param, "character")}`
3060
+ maxLength: ({ param, label }) => `${label} must be at most ${plural(param, "character")}`,
3061
+ minItems: ({ param, label }) => `Select at least ${plural(param, "option")} for ${label}`,
3062
+ maxItems: ({ param, label }) => `Select at most ${plural(param, "option")} for ${label}`,
3063
+ min: ({ param, label }) => `${label} must be at least ${param}`,
3064
+ max: ({ param, label }) => `${label} must be at most ${param}`,
3065
+ integer: ({ label }) => `${label} must be a whole number`,
3066
+ minDate: ({ param, label }) => `${label} must be on or after ${dateStr(param)}`,
3067
+ maxDate: ({ param, label }) => `${label} must be on or before ${dateStr(param)}`,
3068
+ minRangeDays: ({ param, label }) => `${label} must span at least ${plural(param, "day")}`,
3069
+ maxRangeDays: ({ param, label }) => `${label} must span at most ${plural(param, "day")}`,
3070
+ email: ({ label }) => `${label} must be a valid email address`,
3071
+ url: ({ label }) => `${label} must be a valid URL`,
3072
+ pattern: ({ label }) => `${label} is not in the expected format`,
3073
+ oneOf: ({ label }) => `${label} is not an allowed value`,
3074
+ equals: ({ label }) => `${label} does not match`,
3075
+ validate: ({ label }) => `${label} is invalid`,
3076
+ schema: ({ label }) => `${label} is invalid`
2126
3077
  };
2127
3078
  var FALLBACK_LABEL = "This field";
2128
3079
  function resolveMessage(token, param, label, overrides) {
@@ -2136,6 +3087,50 @@ function resolveMessage(token, param, label, overrides) {
2136
3087
  // src/core/validation/rules.ts
2137
3088
  var isEmpty = (value) => value == null || value === "" || Array.isArray(value) && value.length === 0;
2138
3089
  var asLength = (value) => typeof value === "string" || Array.isArray(value) ? value.length : String(value ?? "").length;
3090
+ var toNumber = (value) => {
3091
+ if (typeof value === "number") return Number.isNaN(value) ? null : value;
3092
+ if (typeof value === "string" && value.trim() !== "") {
3093
+ const n = Number(value);
3094
+ return Number.isNaN(n) ? null : n;
3095
+ }
3096
+ return null;
3097
+ };
3098
+ var toTime = (value) => {
3099
+ if (value == null || value === "") return null;
3100
+ const d = value instanceof Date ? value : new Date(value);
3101
+ const t = d.getTime();
3102
+ return Number.isNaN(t) ? null : t;
3103
+ };
3104
+ var rangeSpanDays = (value) => {
3105
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
3106
+ const { start, end } = value;
3107
+ const s = toTime(start);
3108
+ const e = toTime(end);
3109
+ if (s == null || e == null) return null;
3110
+ return Math.abs(Math.round((e - s) / 864e5)) + 1;
3111
+ };
3112
+ var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
3113
+ var URL_RE = /^(https?:\/\/)[^\s.]+\.\S{2,}$/i;
3114
+ function toRegExp(param) {
3115
+ if (param instanceof RegExp) return param;
3116
+ if (typeof param === "string") {
3117
+ try {
3118
+ return new RegExp(param);
3119
+ } catch {
3120
+ return null;
3121
+ }
3122
+ }
3123
+ if (param && typeof param === "object" && "value" in param) {
3124
+ const { value, flags } = param;
3125
+ if (value instanceof RegExp) return value;
3126
+ try {
3127
+ return new RegExp(value, flags);
3128
+ } catch {
3129
+ return null;
3130
+ }
3131
+ }
3132
+ return null;
3133
+ }
2139
3134
  var builtInRules = {
2140
3135
  required: (value, param) => {
2141
3136
  if (param === false) return null;
@@ -2148,6 +3143,84 @@ var builtInRules = {
2148
3143
  maxLength: (value, param) => {
2149
3144
  if (typeof param !== "number" || isEmpty(value)) return null;
2150
3145
  return asLength(value) > param ? "maxLength" : null;
3146
+ },
3147
+ minItems: (value, param) => {
3148
+ if (typeof param !== "number" || !Array.isArray(value)) return null;
3149
+ return value.length < param ? "minItems" : null;
3150
+ },
3151
+ maxItems: (value, param) => {
3152
+ if (typeof param !== "number" || !Array.isArray(value)) return null;
3153
+ return value.length > param ? "maxItems" : null;
3154
+ },
3155
+ email: (value, param) => {
3156
+ if (param === false || isEmpty(value)) return null;
3157
+ return EMAIL_RE.test(String(value)) ? null : "email";
3158
+ },
3159
+ url: (value, param) => {
3160
+ if (param === false || isEmpty(value)) return null;
3161
+ return URL_RE.test(String(value)) ? null : "url";
3162
+ },
3163
+ pattern: (value, param) => {
3164
+ if (isEmpty(value)) return null;
3165
+ const re = toRegExp(param);
3166
+ if (!re) return null;
3167
+ return re.test(String(value)) ? null : "pattern";
3168
+ },
3169
+ integer: (value, param) => {
3170
+ if (param === false || isEmpty(value)) return null;
3171
+ const n = toNumber(value);
3172
+ return n != null && Number.isInteger(n) ? null : "integer";
3173
+ },
3174
+ min: (value, param) => {
3175
+ if (typeof param !== "number" || isEmpty(value)) return null;
3176
+ const n = toNumber(value);
3177
+ return n != null && n < param ? "min" : null;
3178
+ },
3179
+ max: (value, param) => {
3180
+ if (typeof param !== "number" || isEmpty(value)) return null;
3181
+ const n = toNumber(value);
3182
+ return n != null && n > param ? "max" : null;
3183
+ },
3184
+ minDate: (value, param) => {
3185
+ if (isEmpty(value)) return null;
3186
+ const v = toTime(value);
3187
+ const p = toTime(param);
3188
+ return v != null && p != null && v < p ? "minDate" : null;
3189
+ },
3190
+ maxDate: (value, param) => {
3191
+ if (isEmpty(value)) return null;
3192
+ const v = toTime(value);
3193
+ const p = toTime(param);
3194
+ return v != null && p != null && v > p ? "maxDate" : null;
3195
+ },
3196
+ minRangeDays: (value, param) => {
3197
+ if (typeof param !== "number") return null;
3198
+ const span = rangeSpanDays(value);
3199
+ return span != null && span < param ? "minRangeDays" : null;
3200
+ },
3201
+ maxRangeDays: (value, param) => {
3202
+ if (typeof param !== "number") return null;
3203
+ const span = rangeSpanDays(value);
3204
+ return span != null && span > param ? "maxRangeDays" : null;
3205
+ },
3206
+ oneOf: (value, param) => {
3207
+ if (isEmpty(value) || !Array.isArray(param)) return null;
3208
+ return param.includes(value) ? null : "oneOf";
3209
+ },
3210
+ equals: (value, param, allValues) => {
3211
+ if (isEmpty(value)) return null;
3212
+ const target = param && typeof param === "object" && "field" in param ? allValues[param.field] : param;
3213
+ return value == target ? null : "equals";
3214
+ },
3215
+ validate: (value, param, allValues) => {
3216
+ if (typeof param !== "function") return null;
3217
+ const out = param(value, allValues);
3218
+ if (out instanceof Promise) {
3219
+ return out.then(
3220
+ (r) => typeof r === "string" ? r : r === false ? "validate" : null
3221
+ );
3222
+ }
3223
+ return typeof out === "string" ? out : out === false ? "validate" : null;
2151
3224
  }
2152
3225
  };
2153
3226
  var registry = new Map(Object.entries(builtInRules));
@@ -2161,12 +3234,12 @@ function getRule(name) {
2161
3234
  // src/core/validation/resolver.ts
2162
3235
  function createRuleResolver() {
2163
3236
  return (values, config) => {
2164
- const result = {};
3237
+ const pending = [];
2165
3238
  for (const field of config.fields) {
2166
3239
  const validation = field.validation;
2167
3240
  if (!validation) continue;
3241
+ if (!evalCondition(field.when, values)) continue;
2168
3242
  const value = values[field.name];
2169
- const errors = [];
2170
3243
  for (const key of Object.keys(validation)) {
2171
3244
  if (key === "messages") continue;
2172
3245
  const param = validation[key];
@@ -2178,16 +3251,34 @@ function createRuleResolver() {
2178
3251
  }
2179
3252
  continue;
2180
3253
  }
2181
- const token = rule(value, param, values);
2182
- if (token == null) continue;
2183
- errors.push({
3254
+ pending.push({
3255
+ field: field.name,
2184
3256
  rule: key,
2185
- message: resolveMessage(token, param, field.label, validation.messages)
3257
+ param,
3258
+ label: field.label,
3259
+ overrides: validation.messages,
3260
+ token: rule(value, param, values)
2186
3261
  });
2187
3262
  }
2188
- if (errors.length > 0) result[field.name] = errors;
2189
3263
  }
2190
- return result;
3264
+ const collect = (settled) => {
3265
+ const result = {};
3266
+ pending.forEach((p, i) => {
3267
+ const token = settled[i];
3268
+ if (token == null) return;
3269
+ const bucket = result[p.field] ?? [];
3270
+ bucket.push({
3271
+ rule: p.rule,
3272
+ message: resolveMessage(token, p.param, p.label, p.overrides)
3273
+ });
3274
+ result[p.field] = bucket;
3275
+ });
3276
+ return result;
3277
+ };
3278
+ if (pending.some((p) => p.token instanceof Promise)) {
3279
+ return Promise.all(pending.map((p) => Promise.resolve(p.token))).then(collect);
3280
+ }
3281
+ return collect(pending.map((p) => p.token));
2191
3282
  };
2192
3283
  }
2193
3284
  var defaultResolver = /* @__PURE__ */ createRuleResolver();
@@ -2419,23 +3510,43 @@ function useFieldsetState(config, options = {}) {
2419
3510
  getFieldProps
2420
3511
  };
2421
3512
  }
2422
- function FieldGrid({ columns, gap, className, children }) {
3513
+ var useBrowserLayoutEffect = typeof window !== "undefined" ? react.useLayoutEffect : react.useEffect;
3514
+ var GridContext = react.createContext({ count: 1, tier: "desktop" });
3515
+ function FieldGrid({ columns, minColWidth, gap, className, children }) {
3516
+ const ref = react.useRef(null);
3517
+ const per = minColWidth && minColWidth > 0 ? minColWidth : DEFAULT_MIN_COL;
3518
+ const { desktop, tablet, mobile } = columns;
3519
+ const [grid, setGrid] = react.useState(() => resolveGrid(0, columns, per));
3520
+ useBrowserLayoutEffect(() => {
3521
+ const el = ref.current;
3522
+ if (!el) return;
3523
+ const cols = { desktop, tablet, mobile };
3524
+ const apply = (width) => {
3525
+ const next = resolveGrid(width, cols, per);
3526
+ setGrid((cur) => cur.count === next.count && cur.tier === next.tier ? cur : next);
3527
+ };
3528
+ apply(el.getBoundingClientRect().width);
3529
+ if (typeof ResizeObserver === "undefined") return;
3530
+ const ro = new ResizeObserver((entries) => {
3531
+ for (const entry of entries) {
3532
+ const box = entry.contentBoxSize?.[0];
3533
+ apply(box ? box.inlineSize : entry.contentRect.width);
3534
+ }
3535
+ });
3536
+ ro.observe(el);
3537
+ return () => ro.disconnect();
3538
+ }, [desktop, tablet, mobile, per]);
2423
3539
  const style = {
2424
- "--orynn-cols-desktop": columns.desktop,
2425
- "--orynn-cols-tablet": columns.tablet,
2426
- "--orynn-cols-mobile": columns.mobile,
3540
+ "--orynn-cols": grid.count,
2427
3541
  ...gap ? { "--orynn-grid-gap": gap } : {}
2428
3542
  };
2429
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cx("orynn-grid", className), style, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "orynn-grid__track", children }) });
3543
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cx("orynn-grid", className), style, children: /* @__PURE__ */ jsxRuntime.jsx(GridContext.Provider, { value: grid, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "orynn-grid__track", children }) }) });
2430
3544
  }
2431
- function FieldGridItem({ span, columns, children }) {
2432
- if (!span) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
2433
- const style = {
2434
- "--orynn-span-desktop": clampSpan(span.desktop, columns.desktop),
2435
- "--orynn-span-tablet": clampSpan(span.tablet, columns.tablet),
2436
- "--orynn-span-mobile": clampSpan(span.mobile, columns.mobile)
2437
- };
2438
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "orynn-grid__item", style, children });
3545
+ function FieldGridItem({ span, children }) {
3546
+ const { count, tier } = react.useContext(GridContext);
3547
+ const effective = span ? clampSpan(pickSpan(span, tier), count) : 1;
3548
+ if (effective <= 1) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
3549
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "orynn-grid__item", style: { "--orynn-span": effective }, children });
2439
3550
  }
2440
3551
  var noop = () => {
2441
3552
  };
@@ -2445,6 +3556,7 @@ function FieldRendererImpl({ field, registry: registry2, slice, disabled }) {
2445
3556
  registration,
2446
3557
  `No control registered for field type "${field.type}" (field "${field.name}").`
2447
3558
  );
3559
+ const fallbackId = react.useId();
2448
3560
  if (!registration) return null;
2449
3561
  const Control = registration.component;
2450
3562
  const required = field.validation?.required === true;
@@ -2454,6 +3566,36 @@ function FieldRendererImpl({ field, registry: registry2, slice, disabled }) {
2454
3566
  const handleChange = (next) => {
2455
3567
  slice.setValue(registration.formatValue ? registration.formatValue(next) : next);
2456
3568
  };
3569
+ if (registration.selfContained) {
3570
+ return /* @__PURE__ */ jsxRuntime.jsx(
3571
+ "div",
3572
+ {
3573
+ style: { display: "contents" },
3574
+ onBlur: (e) => {
3575
+ if (!e.currentTarget.contains(e.relatedTarget)) slice.markTouched();
3576
+ },
3577
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3578
+ Control,
3579
+ {
3580
+ id: fallbackId,
3581
+ name: field.name,
3582
+ value,
3583
+ onChange: handleChange,
3584
+ onBlur: slice.markTouched,
3585
+ onFocus: noop,
3586
+ disabled: isDisabled,
3587
+ readOnly: field.readOnly === true,
3588
+ required,
3589
+ invalid: messages.length > 0,
3590
+ label: field.label,
3591
+ description: field.description,
3592
+ errors: messages,
3593
+ config: field
3594
+ }
3595
+ )
3596
+ }
3597
+ );
3598
+ }
2457
3599
  return /* @__PURE__ */ jsxRuntime.jsx(
2458
3600
  Field,
2459
3601
  {
@@ -2486,90 +3628,290 @@ var FieldRenderer = /* @__PURE__ */ react.memo(
2486
3628
  FieldRendererImpl,
2487
3629
  (prev, next) => prev.field === next.field && prev.registry === next.registry && prev.disabled === next.disabled && prev.slice.value === next.slice.value && prev.slice.touched === next.slice.touched && prev.slice.errors === next.slice.errors && prev.slice.setValue === next.slice.setValue && prev.slice.markTouched === next.slice.markTouched
2488
3630
  );
2489
- var toStringValue = (value) => typeof value === "string" ? value : value == null ? "" : String(value);
2490
- var InputControl = ({
2491
- id,
2492
- name,
2493
- value,
2494
- onChange,
2495
- onBlur,
2496
- onFocus,
2497
- disabled,
2498
- readOnly,
2499
- required,
2500
- invalid,
2501
- describedById,
2502
- config
2503
- }) => {
2504
- const field = config;
3631
+ function FieldsetView({ state, registry: registry2, disabled, className, id }) {
3632
+ const activeRegistry = registry2 ?? defaultRegistry;
3633
+ const { columns, minColWidth, gap, legend, fields, id: configId } = state.config;
3634
+ const visible = fields.filter((f) => evalCondition(f.when, state.values));
3635
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3636
+ "fieldset",
3637
+ {
3638
+ className: cx("orynn-fieldset", className),
3639
+ id: id ?? configId,
3640
+ disabled: disabled || void 0,
3641
+ children: [
3642
+ legend != null && /* @__PURE__ */ jsxRuntime.jsx("legend", { className: "orynn-fieldset__legend", children: legend }),
3643
+ /* @__PURE__ */ jsxRuntime.jsx(FieldGrid, { columns, minColWidth, gap, children: visible.map((field) => /* @__PURE__ */ jsxRuntime.jsx(FieldGridItem, { span: field.layout, children: /* @__PURE__ */ jsxRuntime.jsx(
3644
+ FieldRenderer,
3645
+ {
3646
+ field,
3647
+ registry: activeRegistry,
3648
+ slice: state.getFieldProps(field.name),
3649
+ disabled: disabled === true
3650
+ }
3651
+ ) }, field.name)) })
3652
+ ]
3653
+ }
3654
+ );
3655
+ }
3656
+ var str = (v) => typeof v === "string" ? v : v == null ? "" : String(v);
3657
+ var toISO = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
3658
+ function shell(p) {
3659
+ const c = p.config;
3660
+ return {
3661
+ id: p.id,
3662
+ name: p.name,
3663
+ label: p.label,
3664
+ description: p.description,
3665
+ error: p.errors,
3666
+ required: p.required,
3667
+ disabled: p.disabled,
3668
+ readOnly: p.readOnly,
3669
+ size: c.size,
3670
+ variant: c.variant,
3671
+ floatingLabel: c.floatingLabel
3672
+ };
3673
+ }
3674
+ var InputControl = (p) => {
3675
+ const c = p.config;
2505
3676
  return /* @__PURE__ */ jsxRuntime.jsx(
2506
- "input",
3677
+ Input,
2507
3678
  {
2508
- id,
2509
- name,
2510
- className: "orynn-input",
2511
- type: field.inputType ?? "text",
2512
- value: toStringValue(value),
2513
- placeholder: field.placeholder,
2514
- required,
2515
- disabled,
2516
- readOnly,
2517
- "aria-invalid": invalid || void 0,
2518
- "aria-describedby": describedById,
2519
- onChange: (event) => onChange(event.target.value, event),
2520
- onBlur,
2521
- onFocus,
2522
- ...field.props
3679
+ ...shell(p),
3680
+ type: c.inputType ?? "text",
3681
+ prefix: c.prefix,
3682
+ suffix: c.suffix,
3683
+ addonBefore: c.addonBefore,
3684
+ addonAfter: c.addonAfter,
3685
+ clearable: c.clearable,
3686
+ maxLength: c.maxLength,
3687
+ showCount: c.showCount,
3688
+ debounce: c.debounce,
3689
+ inputMode: c.inputMode,
3690
+ pattern: c.pattern,
3691
+ placeholder: c.placeholder,
3692
+ value: str(p.value),
3693
+ onChange: (v) => p.onChange(v),
3694
+ ...c.props
2523
3695
  }
2524
3696
  );
2525
3697
  };
2526
- var DropdownControl = ({
2527
- id,
2528
- name,
2529
- value,
2530
- onChange,
2531
- onBlur,
2532
- onFocus,
2533
- disabled,
2534
- required,
2535
- invalid,
2536
- describedById,
2537
- config
2538
- }) => {
2539
- const field = config;
2540
- return /* @__PURE__ */ jsxRuntime.jsxs(
2541
- "select",
3698
+ var TextareaControl = (p) => {
3699
+ const c = p.config;
3700
+ return /* @__PURE__ */ jsxRuntime.jsx(
3701
+ Textarea,
2542
3702
  {
2543
- id,
2544
- name,
2545
- className: "orynn-dropdown",
2546
- value: toStringValue(value),
2547
- required,
2548
- disabled,
2549
- "aria-invalid": invalid || void 0,
2550
- "aria-describedby": describedById,
2551
- onChange: (event) => onChange(event.target.value, event),
2552
- onBlur,
2553
- onFocus,
2554
- ...field.props,
2555
- children: [
2556
- field.placeholder != null && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: required, children: field.placeholder }),
2557
- field.options.map((option) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value))
2558
- ]
3703
+ ...shell(p),
3704
+ minRows: c.rows,
3705
+ maxRows: c.maxRows,
3706
+ autoResize: c.autoResize,
3707
+ resize: c.resize,
3708
+ submitOnEnter: c.submitOnEnter,
3709
+ maxLength: c.maxLength,
3710
+ showCount: c.showCount,
3711
+ placeholder: c.placeholder,
3712
+ value: str(p.value),
3713
+ onChange: (v) => p.onChange(v),
3714
+ ...c.props
2559
3715
  }
2560
3716
  );
2561
3717
  };
2562
- function registerBuiltins(registry2) {
2563
- if (!registry2.has("input")) {
2564
- registry2.register("input", { component: InputControl, defaultValue: "" });
3718
+ var NumberFieldControl = (p) => {
3719
+ const c = p.config;
3720
+ return /* @__PURE__ */ jsxRuntime.jsx(
3721
+ NumberField,
3722
+ {
3723
+ ...shell(p),
3724
+ min: c.min,
3725
+ max: c.max,
3726
+ step: c.step,
3727
+ precision: c.precision,
3728
+ decimalScale: c.decimalScale,
3729
+ currency: c.currency,
3730
+ style: c.style,
3731
+ locale: c.locale,
3732
+ prefix: c.prefix,
3733
+ suffix: c.suffix,
3734
+ thousandSeparator: c.thousandSeparator,
3735
+ allowNegative: c.allowNegative,
3736
+ clampBehavior: c.clampBehavior,
3737
+ hideControls: c.hideControls,
3738
+ placeholder: c.placeholder,
3739
+ value: typeof p.value === "number" ? p.value : null,
3740
+ onChange: (v) => p.onChange(v),
3741
+ ...c.props
3742
+ }
3743
+ );
3744
+ };
3745
+ var DropdownControl = (p) => {
3746
+ const c = p.config;
3747
+ const value = c.multiple ? Array.isArray(p.value) ? p.value : [] : Array.isArray(p.value) ? p.value[0] ?? "" : str(p.value);
3748
+ return /* @__PURE__ */ jsxRuntime.jsx(
3749
+ Dropdown,
3750
+ {
3751
+ ...shell(p),
3752
+ options: c.options,
3753
+ searchable: c.searchable,
3754
+ multiple: c.multiple,
3755
+ clearable: c.clearable,
3756
+ creatable: c.creatable,
3757
+ maxValues: c.maxValues,
3758
+ hidePickedOptions: c.hidePickedOptions,
3759
+ showSelectAll: c.showSelectAll,
3760
+ searchDebounce: c.searchDebounce,
3761
+ placeholder: c.placeholder,
3762
+ value,
3763
+ onChange: (v) => p.onChange(v ?? (c.multiple ? [] : "")),
3764
+ ...c.props
3765
+ }
3766
+ );
3767
+ };
3768
+ var DateControl = (p) => {
3769
+ const c = p.config;
3770
+ const toDate2 = (x) => x == null ? void 0 : x instanceof Date ? x : new Date(x);
3771
+ const mode = c.mode ?? "single";
3772
+ const common = {
3773
+ ...shell(p),
3774
+ format: c.format,
3775
+ precision: c.precision,
3776
+ minDate: toDate2(c.minDate),
3777
+ maxDate: toDate2(c.maxDate),
3778
+ showToday: c.showToday,
3779
+ clearable: c.clearable,
3780
+ placeholder: c.placeholder,
3781
+ presets: c.presets,
3782
+ ...c.props
3783
+ };
3784
+ if (mode === "range") {
3785
+ const v = p.value ?? {};
3786
+ return /* @__PURE__ */ jsxRuntime.jsx(
3787
+ DatePicker,
3788
+ {
3789
+ ...common,
3790
+ mode: "range",
3791
+ value: { start: v.start ?? null, end: v.end ?? null },
3792
+ onChange: (r) => p.onChange({
3793
+ start: r.start ? toISO(r.start) : "",
3794
+ end: r.end ? toISO(r.end) : ""
3795
+ })
3796
+ }
3797
+ );
2565
3798
  }
2566
- if (!registry2.has("dropdown")) {
2567
- registry2.register("dropdown", {
2568
- component: DropdownControl,
2569
- defaultValue: "",
2570
- validateConfig: (config) => Array.isArray(config.options) ? null : "a dropdown field requires an `options` array"
2571
- });
3799
+ if (mode === "multiple") {
3800
+ return /* @__PURE__ */ jsxRuntime.jsx(
3801
+ DatePicker,
3802
+ {
3803
+ ...common,
3804
+ mode: "multiple",
3805
+ value: Array.isArray(p.value) ? p.value : [],
3806
+ onChange: (arr) => p.onChange(arr.map(toISO))
3807
+ }
3808
+ );
2572
3809
  }
3810
+ return /* @__PURE__ */ jsxRuntime.jsx(
3811
+ DatePicker,
3812
+ {
3813
+ ...common,
3814
+ value: typeof p.value === "string" && p.value ? p.value : null,
3815
+ onChange: (d) => p.onChange(d instanceof Date ? toISO(d) : "")
3816
+ }
3817
+ );
3818
+ };
3819
+ var CheckboxControl = (p) => {
3820
+ const c = p.config;
3821
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "orynn-field", children: [
3822
+ /* @__PURE__ */ jsxRuntime.jsx(
3823
+ Checkbox,
3824
+ {
3825
+ id: p.id,
3826
+ name: p.name,
3827
+ label: c.checkboxLabel ?? c.label,
3828
+ description: c.description,
3829
+ checked: Boolean(p.value),
3830
+ onChange: (v) => p.onChange(v),
3831
+ disabled: p.disabled,
3832
+ required: p.required,
3833
+ invalid: (p.errors?.length ?? 0) > 0,
3834
+ ...c.props
3835
+ }
3836
+ ),
3837
+ p.errors?.[0] && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "orynn-field__error", role: "alert", children: p.errors[0] })
3838
+ ] });
3839
+ };
3840
+ var CheckboxGroupControl = (p) => {
3841
+ const c = p.config;
3842
+ return /* @__PURE__ */ jsxRuntime.jsx(
3843
+ CheckboxGroup,
3844
+ {
3845
+ id: p.id,
3846
+ name: p.name,
3847
+ label: p.label,
3848
+ description: p.description,
3849
+ error: p.errors,
3850
+ required: p.required,
3851
+ disabled: p.disabled,
3852
+ options: c.options.map((o) => ({
3853
+ label: o.label,
3854
+ value: o.value,
3855
+ description: o.description,
3856
+ icon: o.icon,
3857
+ disabled: o.disabled
3858
+ })),
3859
+ orientation: c.orientation,
3860
+ columns: c.columns,
3861
+ showSelectAll: c.showSelectAll,
3862
+ min: c.min,
3863
+ max: c.max,
3864
+ value: Array.isArray(p.value) ? p.value : [],
3865
+ onChange: (v) => p.onChange(v),
3866
+ ...c.props
3867
+ }
3868
+ );
3869
+ };
3870
+ var RadioControl = (p) => {
3871
+ const c = p.config;
3872
+ return /* @__PURE__ */ jsxRuntime.jsx(
3873
+ RadioGroup,
3874
+ {
3875
+ id: p.id,
3876
+ name: p.name,
3877
+ label: p.label,
3878
+ description: p.description,
3879
+ error: p.errors,
3880
+ required: p.required,
3881
+ disabled: p.disabled,
3882
+ options: c.options.map((o) => ({
3883
+ label: o.label,
3884
+ value: o.value,
3885
+ description: o.description,
3886
+ icon: o.icon,
3887
+ disabled: o.disabled
3888
+ })),
3889
+ orientation: c.orientation,
3890
+ columns: c.columns,
3891
+ variant: c.radioVariant,
3892
+ value: typeof p.value === "string" && p.value ? p.value : null,
3893
+ onChange: (v) => p.onChange(v),
3894
+ ...c.props
3895
+ }
3896
+ );
3897
+ };
3898
+ var needsOptions = (config) => Array.isArray(config.options) ? null : "requires an `options` array";
3899
+ function registerBuiltins(registry2) {
3900
+ const add = (type, component, extra = {}) => {
3901
+ if (!registry2.has(type)) {
3902
+ registry2.register(type, { component, selfContained: true, defaultValue: "", ...extra });
3903
+ }
3904
+ };
3905
+ add("input", InputControl);
3906
+ add("textarea", TextareaControl);
3907
+ add("number", NumberFieldControl, {
3908
+ parseValue: (raw) => typeof raw === "number" ? raw : raw === "" || raw == null ? null : Number(raw)
3909
+ });
3910
+ add("dropdown", DropdownControl, { validateConfig: needsOptions });
3911
+ add("date", DateControl);
3912
+ add("checkbox", CheckboxControl, { defaultValue: false });
3913
+ add("checkbox-group", CheckboxGroupControl, { defaultValue: [], validateConfig: needsOptions });
3914
+ add("radio", RadioControl, { validateConfig: needsOptions });
2573
3915
  }
2574
3916
  var seeded = false;
2575
3917
  function ensureBuiltins(defaultRegistry2) {
@@ -2603,110 +3945,938 @@ function Fieldset(props) {
2603
3945
  ...validateOn ? { validateOn } : {}
2604
3946
  };
2605
3947
  const fs = useFieldsetState(config, options);
2606
- const { columns, gap, legend, fields, id: configId } = fs.config;
2607
- return /* @__PURE__ */ jsxRuntime.jsxs(
2608
- "fieldset",
3948
+ return /* @__PURE__ */ jsxRuntime.jsx(
3949
+ FieldsetView,
2609
3950
  {
2610
- className: cx("orynn-fieldset", className),
2611
- id: id ?? configId,
2612
- disabled: disabled || void 0,
2613
- children: [
2614
- legend != null && /* @__PURE__ */ jsxRuntime.jsx("legend", { className: "orynn-fieldset__legend", children: legend }),
2615
- /* @__PURE__ */ jsxRuntime.jsx(FieldGrid, { columns, gap, children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsx(FieldGridItem, { span: field.layout, columns, children: /* @__PURE__ */ jsxRuntime.jsx(
2616
- FieldRenderer,
2617
- {
2618
- field,
2619
- registry: activeRegistry,
2620
- slice: fs.getFieldProps(field.name),
2621
- disabled: disabled === true
2622
- }
2623
- ) }, field.name)) })
2624
- ]
3951
+ state: fs,
3952
+ registry: activeRegistry,
3953
+ disabled: disabled === true,
3954
+ className,
3955
+ id
2625
3956
  }
2626
3957
  );
2627
3958
  }
2628
-
2629
- // src/theme/theme.ts
2630
- var RADIUS_KEYWORDS = /* @__PURE__ */ new Set(["none", "sm", "md", "lg", "pill"]);
2631
- var len = (v) => typeof v === "number" ? `${v}px` : v;
2632
- function contrastFor(color) {
2633
- const m = /^#?([\da-f]{3}|[\da-f]{6})$/i.exec(color.trim());
2634
- if (!m?.[1]) return "#ffffff";
2635
- const hex = m[1].length === 3 ? m[1].replace(/./g, "$&$&") : m[1];
2636
- const n = Number.parseInt(hex, 16);
2637
- const toLin = (v) => {
2638
- const c = v / 255;
2639
- return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
2640
- };
2641
- const lum = 0.2126 * toLin(n >> 16 & 255) + 0.7152 * toLin(n >> 8 & 255) + 0.0722 * toLin(n & 255);
2642
- return lum > 0.42 ? "#0b0d12" : "#ffffff";
2643
- }
2644
- function normalizeTheme(input) {
2645
- return typeof input === "string" ? { preset: input } : input ?? {};
2646
- }
2647
- function resolveThemeVars(config) {
2648
- const vars = {};
2649
- if (config.accent) {
2650
- vars["--orynn-color-accent"] = config.accent;
2651
- vars["--orynn-color-accent-hover"] = `color-mix(in srgb, ${config.accent} 86%, #000)`;
2652
- vars["--orynn-color-accent-contrast"] = config.accentContrast ?? contrastFor(config.accent);
2653
- }
2654
- if (config.ring) vars["--orynn-color-focus-ring"] = config.ring;
2655
- if (config.font) vars["--orynn-font-family"] = config.font;
2656
- if (config.fontSize != null) vars["--orynn-font-size"] = len(config.fontSize);
2657
- if (config.controlWidth != null) vars["--orynn-control-width"] = len(config.controlWidth);
2658
- if (config.radius != null) {
2659
- const r = config.radius;
2660
- if (r === "md") vars["--orynn-radius"] = "10px";
2661
- else if (typeof r === "string" && RADIUS_KEYWORDS.has(r))
2662
- vars["--orynn-radius"] = `var(--orynn-radius-${r})`;
2663
- else vars["--orynn-radius"] = len(r);
2664
- }
2665
- Object.assign(vars, config.vars ?? {});
2666
- return vars;
3959
+ function Form(props) {
3960
+ const {
3961
+ config,
3962
+ value,
3963
+ defaultValue,
3964
+ onChange,
3965
+ onSubmit,
3966
+ onInvalid,
3967
+ resolver,
3968
+ validateOn,
3969
+ disabled,
3970
+ children,
3971
+ className,
3972
+ ...rest
3973
+ } = props;
3974
+ ensureBuiltins(defaultRegistry);
3975
+ const fs = useFieldsetState(config, {
3976
+ registry: defaultRegistry,
3977
+ ...value !== void 0 ? { value } : {},
3978
+ ...defaultValue !== void 0 ? { defaultValue } : {},
3979
+ ...onChange ? { onChange } : {},
3980
+ ...resolver ? { resolver } : {},
3981
+ ...validateOn ? { validateOn } : {}
3982
+ });
3983
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3984
+ "form",
3985
+ {
3986
+ ...rest,
3987
+ className: cx("orynn-form", className),
3988
+ noValidate: true,
3989
+ onSubmit: fs.handleSubmit(
3990
+ (values) => onSubmit(values, fs),
3991
+ (errors) => onInvalid?.(errors)
3992
+ ),
3993
+ children: [
3994
+ /* @__PURE__ */ jsxRuntime.jsx(FieldsetView, { state: fs, disabled }),
3995
+ typeof children === "function" ? children(fs) : children
3996
+ ]
3997
+ }
3998
+ );
2667
3999
  }
2668
- var OrynnContext = react.createContext({
2669
- preset: "default",
2670
- density: "comfortable",
2671
- config: {}
4000
+
4001
+ // src/primitives/shared/classes.ts
4002
+ var mod = (base2, key, fallback) => key === fallback ? void 0 : `${base2}--${key}`;
4003
+ function buttonClasses(variant = "default", size = "default") {
4004
+ return cx(
4005
+ "orynn-button",
4006
+ mod("orynn-button", variant, "default"),
4007
+ mod("orynn-button", size, "default")
4008
+ );
4009
+ }
4010
+ function badgeClasses(variant = "default") {
4011
+ return cx("orynn-badge", mod("orynn-badge", variant, "default"));
4012
+ }
4013
+ var Button = /* @__PURE__ */ react.forwardRef(function Button2({
4014
+ className,
4015
+ variant = "default",
4016
+ size = "default",
4017
+ asChild = false,
4018
+ type,
4019
+ loading = false,
4020
+ loadingText,
4021
+ leftIcon,
4022
+ rightIcon,
4023
+ fullWidth = false,
4024
+ disabled,
4025
+ children,
4026
+ ...props
4027
+ }, ref) {
4028
+ const shared = {
4029
+ "data-slot": "button",
4030
+ "data-variant": variant,
4031
+ "data-size": size,
4032
+ "data-loading": loading || void 0,
4033
+ "data-full-width": fullWidth || void 0,
4034
+ className: cx(buttonClasses(variant, size), className),
4035
+ ...props
4036
+ };
4037
+ if (asChild) {
4038
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Slot.Root, { ref, "aria-busy": loading || void 0, ...shared, children });
4039
+ }
4040
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4041
+ "button",
4042
+ {
4043
+ ref,
4044
+ type: type ?? "button",
4045
+ disabled: disabled || loading,
4046
+ "aria-busy": loading || void 0,
4047
+ ...shared,
4048
+ children: [
4049
+ loading ? /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, { className: "orynn-button__spinner" }) : leftIcon,
4050
+ loading && loadingText != null ? loadingText : children,
4051
+ !loading && rightIcon
4052
+ ]
4053
+ }
4054
+ );
2672
4055
  });
2673
- function OrynnProvider({ theme, as = "div", className, children }) {
2674
- const config = react.useMemo(() => normalizeTheme(theme), [theme]);
2675
- const preset = config.preset ?? "default";
2676
- const density = config.density ?? "comfortable";
2677
- const style = react.useMemo(() => resolveThemeVars(config), [config]);
2678
- const ctx = react.useMemo(
2679
- () => ({ preset, density, config }),
2680
- [preset, density, config]
4056
+ var Label = /* @__PURE__ */ react.forwardRef(function Label2({ className, required, optional, children, ...props }, ref) {
4057
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4058
+ radixUi.Label.Root,
4059
+ {
4060
+ ref,
4061
+ "data-slot": "label",
4062
+ className: cx("orynn-label", className),
4063
+ ...props,
4064
+ children: [
4065
+ children,
4066
+ required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-label__required", "aria-hidden": "true", children: "*" }),
4067
+ !required && optional && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-label__optional", children: optional === true ? "(optional)" : optional })
4068
+ ]
4069
+ }
2681
4070
  );
2682
- const dataProps = {
2683
- "data-orynn-theme": preset === "default" ? void 0 : preset,
2684
- "data-orynn-density": density === "comfortable" ? void 0 : density
4071
+ });
4072
+ var Card = /* @__PURE__ */ react.forwardRef(function Card2({ className, asChild = false, interactive = false, ...props }, ref) {
4073
+ const shared = {
4074
+ "data-slot": "card",
4075
+ "data-interactive": interactive || void 0,
4076
+ className: cx("orynn-card", className),
4077
+ ...props
4078
+ };
4079
+ return asChild ? /* @__PURE__ */ jsxRuntime.jsx(radixUi.Slot.Root, { ref, ...shared }) : /* @__PURE__ */ jsxRuntime.jsx("div", { ref, ...shared });
4080
+ });
4081
+ var CardHeader = /* @__PURE__ */ react.forwardRef(function CardHeader2({ className, ...props }, ref) {
4082
+ return /* @__PURE__ */ jsxRuntime.jsx(
4083
+ "div",
4084
+ {
4085
+ ref,
4086
+ "data-slot": "card-header",
4087
+ className: cx("orynn-card__header", className),
4088
+ ...props
4089
+ }
4090
+ );
4091
+ });
4092
+ var CardTitle = /* @__PURE__ */ react.forwardRef(function CardTitle2({ className, ...props }, ref) {
4093
+ return /* @__PURE__ */ jsxRuntime.jsx(
4094
+ "div",
4095
+ {
4096
+ ref,
4097
+ "data-slot": "card-title",
4098
+ className: cx("orynn-card__title", className),
4099
+ ...props
4100
+ }
4101
+ );
4102
+ });
4103
+ var CardDescription = /* @__PURE__ */ react.forwardRef(
4104
+ function CardDescription2({ className, ...props }, ref) {
4105
+ return /* @__PURE__ */ jsxRuntime.jsx(
4106
+ "div",
4107
+ {
4108
+ ref,
4109
+ "data-slot": "card-description",
4110
+ className: cx("orynn-card__description", className),
4111
+ ...props
4112
+ }
4113
+ );
4114
+ }
4115
+ );
4116
+ var CardAction = /* @__PURE__ */ react.forwardRef(function CardAction2({ className, ...props }, ref) {
4117
+ return /* @__PURE__ */ jsxRuntime.jsx(
4118
+ "div",
4119
+ {
4120
+ ref,
4121
+ "data-slot": "card-action",
4122
+ className: cx("orynn-card__action", className),
4123
+ ...props
4124
+ }
4125
+ );
4126
+ });
4127
+ var CardContent = /* @__PURE__ */ react.forwardRef(
4128
+ function CardContent2({ className, ...props }, ref) {
4129
+ return /* @__PURE__ */ jsxRuntime.jsx(
4130
+ "div",
4131
+ {
4132
+ ref,
4133
+ "data-slot": "card-content",
4134
+ className: cx("orynn-card__content", className),
4135
+ ...props
4136
+ }
4137
+ );
4138
+ }
4139
+ );
4140
+ var CardFooter = /* @__PURE__ */ react.forwardRef(function CardFooter2({ className, ...props }, ref) {
4141
+ return /* @__PURE__ */ jsxRuntime.jsx(
4142
+ "div",
4143
+ {
4144
+ ref,
4145
+ "data-slot": "card-footer",
4146
+ className: cx("orynn-card__footer", className),
4147
+ ...props
4148
+ }
4149
+ );
4150
+ });
4151
+ var Badge = /* @__PURE__ */ react.forwardRef(function Badge2({
4152
+ className,
4153
+ variant = "default",
4154
+ size = "md",
4155
+ dot = false,
4156
+ icon,
4157
+ removable = false,
4158
+ onRemove,
4159
+ asChild = false,
4160
+ children,
4161
+ ...props
4162
+ }, ref) {
4163
+ const shared = {
4164
+ "data-slot": "badge",
4165
+ "data-variant": variant,
4166
+ "data-size": size,
4167
+ className: cx(badgeClasses(variant), className),
4168
+ ...props
2685
4169
  };
2686
- return /* @__PURE__ */ jsxRuntime.jsx(OrynnContext.Provider, { value: ctx, children: as === "contents" ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "contents" }, ...dataProps, children }) : as === "span" ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cx("orynn-root", className), style, ...dataProps, children }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: cx("orynn-root", className), style, ...dataProps, children }) });
4170
+ if (asChild) {
4171
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Slot.Root, { ref, ...shared, children });
4172
+ }
4173
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { ref, ...shared, children: [
4174
+ dot && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-badge__dot", "aria-hidden": "true" }),
4175
+ icon,
4176
+ children,
4177
+ removable && /* @__PURE__ */ jsxRuntime.jsx(
4178
+ "button",
4179
+ {
4180
+ type: "button",
4181
+ className: "orynn-badge__remove",
4182
+ "aria-label": "Remove",
4183
+ onClick: onRemove,
4184
+ children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, {})
4185
+ }
4186
+ )
4187
+ ] });
4188
+ });
4189
+ var Alert = /* @__PURE__ */ react.forwardRef(function Alert2({ className, variant = "default", icon, title, dismissible, onDismiss, children, ...props }, ref) {
4190
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4191
+ "div",
4192
+ {
4193
+ ref,
4194
+ "data-slot": "alert",
4195
+ "data-variant": variant,
4196
+ "data-dismissible": dismissible || void 0,
4197
+ role: "alert",
4198
+ className: cx("orynn-alert", variant !== "default" && `orynn-alert--${variant}`, className),
4199
+ ...props,
4200
+ children: [
4201
+ icon,
4202
+ title != null && /* @__PURE__ */ jsxRuntime.jsx(AlertTitle, { children: title }),
4203
+ children,
4204
+ dismissible && /* @__PURE__ */ jsxRuntime.jsx(
4205
+ "button",
4206
+ {
4207
+ type: "button",
4208
+ className: "orynn-alert__dismiss",
4209
+ "aria-label": "Dismiss",
4210
+ onClick: onDismiss,
4211
+ children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, {})
4212
+ }
4213
+ )
4214
+ ]
4215
+ }
4216
+ );
4217
+ });
4218
+ var AlertTitle = /* @__PURE__ */ react.forwardRef(
4219
+ function AlertTitle2({ className, ...props }, ref) {
4220
+ return /* @__PURE__ */ jsxRuntime.jsx(
4221
+ "div",
4222
+ {
4223
+ ref,
4224
+ "data-slot": "alert-title",
4225
+ className: cx("orynn-alert__title", className),
4226
+ ...props
4227
+ }
4228
+ );
4229
+ }
4230
+ );
4231
+ var AlertDescription = /* @__PURE__ */ react.forwardRef(
4232
+ function AlertDescription2({ className, ...props }, ref) {
4233
+ return /* @__PURE__ */ jsxRuntime.jsx(
4234
+ "div",
4235
+ {
4236
+ ref,
4237
+ "data-slot": "alert-description",
4238
+ className: cx("orynn-alert__description", className),
4239
+ ...props
4240
+ }
4241
+ );
4242
+ }
4243
+ );
4244
+ var Separator = /* @__PURE__ */ react.forwardRef(
4245
+ function Separator2({
4246
+ className,
4247
+ orientation = "horizontal",
4248
+ decorative = true,
4249
+ variant = "solid",
4250
+ children,
4251
+ ...props
4252
+ }, ref) {
4253
+ if (children != null && orientation === "horizontal") {
4254
+ return /* @__PURE__ */ jsxRuntime.jsx(
4255
+ "div",
4256
+ {
4257
+ ref,
4258
+ role: "separator",
4259
+ "aria-orientation": "horizontal",
4260
+ "data-slot": "separator",
4261
+ "data-orientation": "horizontal",
4262
+ "data-variant": variant,
4263
+ className: cx("orynn-separator", "orynn-separator--labelled", className),
4264
+ ...props,
4265
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-separator__label", children })
4266
+ }
4267
+ );
4268
+ }
4269
+ return /* @__PURE__ */ jsxRuntime.jsx(
4270
+ radixUi.Separator.Root,
4271
+ {
4272
+ ref,
4273
+ "data-slot": "separator",
4274
+ "data-variant": variant,
4275
+ orientation,
4276
+ decorative,
4277
+ className: cx("orynn-separator", className),
4278
+ ...props
4279
+ }
4280
+ );
4281
+ }
4282
+ );
4283
+ var Table = /* @__PURE__ */ react.forwardRef(function Table2({ className, size, dense, ...props }, ref) {
4284
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot": "table-container", className: "orynn-table__wrap", children: /* @__PURE__ */ jsxRuntime.jsx(
4285
+ "table",
4286
+ {
4287
+ ref,
4288
+ "data-slot": "table",
4289
+ "data-size": dense ? "sm" : size,
4290
+ className: cx("orynn-table", className),
4291
+ ...props
4292
+ }
4293
+ ) });
4294
+ });
4295
+ var TableHeader = /* @__PURE__ */ react.forwardRef(
4296
+ function TableHeader2({ className, sticky, ...props }, ref) {
4297
+ return /* @__PURE__ */ jsxRuntime.jsx(
4298
+ "thead",
4299
+ {
4300
+ ref,
4301
+ "data-slot": "table-header",
4302
+ "data-sticky": sticky || void 0,
4303
+ className: cx("orynn-table__header", className),
4304
+ ...props
4305
+ }
4306
+ );
4307
+ }
4308
+ );
4309
+ var TableBody = /* @__PURE__ */ react.forwardRef(function TableBody2({ className, ...props }, ref) {
4310
+ return /* @__PURE__ */ jsxRuntime.jsx(
4311
+ "tbody",
4312
+ {
4313
+ ref,
4314
+ "data-slot": "table-body",
4315
+ className: cx("orynn-table__body", className),
4316
+ ...props
4317
+ }
4318
+ );
4319
+ });
4320
+ var TableFooter = /* @__PURE__ */ react.forwardRef(function TableFooter2({ className, ...props }, ref) {
4321
+ return /* @__PURE__ */ jsxRuntime.jsx(
4322
+ "tfoot",
4323
+ {
4324
+ ref,
4325
+ "data-slot": "table-footer",
4326
+ className: cx("orynn-table__footer", className),
4327
+ ...props
4328
+ }
4329
+ );
4330
+ });
4331
+ var TableRow = /* @__PURE__ */ react.forwardRef(
4332
+ function TableRow2({ className, selected, ...props }, ref) {
4333
+ return /* @__PURE__ */ jsxRuntime.jsx(
4334
+ "tr",
4335
+ {
4336
+ ref,
4337
+ "data-slot": "table-row",
4338
+ "data-state": selected ? "selected" : void 0,
4339
+ className: cx("orynn-table__row", className),
4340
+ ...props
4341
+ }
4342
+ );
4343
+ }
4344
+ );
4345
+ var TableHead = /* @__PURE__ */ react.forwardRef(
4346
+ function TableHead2({ className, ...props }, ref) {
4347
+ return /* @__PURE__ */ jsxRuntime.jsx(
4348
+ "th",
4349
+ {
4350
+ ref,
4351
+ "data-slot": "table-head",
4352
+ className: cx("orynn-table__head", className),
4353
+ ...props
4354
+ }
4355
+ );
4356
+ }
4357
+ );
4358
+ var TableCell = /* @__PURE__ */ react.forwardRef(
4359
+ function TableCell2({ className, ...props }, ref) {
4360
+ return /* @__PURE__ */ jsxRuntime.jsx(
4361
+ "td",
4362
+ {
4363
+ ref,
4364
+ "data-slot": "table-cell",
4365
+ className: cx("orynn-table__cell", className),
4366
+ ...props
4367
+ }
4368
+ );
4369
+ }
4370
+ );
4371
+ var TableCaption = /* @__PURE__ */ react.forwardRef(function TableCaption2({ className, ...props }, ref) {
4372
+ return /* @__PURE__ */ jsxRuntime.jsx(
4373
+ "caption",
4374
+ {
4375
+ ref,
4376
+ "data-slot": "table-caption",
4377
+ className: cx("orynn-table__caption", className),
4378
+ ...props
4379
+ }
4380
+ );
4381
+ });
4382
+ var Switch = /* @__PURE__ */ react.forwardRef(function Switch2({
4383
+ className,
4384
+ size = "default",
4385
+ label,
4386
+ description,
4387
+ labelPlacement = "end",
4388
+ onLabel,
4389
+ offLabel,
4390
+ thumbIcon,
4391
+ id: idProp,
4392
+ ...props
4393
+ }, ref) {
4394
+ const reactId = react.useId();
4395
+ const id = idProp ?? reactId;
4396
+ const control = /* @__PURE__ */ jsxRuntime.jsxs(
4397
+ radixUi.Switch.Root,
4398
+ {
4399
+ ref,
4400
+ id,
4401
+ "data-slot": "switch",
4402
+ "data-size": size,
4403
+ className: cx("orynn-switch", className),
4404
+ ...props,
4405
+ children: [
4406
+ offLabel != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-switch__text orynn-switch__text--off", "aria-hidden": "true", children: offLabel }),
4407
+ onLabel != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-switch__text orynn-switch__text--on", "aria-hidden": "true", children: onLabel }),
4408
+ /* @__PURE__ */ jsxRuntime.jsx(radixUi.Switch.Thumb, { "data-slot": "switch-thumb", className: "orynn-switch__thumb", children: thumbIcon })
4409
+ ]
4410
+ }
4411
+ );
4412
+ if (label == null && description == null) return control;
4413
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4414
+ "label",
4415
+ {
4416
+ className: "orynn-switch-field",
4417
+ "data-slot": "switch-field",
4418
+ "data-placement": labelPlacement,
4419
+ htmlFor: id,
4420
+ children: [
4421
+ control,
4422
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "orynn-switch-field__text", children: [
4423
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-switch-field__label", children: label }),
4424
+ description != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-switch-field__description", children: description })
4425
+ ] })
4426
+ ]
4427
+ }
4428
+ );
4429
+ });
4430
+ var Tabs = /* @__PURE__ */ react.forwardRef(function Tabs2({ className, variant = "pill", size = "md", fitted = false, ...props }, ref) {
4431
+ return /* @__PURE__ */ jsxRuntime.jsx(
4432
+ radixUi.Tabs.Root,
4433
+ {
4434
+ ref,
4435
+ "data-slot": "tabs",
4436
+ "data-variant": variant,
4437
+ "data-size": size,
4438
+ "data-fitted": fitted || void 0,
4439
+ className: cx("orynn-tabs", className),
4440
+ ...props
4441
+ }
4442
+ );
4443
+ });
4444
+ var TabsList = /* @__PURE__ */ react.forwardRef(function TabsList2({ className, ...props }, ref) {
4445
+ return /* @__PURE__ */ jsxRuntime.jsx(
4446
+ radixUi.Tabs.List,
4447
+ {
4448
+ ref,
4449
+ "data-slot": "tabs-list",
4450
+ className: cx("orynn-tabs__list", className),
4451
+ ...props
4452
+ }
4453
+ );
4454
+ });
4455
+ var TabsTrigger = /* @__PURE__ */ react.forwardRef(function TabsTrigger2({ className, ...props }, ref) {
4456
+ return /* @__PURE__ */ jsxRuntime.jsx(
4457
+ radixUi.Tabs.Trigger,
4458
+ {
4459
+ ref,
4460
+ "data-slot": "tabs-trigger",
4461
+ className: cx("orynn-tabs__trigger", className),
4462
+ ...props
4463
+ }
4464
+ );
4465
+ });
4466
+ var TabsContent = /* @__PURE__ */ react.forwardRef(function TabsContent2({ className, ...props }, ref) {
4467
+ return /* @__PURE__ */ jsxRuntime.jsx(
4468
+ radixUi.Tabs.Content,
4469
+ {
4470
+ ref,
4471
+ "data-slot": "tabs-content",
4472
+ className: cx("orynn-tabs__content", className),
4473
+ ...props
4474
+ }
4475
+ );
4476
+ });
4477
+ var Dialog = (props) => /* @__PURE__ */ jsxRuntime.jsx(radixUi.Dialog.Root, { "data-slot": "dialog", ...props });
4478
+ var DialogTrigger = (props) => /* @__PURE__ */ jsxRuntime.jsx(radixUi.Dialog.Trigger, { "data-slot": "dialog-trigger", ...props });
4479
+ var DialogClose = (props) => /* @__PURE__ */ jsxRuntime.jsx(radixUi.Dialog.Close, { "data-slot": "dialog-close", ...props });
4480
+ var DialogPortal = radixUi.Dialog.Portal;
4481
+ var DialogContent = /* @__PURE__ */ react.forwardRef(
4482
+ function DialogContent2({ className, children, showCloseButton = true, size = "lg", scrollable, container, ...props }, ref) {
4483
+ const themed = usePortalContainer();
4484
+ return /* @__PURE__ */ jsxRuntime.jsxs(radixUi.Dialog.Portal, { container: container ?? themed ?? void 0, children: [
4485
+ /* @__PURE__ */ jsxRuntime.jsx(
4486
+ radixUi.Dialog.Overlay,
4487
+ {
4488
+ "data-slot": "dialog-overlay",
4489
+ className: "orynn-overlay orynn-dialog__overlay"
4490
+ }
4491
+ ),
4492
+ /* @__PURE__ */ jsxRuntime.jsxs(
4493
+ radixUi.Dialog.Content,
4494
+ {
4495
+ ref,
4496
+ "data-slot": "dialog-content",
4497
+ "data-size": size,
4498
+ "data-scrollable": scrollable || void 0,
4499
+ className: cx("orynn-dialog__content", className),
4500
+ ...props,
4501
+ children: [
4502
+ children,
4503
+ showCloseButton && /* @__PURE__ */ jsxRuntime.jsx(
4504
+ radixUi.Dialog.Close,
4505
+ {
4506
+ "data-slot": "dialog-close",
4507
+ className: "orynn-dialog__close",
4508
+ "aria-label": "Close",
4509
+ children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, {})
4510
+ }
4511
+ )
4512
+ ]
4513
+ }
4514
+ )
4515
+ ] });
4516
+ }
4517
+ );
4518
+ function DialogHeader({ className, ...props }) {
4519
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot": "dialog-header", className: cx("orynn-dialog__header", className), ...props });
2687
4520
  }
2688
- function useOrynnTheme() {
2689
- return react.useContext(OrynnContext);
4521
+ function DialogFooter({ className, ...props }) {
4522
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot": "dialog-footer", className: cx("orynn-dialog__footer", className), ...props });
4523
+ }
4524
+ var DialogTitle = /* @__PURE__ */ react.forwardRef(function DialogTitle2({ className, ...props }, ref) {
4525
+ return /* @__PURE__ */ jsxRuntime.jsx(
4526
+ radixUi.Dialog.Title,
4527
+ {
4528
+ ref,
4529
+ "data-slot": "dialog-title",
4530
+ className: cx("orynn-dialog__title", className),
4531
+ ...props
4532
+ }
4533
+ );
4534
+ });
4535
+ var DialogDescription = /* @__PURE__ */ react.forwardRef(function DialogDescription2({ className, ...props }, ref) {
4536
+ return /* @__PURE__ */ jsxRuntime.jsx(
4537
+ radixUi.Dialog.Description,
4538
+ {
4539
+ ref,
4540
+ "data-slot": "dialog-description",
4541
+ className: cx("orynn-dialog__description", className),
4542
+ ...props
4543
+ }
4544
+ );
4545
+ });
4546
+ var Popover2 = (props) => /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Root, { "data-slot": "popover", ...props });
4547
+ var PopoverTrigger = (props) => /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Trigger, { "data-slot": "popover-trigger", ...props });
4548
+ var PopoverAnchor = (props) => /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Anchor, { "data-slot": "popover-anchor", ...props });
4549
+ var PopoverClose = radixUi.Popover.Close;
4550
+ var PopoverArrow = (props) => /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Arrow, { "data-slot": "popover-arrow", className: "orynn-popover__arrow", ...props });
4551
+ var PopoverContent = /* @__PURE__ */ react.forwardRef(
4552
+ function PopoverContent2({ className, align = "center", sideOffset = 4, arrow = false, container, children, ...props }, ref) {
4553
+ const themed = usePortalContainer();
4554
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Portal, { container: container ?? themed ?? void 0, children: /* @__PURE__ */ jsxRuntime.jsxs(
4555
+ radixUi.Popover.Content,
4556
+ {
4557
+ ref,
4558
+ "data-slot": "popover-content",
4559
+ align,
4560
+ sideOffset,
4561
+ className: cx("orynn-content orynn-popover__content", className),
4562
+ ...props,
4563
+ children: [
4564
+ children,
4565
+ arrow && /* @__PURE__ */ jsxRuntime.jsx(PopoverArrow, {})
4566
+ ]
4567
+ }
4568
+ ) });
4569
+ }
4570
+ );
4571
+ function TooltipProvider({
4572
+ delayDuration = 0,
4573
+ ...props
4574
+ }) {
4575
+ return /* @__PURE__ */ jsxRuntime.jsx(
4576
+ radixUi.Tooltip.Provider,
4577
+ {
4578
+ "data-slot": "tooltip-provider",
4579
+ delayDuration,
4580
+ ...props
4581
+ }
4582
+ );
4583
+ }
4584
+ function Tooltip({
4585
+ delayDuration = 0,
4586
+ skipDelayDuration,
4587
+ disableHoverableContent,
4588
+ disableProvider = false,
4589
+ ...props
4590
+ }) {
4591
+ const root = /* @__PURE__ */ jsxRuntime.jsx(
4592
+ radixUi.Tooltip.Root,
4593
+ {
4594
+ "data-slot": "tooltip",
4595
+ delayDuration,
4596
+ disableHoverableContent,
4597
+ ...props
4598
+ }
4599
+ );
4600
+ if (disableProvider) return root;
4601
+ return /* @__PURE__ */ jsxRuntime.jsx(
4602
+ TooltipProvider,
4603
+ {
4604
+ delayDuration,
4605
+ skipDelayDuration,
4606
+ disableHoverableContent,
4607
+ children: root
4608
+ }
4609
+ );
4610
+ }
4611
+ var TooltipTrigger = (props) => /* @__PURE__ */ jsxRuntime.jsx(radixUi.Tooltip.Trigger, { "data-slot": "tooltip-trigger", ...props });
4612
+ var TooltipContent = /* @__PURE__ */ react.forwardRef(
4613
+ function TooltipContent2({ className, sideOffset = 0, arrow = true, arrowSize = 11, container, children, ...props }, ref) {
4614
+ const themed = usePortalContainer();
4615
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Tooltip.Portal, { container: container ?? themed ?? void 0, children: /* @__PURE__ */ jsxRuntime.jsxs(
4616
+ radixUi.Tooltip.Content,
4617
+ {
4618
+ ref,
4619
+ "data-slot": "tooltip-content",
4620
+ sideOffset,
4621
+ className: cx("orynn-content orynn-tooltip__content", className),
4622
+ ...props,
4623
+ children: [
4624
+ children,
4625
+ arrow && /* @__PURE__ */ jsxRuntime.jsx(
4626
+ radixUi.Tooltip.Arrow,
4627
+ {
4628
+ width: arrowSize,
4629
+ height: Math.round(arrowSize / 2.2),
4630
+ className: "orynn-tooltip__arrow"
4631
+ }
4632
+ )
4633
+ ]
4634
+ }
4635
+ ) });
4636
+ }
4637
+ );
4638
+ var DropdownMenu = (props) => /* @__PURE__ */ jsxRuntime.jsx(radixUi.DropdownMenu.Root, { "data-slot": "dropdown-menu", ...props });
4639
+ var DropdownMenuTrigger = (props) => /* @__PURE__ */ jsxRuntime.jsx(radixUi.DropdownMenu.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
4640
+ var DropdownMenuGroup = radixUi.DropdownMenu.Group;
4641
+ var DropdownMenuPortal = radixUi.DropdownMenu.Portal;
4642
+ var DropdownMenuSub = radixUi.DropdownMenu.Sub;
4643
+ var DropdownMenuRadioGroup = radixUi.DropdownMenu.RadioGroup;
4644
+ var DropdownMenuContent = /* @__PURE__ */ react.forwardRef(function DropdownMenuContent2({ className, sideOffset = 4, container, ...props }, ref) {
4645
+ const themed = usePortalContainer();
4646
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.DropdownMenu.Portal, { container: container ?? themed ?? void 0, children: /* @__PURE__ */ jsxRuntime.jsx(
4647
+ radixUi.DropdownMenu.Content,
4648
+ {
4649
+ ref,
4650
+ "data-slot": "dropdown-menu-content",
4651
+ sideOffset,
4652
+ className: cx("orynn-content orynn-menu__content", className),
4653
+ ...props
4654
+ }
4655
+ ) });
4656
+ });
4657
+ var DropdownMenuItem = /* @__PURE__ */ react.forwardRef(
4658
+ function DropdownMenuItem2({ className, inset, variant = "default", icon, shortcut, children, ...props }, ref) {
4659
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4660
+ radixUi.DropdownMenu.Item,
4661
+ {
4662
+ ref,
4663
+ "data-slot": "dropdown-menu-item",
4664
+ "data-inset": inset || void 0,
4665
+ "data-variant": variant,
4666
+ className: cx("orynn-menu__item", className),
4667
+ ...props,
4668
+ children: [
4669
+ icon,
4670
+ children,
4671
+ shortcut != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-menu__shortcut", children: shortcut })
4672
+ ]
4673
+ }
4674
+ );
4675
+ }
4676
+ );
4677
+ var DropdownMenuCheckboxItem = /* @__PURE__ */ react.forwardRef(function DropdownMenuCheckboxItem2({ className, children, checked, ...props }, ref) {
4678
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4679
+ radixUi.DropdownMenu.CheckboxItem,
4680
+ {
4681
+ ref,
4682
+ "data-slot": "dropdown-menu-checkbox-item",
4683
+ className: cx("orynn-menu__item", "orynn-menu__item--check", className),
4684
+ checked,
4685
+ ...props,
4686
+ children: [
4687
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-menu__indicator", children: /* @__PURE__ */ jsxRuntime.jsx(radixUi.DropdownMenu.ItemIndicator, { children: /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, {}) }) }),
4688
+ children
4689
+ ]
4690
+ }
4691
+ );
4692
+ });
4693
+ var DropdownMenuRadioItem = /* @__PURE__ */ react.forwardRef(function DropdownMenuRadioItem2({ className, children, ...props }, ref) {
4694
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4695
+ radixUi.DropdownMenu.RadioItem,
4696
+ {
4697
+ ref,
4698
+ "data-slot": "dropdown-menu-radio-item",
4699
+ className: cx("orynn-menu__item", "orynn-menu__item--check", className),
4700
+ ...props,
4701
+ children: [
4702
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-menu__indicator", children: /* @__PURE__ */ jsxRuntime.jsx(radixUi.DropdownMenu.ItemIndicator, { children: /* @__PURE__ */ jsxRuntime.jsx(CircleIcon, { className: "orynn-menu__dot" }) }) }),
4703
+ children
4704
+ ]
4705
+ }
4706
+ );
4707
+ });
4708
+ var DropdownMenuLabel = /* @__PURE__ */ react.forwardRef(
4709
+ function DropdownMenuLabel2({ className, inset, ...props }, ref) {
4710
+ return /* @__PURE__ */ jsxRuntime.jsx(
4711
+ radixUi.DropdownMenu.Label,
4712
+ {
4713
+ ref,
4714
+ "data-slot": "dropdown-menu-label",
4715
+ "data-inset": inset || void 0,
4716
+ className: cx("orynn-menu__label", className),
4717
+ ...props
4718
+ }
4719
+ );
4720
+ }
4721
+ );
4722
+ var DropdownMenuSeparator = /* @__PURE__ */ react.forwardRef(function DropdownMenuSeparator2({ className, ...props }, ref) {
4723
+ return /* @__PURE__ */ jsxRuntime.jsx(
4724
+ radixUi.DropdownMenu.Separator,
4725
+ {
4726
+ ref,
4727
+ "data-slot": "dropdown-menu-separator",
4728
+ className: cx("orynn-menu__separator", className),
4729
+ ...props
4730
+ }
4731
+ );
4732
+ });
4733
+ function DropdownMenuShortcut({ className, ...props }) {
4734
+ return /* @__PURE__ */ jsxRuntime.jsx(
4735
+ "span",
4736
+ {
4737
+ "data-slot": "dropdown-menu-shortcut",
4738
+ className: cx("orynn-menu__shortcut", className),
4739
+ ...props
4740
+ }
4741
+ );
2690
4742
  }
4743
+ var DropdownMenuSubTrigger = /* @__PURE__ */ react.forwardRef(function DropdownMenuSubTrigger2({ className, inset, children, ...props }, ref) {
4744
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4745
+ radixUi.DropdownMenu.SubTrigger,
4746
+ {
4747
+ ref,
4748
+ "data-slot": "dropdown-menu-sub-trigger",
4749
+ "data-inset": inset || void 0,
4750
+ className: cx("orynn-menu__item", "orynn-menu__sub-trigger", className),
4751
+ ...props,
4752
+ children: [
4753
+ children,
4754
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronRightIcon, { className: "orynn-menu__sub-chevron" })
4755
+ ]
4756
+ }
4757
+ );
4758
+ });
4759
+ var DropdownMenuSubContent = /* @__PURE__ */ react.forwardRef(function DropdownMenuSubContent2({ className, ...props }, ref) {
4760
+ return /* @__PURE__ */ jsxRuntime.jsx(
4761
+ radixUi.DropdownMenu.SubContent,
4762
+ {
4763
+ ref,
4764
+ "data-slot": "dropdown-menu-sub-content",
4765
+ className: cx("orynn-content", "orynn-menu__content", "orynn-menu__sub-content", className),
4766
+ ...props
4767
+ }
4768
+ );
4769
+ });
2691
4770
 
2692
4771
  // src/types/validation.ts
2693
4772
  var FORM_ERROR_KEY = "$form";
2694
4773
 
4774
+ // src/core/validation/standard-schema.ts
4775
+ function pathToField(path) {
4776
+ if (!path || path.length === 0) return { field: FORM_ERROR_KEY, rest: "" };
4777
+ const seg = (p) => String(typeof p === "object" && p !== null && "key" in p ? p.key : p);
4778
+ const parts = path.map(seg);
4779
+ return { field: parts[0] ?? FORM_ERROR_KEY, rest: parts.slice(1).join(".") };
4780
+ }
4781
+ function standardSchemaResolver(schema) {
4782
+ return (values) => {
4783
+ const out = schema["~standard"].validate(values);
4784
+ const toResult = (r) => {
4785
+ const result = {};
4786
+ if (!("issues" in r) || !r.issues) return result;
4787
+ for (const issue of r.issues) {
4788
+ const { field, rest } = pathToField(issue.path);
4789
+ const bucket = result[field] ?? [];
4790
+ bucket.push({ rule: "schema", message: issue.message, ...rest ? { path: rest } : {} });
4791
+ result[field] = bucket;
4792
+ }
4793
+ return result;
4794
+ };
4795
+ return out instanceof Promise ? out.then(toResult) : toResult(out);
4796
+ };
4797
+ }
4798
+
4799
+ exports.Alert = Alert;
4800
+ exports.AlertDescription = AlertDescription;
4801
+ exports.AlertTitle = AlertTitle;
4802
+ exports.Badge = Badge;
4803
+ exports.Button = Button;
4804
+ exports.Card = Card;
4805
+ exports.CardAction = CardAction;
4806
+ exports.CardContent = CardContent;
4807
+ exports.CardDescription = CardDescription;
4808
+ exports.CardFooter = CardFooter;
4809
+ exports.CardHeader = CardHeader;
4810
+ exports.CardTitle = CardTitle;
2695
4811
  exports.Checkbox = Checkbox;
2696
4812
  exports.CheckboxGroup = CheckboxGroup;
2697
4813
  exports.DatePicker = DatePicker;
4814
+ exports.Dialog = Dialog;
4815
+ exports.DialogClose = DialogClose;
4816
+ exports.DialogContent = DialogContent;
4817
+ exports.DialogDescription = DialogDescription;
4818
+ exports.DialogFooter = DialogFooter;
4819
+ exports.DialogHeader = DialogHeader;
4820
+ exports.DialogPortal = DialogPortal;
4821
+ exports.DialogTitle = DialogTitle;
4822
+ exports.DialogTrigger = DialogTrigger;
2698
4823
  exports.Dropdown = Dropdown;
4824
+ exports.DropdownMenu = DropdownMenu;
4825
+ exports.DropdownMenuCheckboxItem = DropdownMenuCheckboxItem;
4826
+ exports.DropdownMenuContent = DropdownMenuContent;
4827
+ exports.DropdownMenuGroup = DropdownMenuGroup;
4828
+ exports.DropdownMenuItem = DropdownMenuItem;
4829
+ exports.DropdownMenuLabel = DropdownMenuLabel;
4830
+ exports.DropdownMenuPortal = DropdownMenuPortal;
4831
+ exports.DropdownMenuRadioGroup = DropdownMenuRadioGroup;
4832
+ exports.DropdownMenuRadioItem = DropdownMenuRadioItem;
4833
+ exports.DropdownMenuSeparator = DropdownMenuSeparator;
4834
+ exports.DropdownMenuShortcut = DropdownMenuShortcut;
4835
+ exports.DropdownMenuSub = DropdownMenuSub;
4836
+ exports.DropdownMenuSubContent = DropdownMenuSubContent;
4837
+ exports.DropdownMenuSubTrigger = DropdownMenuSubTrigger;
4838
+ exports.DropdownMenuTrigger = DropdownMenuTrigger;
2699
4839
  exports.FORM_ERROR_KEY = FORM_ERROR_KEY;
2700
4840
  exports.Field = Field;
2701
4841
  exports.FieldBox = FieldBox;
2702
4842
  exports.Fieldset = Fieldset;
4843
+ exports.FieldsetView = FieldsetView;
4844
+ exports.Form = Form;
2703
4845
  exports.Input = Input;
4846
+ exports.Label = Label;
2704
4847
  exports.NumberField = NumberField;
2705
4848
  exports.OrynnProvider = OrynnProvider;
4849
+ exports.Popover = Popover2;
4850
+ exports.PopoverAnchor = PopoverAnchor;
4851
+ exports.PopoverArrow = PopoverArrow;
4852
+ exports.PopoverClose = PopoverClose;
4853
+ exports.PopoverContent = PopoverContent;
4854
+ exports.PopoverTrigger = PopoverTrigger;
2706
4855
  exports.Radio = Radio;
2707
4856
  exports.RadioGroup = RadioGroup;
2708
4857
  exports.Select = Dropdown;
4858
+ exports.Separator = Separator;
4859
+ exports.Switch = Switch;
4860
+ exports.Table = Table;
4861
+ exports.TableBody = TableBody;
4862
+ exports.TableCaption = TableCaption;
4863
+ exports.TableCell = TableCell;
4864
+ exports.TableFooter = TableFooter;
4865
+ exports.TableHead = TableHead;
4866
+ exports.TableHeader = TableHeader;
4867
+ exports.TableRow = TableRow;
4868
+ exports.Tabs = Tabs;
4869
+ exports.TabsContent = TabsContent;
4870
+ exports.TabsList = TabsList;
4871
+ exports.TabsTrigger = TabsTrigger;
2709
4872
  exports.Textarea = Textarea;
4873
+ exports.Tooltip = Tooltip;
4874
+ exports.TooltipContent = TooltipContent;
4875
+ exports.TooltipProvider = TooltipProvider;
4876
+ exports.TooltipTrigger = TooltipTrigger;
4877
+ exports.badgeClasses = badgeClasses;
4878
+ exports.builtInRules = builtInRules;
4879
+ exports.buttonClasses = buttonClasses;
2710
4880
  exports.createRegistry = createRegistry;
2711
4881
  exports.createRuleResolver = createRuleResolver;
2712
4882
  exports.defaultRegistry = defaultRegistry;
@@ -2715,7 +4885,9 @@ exports.normalizeConfig = normalizeConfig;
2715
4885
  exports.registerField = registerField;
2716
4886
  exports.registerRule = registerRule;
2717
4887
  exports.resolveThemeVars = resolveThemeVars;
4888
+ exports.standardSchemaResolver = standardSchemaResolver;
2718
4889
  exports.useFieldsetState = useFieldsetState;
2719
4890
  exports.useOrynnTheme = useOrynnTheme;
4891
+ exports.usePortalContainer = usePortalContainer;
2720
4892
  //# sourceMappingURL=index.cjs.map
2721
4893
  //# sourceMappingURL=index.cjs.map