orynn 0.2.0 → 0.6.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",
@@ -456,14 +623,17 @@ function Popover({
456
623
  left: pos.left,
457
624
  top: pos.top,
458
625
  width: matchWidth ? pos.width : void 0,
459
- minWidth: pos.width,
626
+ // Only pin the min-width to the anchor when we're matching it. With
627
+ // `matchWidth={false}` the panel sizes to its own content, so a wide
628
+ // trigger doesn't stretch it across the screen.
629
+ minWidth: matchWidth ? pos.width : void 0,
460
630
  maxHeight: pos.maxHeight,
461
631
  transform: pos.placement === "top" ? "translateY(-100%)" : void 0
462
632
  },
463
633
  children
464
634
  }
465
635
  ),
466
- document.body
636
+ container
467
637
  );
468
638
  }
469
639
 
@@ -488,27 +658,82 @@ var clampDate = (d, min, max) => {
488
658
  return d;
489
659
  };
490
660
  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]);
661
+ 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;
662
+ function formatDate(d, fmt = "yyyy-MM-dd", locale) {
663
+ if (typeof fmt !== "string") return new Intl.DateTimeFormat(locale, fmt).format(d);
664
+ const h12 = d.getHours() % 12 || 12;
665
+ const map = {
666
+ yyyy: String(d.getFullYear()),
667
+ yy: pad(d.getFullYear() % 100),
668
+ MMMM: new Intl.DateTimeFormat(locale, { month: "long" }).format(d),
669
+ MMM: new Intl.DateTimeFormat(locale, { month: "short" }).format(d),
670
+ MM: pad(d.getMonth() + 1),
671
+ M: String(d.getMonth() + 1),
672
+ dd: pad(d.getDate()),
673
+ d: String(d.getDate()),
674
+ EEEE: new Intl.DateTimeFormat(locale, { weekday: "long" }).format(d),
675
+ EEE: new Intl.DateTimeFormat(locale, { weekday: "short" }).format(d),
676
+ HH: pad(d.getHours()),
677
+ H: String(d.getHours()),
678
+ hh: pad(h12),
679
+ h: String(h12),
680
+ mm: pad(d.getMinutes()),
681
+ m: String(d.getMinutes()),
682
+ ss: pad(d.getSeconds()),
683
+ s: String(d.getSeconds()),
684
+ aa: d.getHours() < 12 ? "am" : "pm",
685
+ a: d.getHours() < 12 ? "am" : "pm",
686
+ A: d.getHours() < 12 ? "AM" : "PM"
687
+ };
688
+ return fmt.replace(FORMAT_TOKENS, (t) => map[t] ?? t);
689
+ }
690
+ function matchMonthName(input, locale) {
691
+ for (let m = 0; m < 12; m += 1) {
692
+ for (const style of ["long", "short"]) {
693
+ const name = new Intl.DateTimeFormat(locale, { month: style }).format(new Date(2021, m, 1));
694
+ const i = input.toLowerCase().indexOf(name.toLowerCase());
695
+ if (i !== -1) return { month: m, rest: input.slice(0, i) + input.slice(i + name.length) };
696
+ }
697
+ }
698
+ return null;
699
+ }
700
+ function parseDate(input, fmt = "yyyy-MM-dd", locale) {
701
+ const pattern = typeof fmt === "string" ? fmt : "yyyy-MM-dd";
702
+ let text = input;
703
+ let monthFromName;
704
+ if (/MMM/.test(pattern)) {
705
+ const hit = matchMonthName(text, locale);
706
+ if (hit) {
707
+ monthFromName = hit.month;
708
+ text = hit.rest;
709
+ }
710
+ }
711
+ const nums = text.match(/\d+/g);
712
+ if (!nums && monthFromName === void 0) return null;
713
+ 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
714
  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);
715
+ let mo = monthFromName;
716
+ let day;
717
+ let hh = 0;
718
+ let mm = 0;
719
+ let ss = 0;
720
+ let ni = 0;
721
+ for (const tok of order) {
722
+ if ((tok === "MM" || tok === "M") && monthFromName !== void 0) continue;
723
+ const n = Number((nums ?? [])[ni]);
724
+ ni += 1;
725
+ if (Number.isNaN(n)) continue;
726
+ if (tok === "yyyy" || tok === "yy") y = n < 100 ? 2e3 + n : n;
727
+ else if (tok === "MM" || tok === "M") mo = n - 1;
728
+ else if (tok === "dd" || tok === "d") day = n;
729
+ else if (tok === "HH" || tok === "H" || tok === "hh" || tok === "h") hh = n;
730
+ else if (tok === "mm" || tok === "m") mm = n;
731
+ else if (tok === "ss" || tok === "s") ss = n;
732
+ }
733
+ if (/pm/i.test(input) && hh < 12) hh += 12;
734
+ if (/am/i.test(input) && hh === 12) hh = 0;
735
+ if (y == null && day == null && mo == null) return null;
736
+ const d = new Date(y ?? (/* @__PURE__ */ new Date()).getFullYear(), mo ?? 0, day ?? 1, hh, mm, ss);
512
737
  return Number.isNaN(d.getTime()) ? null : d;
513
738
  }
514
739
  function toDate(v) {
@@ -517,11 +742,14 @@ function toDate(v) {
517
742
  const d = new Date(v);
518
743
  return Number.isNaN(d.getTime()) ? null : startOfDay(d);
519
744
  }
520
- function monthMatrix(viewYear, viewMonth, firstDayOfWeek) {
745
+ function monthMatrix(viewYear, viewMonth, firstDayOfWeek, fixedWeeks = true) {
521
746
  const first = new Date(viewYear, viewMonth, 1);
522
747
  const offset = (first.getDay() - firstDayOfWeek + 7) % 7;
523
748
  const start = addDays(first, -offset);
524
- return Array.from({ length: 42 }, (_, i) => addDays(start, i));
749
+ if (fixedWeeks) return Array.from({ length: 42 }, (_, i) => addDays(start, i));
750
+ const last = new Date(viewYear, viewMonth + 1, 0);
751
+ const used = Math.ceil((offset + last.getDate()) / 7);
752
+ return Array.from({ length: used * 7 }, (_, i) => addDays(start, i));
525
753
  }
526
754
  function weekdayLabels(firstDayOfWeek, locale) {
527
755
  const fmt = new Intl.DateTimeFormat(locale, { weekday: "short" });
@@ -530,6 +758,10 @@ function weekdayLabels(firstDayOfWeek, locale) {
530
758
  (_, i) => fmt.format(new Date(2023, 0, 1 + (firstDayOfWeek + i) % 7))
531
759
  );
532
760
  }
761
+ function decadeGrid(year) {
762
+ const start = Math.floor(year / 10) * 10;
763
+ return { start, years: Array.from({ length: 12 }, (_, i) => start - 1 + i) };
764
+ }
533
765
  function isoWeek(d) {
534
766
  const date = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
535
767
  const dayNum = (date.getUTCDay() + 6) % 7;
@@ -538,9 +770,40 @@ function isoWeek(d) {
538
770
  const diff = date.getTime() - firstThursday.getTime();
539
771
  return 1 + Math.round(diff / (7 * 24 * 3600 * 1e3));
540
772
  }
773
+
774
+ // src/controls/DatePicker/calendar-range.ts
775
+ function normalizeRange(r) {
776
+ if (r.start && r.end && startOfDay(r.start) > startOfDay(r.end)) {
777
+ return { start: r.end, end: r.start };
778
+ }
779
+ return r;
780
+ }
781
+ function isRangeComplete(r) {
782
+ return !!r && !!r.start && !!r.end;
783
+ }
784
+ function isWithinRange(d, r) {
785
+ if (!isRangeComplete(r)) return false;
786
+ const t = startOfDay(d).getTime();
787
+ return t > startOfDay(r.start).getTime() && t < startOfDay(r.end).getTime();
788
+ }
789
+ var isRangeStart = (d, r) => !!r && isSameDay(d, r.start);
790
+ var isRangeEnd = (d, r) => !!r && isSameDay(d, r.end);
791
+ function previewRange(anchor, hover) {
792
+ if (!anchor || !hover) return null;
793
+ return normalizeRange({ start: anchor, end: hover });
794
+ }
795
+ function nextRange(current, d) {
796
+ if (!current.start || current.start && current.end) return { start: startOfDay(d), end: null };
797
+ return normalizeRange({ start: current.start, end: startOfDay(d) });
798
+ }
541
799
  function Calendar({
542
800
  value,
543
801
  onSelect,
802
+ mode = "single",
803
+ selectedDates,
804
+ range: range2,
805
+ previewRange: previewRange2,
806
+ onHoverDate,
544
807
  minDate,
545
808
  maxDate,
546
809
  isDisabled,
@@ -550,16 +813,36 @@ function Calendar({
550
813
  showToday,
551
814
  showClear,
552
815
  onClear,
553
- autoFocus
816
+ autoFocus,
817
+ view: viewProp,
818
+ defaultView,
819
+ onViewChange,
820
+ precision = "day",
821
+ fixedWeeks = true,
822
+ defaultMonth,
823
+ numberOfMonths = 1,
824
+ className,
825
+ style,
826
+ fillWidth
554
827
  }) {
828
+ const panels = Math.max(1, Math.floor(numberOfMonths));
555
829
  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() });
830
+ const anchor = value ?? (mode === "range" ? range2?.start ?? range2?.end ?? null : null) ?? (mode === "multiple" ? selectedDates?.[0] ?? null : null) ?? defaultMonth ?? null;
831
+ const initial = anchor ?? clampDate(today, minDate, maxDate);
832
+ const [nav, setNav] = react.useState({ y: initial.getFullYear(), m: initial.getMonth() });
558
833
  const [focused, setFocused] = react.useState(initial);
834
+ const [innerView, setInnerView] = react.useState(
835
+ defaultView ?? (precision === "day" ? "day" : precision)
836
+ );
837
+ const view = viewProp ?? innerView;
838
+ const setView = (v) => {
839
+ if (viewProp === void 0) setInnerView(v);
840
+ onViewChange?.(v);
841
+ };
559
842
  const gridRef = react.useRef(null);
560
843
  react.useEffect(() => {
561
844
  if (value) {
562
- setView({ y: value.getFullYear(), m: value.getMonth() });
845
+ setNav({ y: value.getFullYear(), m: value.getMonth() });
563
846
  setFocused(value);
564
847
  }
565
848
  }, [value]);
@@ -568,94 +851,118 @@ function Calendar({
568
851
  gridRef.current?.querySelector('[data-focused="true"]')?.focus();
569
852
  }
570
853
  }, [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
854
  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() });
855
+ const monthOutOfRange = (y, m) => {
856
+ const first = new Date(y, m, 1);
857
+ const last = new Date(y, m + 1, 0);
858
+ return maxDate != null && first > startOfDay(maxDate) || minDate != null && last < startOfDay(minDate);
859
+ };
860
+ const yearOutOfRange = (y) => maxDate != null && new Date(y, 0, 1) > startOfDay(maxDate) || minDate != null && new Date(y, 11, 31) < startOfDay(minDate);
861
+ const focusGrid = () => {
581
862
  requestAnimationFrame(() => {
582
863
  gridRef.current?.querySelector('[data-focused="true"]')?.focus();
583
864
  });
584
865
  };
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);
866
+ const navIndex = nav.y * 12 + nav.m;
867
+ const goto = (d) => {
868
+ const c = clampDate(d, minDate, maxDate);
869
+ setFocused(c);
870
+ const ci = c.getFullYear() * 12 + c.getMonth();
871
+ if (ci < navIndex || ci > navIndex + panels - 1) {
872
+ setNav({ y: c.getFullYear(), m: c.getMonth() });
873
+ }
874
+ focusGrid();
604
875
  };
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(
876
+ const canDrillUp = view !== "year";
877
+ const dayTitle = () => {
878
+ const first = new Date(nav.y, nav.m, 1);
879
+ if (panels <= 1) {
880
+ return new Intl.DateTimeFormat(locale, { month: "long", year: "numeric" }).format(first);
881
+ }
882
+ const last = new Date(nav.y, nav.m + panels - 1, 1);
883
+ const f = new Intl.DateTimeFormat(locale, { month: "short" }).format(first);
884
+ const l = new Intl.DateTimeFormat(locale, { month: "short", year: "numeric" }).format(last);
885
+ return first.getFullYear() === last.getFullYear() ? `${f} \u2013 ${l}` : `${f} ${first.getFullYear()} \u2013 ${l}`;
886
+ };
887
+ const titleText = view === "day" ? dayTitle() : view === "month" ? String(nav.y) : (() => {
888
+ const g = decadeGrid(nav.y);
889
+ return `${g.start} \u2013 ${g.start + 9}`;
890
+ })();
891
+ const step = (dir) => {
892
+ if (view === "day") setNav((v) => ({ ...addMonthNav(v, dir) }));
893
+ else if (view === "month") setNav((v) => ({ ...v, y: v.y + dir }));
894
+ else setNav((v) => ({ ...v, y: v.y + dir * 10 }));
895
+ };
896
+ const header = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "orynn-calendar__header", children: [
897
+ /* @__PURE__ */ jsxRuntime.jsx(
898
+ "button",
899
+ {
900
+ type: "button",
901
+ className: "orynn-calendar__nav",
902
+ "aria-label": view === "day" ? "Previous month" : view === "month" ? "Previous year" : "Previous decade",
903
+ onClick: () => step(-1),
904
+ children: /* @__PURE__ */ jsxRuntime.jsx(ChevronLeftIcon, {})
905
+ }
906
+ ),
907
+ /* @__PURE__ */ jsxRuntime.jsx(
908
+ "button",
909
+ {
910
+ type: "button",
911
+ className: "orynn-calendar__title",
912
+ "aria-live": "polite",
913
+ disabled: !canDrillUp,
914
+ onClick: () => {
915
+ if (!canDrillUp) return;
916
+ setView(view === "day" ? "month" : "year");
917
+ },
918
+ children: titleText
919
+ }
920
+ ),
921
+ /* @__PURE__ */ jsxRuntime.jsx(
922
+ "button",
923
+ {
924
+ type: "button",
925
+ className: "orynn-calendar__nav",
926
+ "aria-label": view === "day" ? "Next month" : view === "month" ? "Next year" : "Next decade",
927
+ onClick: () => step(1),
928
+ children: /* @__PURE__ */ jsxRuntime.jsx(ChevronRightIcon, {})
929
+ }
930
+ )
931
+ ] });
932
+ const dayGrid = (monthOffset = 0) => {
933
+ const panelMonth = new Date(nav.y, nav.m + monthOffset, 1);
934
+ const py = panelMonth.getFullYear();
935
+ const pm = panelMonth.getMonth();
936
+ const cells = monthMatrix(py, pm, firstDayOfWeek, fixedWeeks);
937
+ const weeks = cells.length / 7;
938
+ const weekdays = weekdayLabels(firstDayOfWeek, locale);
939
+ const monthName = new Intl.DateTimeFormat(locale, { month: "long", year: "numeric" }).format(
940
+ panelMonth
941
+ );
942
+ const onKeyDown = (e) => {
943
+ const k = e.key;
944
+ let next = null;
945
+ if (k === "ArrowLeft") next = addDays(focused, -1);
946
+ else if (k === "ArrowRight") next = addDays(focused, 1);
947
+ else if (k === "ArrowUp") next = addDays(focused, -7);
948
+ else if (k === "ArrowDown") next = addDays(focused, 7);
949
+ else if (k === "Home")
950
+ next = addDays(focused, -((focused.getDay() - firstDayOfWeek + 7) % 7));
951
+ else if (k === "End")
952
+ next = addDays(focused, 6 - (focused.getDay() - firstDayOfWeek + 7) % 7);
953
+ else if (k === "PageUp") next = addMonths(focused, e.shiftKey ? -12 : -1);
954
+ else if (k === "PageDown") next = addMonths(focused, e.shiftKey ? 12 : 1);
955
+ else if (k === "Enter" || k === " ") {
956
+ e.preventDefault();
957
+ if (!outOfRange(focused)) onSelect(focused);
958
+ return;
959
+ } else return;
960
+ e.preventDefault();
961
+ goto(next);
962
+ };
963
+ return /* @__PURE__ */ jsxRuntime.jsxs(
656
964
  "table",
657
965
  {
658
- ref: gridRef,
659
966
  className: "orynn-calendar__grid",
660
967
  role: "grid",
661
968
  "aria-label": monthName,
@@ -665,16 +972,29 @@ function Calendar({
665
972
  showWeekNumbers && /* @__PURE__ */ jsxRuntime.jsx("th", { "aria-hidden": "true" }),
666
973
  weekdays.map((w) => /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", children: w }, w))
667
974
  ] }) }),
668
- /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: Array.from({ length: 6 }, (_, week) => {
975
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { onMouseLeave: onHoverDate ? () => onHoverDate(null) : void 0, children: Array.from({ length: weeks }, (_, week) => {
669
976
  const row = cells.slice(week * 7, week * 7 + 7);
670
977
  const firstOfRow = row[0];
671
978
  return /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
672
979
  showWeekNumbers && /* @__PURE__ */ jsxRuntime.jsx("td", { className: "orynn-calendar__weeknum", children: isoWeek(firstOfRow) }),
673
980
  row.map((d) => {
674
- const inMonth = d.getMonth() === view.m;
981
+ const inMonth = d.getMonth() === pm;
675
982
  const disabled = outOfRange(d);
676
- const selected = isSameDay(d, value);
677
983
  const isFocused = isSameDay(d, focused);
984
+ let selected;
985
+ let rangeStart = false;
986
+ let rangeEnd = false;
987
+ let inRange = false;
988
+ if (mode === "multiple") {
989
+ selected = (selectedDates ?? []).some((x) => isSameDay(x, d));
990
+ } else if (mode === "range") {
991
+ rangeStart = isRangeStart(d, range2) || !!previewRange2 && isRangeStart(d, previewRange2);
992
+ rangeEnd = isRangeEnd(d, range2) || !!previewRange2 && isRangeEnd(d, previewRange2);
993
+ inRange = isWithinRange(d, range2) || !!previewRange2 && isWithinRange(d, previewRange2);
994
+ selected = rangeStart || rangeEnd;
995
+ } else {
996
+ selected = isSameDay(d, value);
997
+ }
678
998
  return /* @__PURE__ */ jsxRuntime.jsx("td", { role: "gridcell", "aria-selected": selected, children: /* @__PURE__ */ jsxRuntime.jsx(
679
999
  "button",
680
1000
  {
@@ -685,10 +1005,15 @@ function Calendar({
685
1005
  "data-outside": !inMonth || void 0,
686
1006
  "data-today": isSameDay(d, today) || void 0,
687
1007
  "data-selected": selected || void 0,
1008
+ "data-range-start": rangeStart || void 0,
1009
+ "data-range-end": rangeEnd || void 0,
1010
+ "data-in-range": inRange || void 0,
688
1011
  disabled,
689
1012
  "aria-label": new Intl.DateTimeFormat(locale, { dateStyle: "full" }).format(
690
1013
  d
691
1014
  ),
1015
+ onMouseEnter: onHoverDate ? () => onHoverDate(d) : void 0,
1016
+ onFocus: onHoverDate ? () => onHoverDate(d) : void 0,
692
1017
  onClick: () => {
693
1018
  setFocused(d);
694
1019
  onSelect(d);
@@ -700,25 +1025,189 @@ function Calendar({
700
1025
  ] }, firstOfRow.toISOString());
701
1026
  }) })
702
1027
  ]
1028
+ },
1029
+ `${py}-${pm}`
1030
+ );
1031
+ };
1032
+ const monthGrid = () => {
1033
+ const names = Array.from(
1034
+ { length: 12 },
1035
+ (_, i) => new Intl.DateTimeFormat(locale, { month: "short" }).format(new Date(2021, i, 1))
1036
+ );
1037
+ const onKeyDown = (e) => {
1038
+ let m = nav.m;
1039
+ if (e.key === "ArrowLeft") m -= 1;
1040
+ else if (e.key === "ArrowRight") m += 1;
1041
+ else if (e.key === "ArrowUp") m -= 3;
1042
+ else if (e.key === "ArrowDown") m += 3;
1043
+ else if (e.key === "Enter" || e.key === " ") {
1044
+ e.preventDefault();
1045
+ pickMonth(nav.m);
1046
+ return;
1047
+ } else return;
1048
+ e.preventDefault();
1049
+ const d = addMonths(new Date(nav.y, nav.m, 1), m - nav.m);
1050
+ setNav({ y: d.getFullYear(), m: d.getMonth() });
1051
+ focusGrid();
1052
+ };
1053
+ return /* @__PURE__ */ jsxRuntime.jsx(
1054
+ "div",
1055
+ {
1056
+ className: "orynn-calendar__pickgrid",
1057
+ role: "grid",
1058
+ "aria-label": `Months of ${nav.y}`,
1059
+ onKeyDown,
1060
+ 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) => {
1061
+ const i = rowStart + j;
1062
+ const isFocused = i === nav.m;
1063
+ const selected = value != null && value.getFullYear() === nav.y && value.getMonth() === i;
1064
+ return /* @__PURE__ */ jsxRuntime.jsx(
1065
+ "button",
1066
+ {
1067
+ type: "button",
1068
+ className: "orynn-calendar__cell",
1069
+ tabIndex: isFocused ? 0 : -1,
1070
+ "data-focused": isFocused || void 0,
1071
+ "data-selected": selected || void 0,
1072
+ disabled: monthOutOfRange(nav.y, i),
1073
+ onClick: () => pickMonth(i),
1074
+ children: name
1075
+ },
1076
+ name
1077
+ );
1078
+ }) }, rowStart))
703
1079
  }
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
- ] });
1080
+ );
1081
+ };
1082
+ const pickMonth = (m) => {
1083
+ if (monthOutOfRange(nav.y, m)) return;
1084
+ setNav((v) => ({ ...v, m }));
1085
+ if (precision === "month") {
1086
+ onSelect(new Date(nav.y, m, 1));
1087
+ } else {
1088
+ setView("day");
1089
+ setFocused(clampDate(new Date(nav.y, m, 1), minDate, maxDate));
1090
+ focusGrid();
1091
+ }
1092
+ };
1093
+ const yearGrid = () => {
1094
+ const g = decadeGrid(nav.y);
1095
+ const onKeyDown = (e) => {
1096
+ let y = nav.y;
1097
+ if (e.key === "ArrowLeft") y -= 1;
1098
+ else if (e.key === "ArrowRight") y += 1;
1099
+ else if (e.key === "ArrowUp") y -= 3;
1100
+ else if (e.key === "ArrowDown") y += 3;
1101
+ else if (e.key === "Enter" || e.key === " ") {
1102
+ e.preventDefault();
1103
+ pickYear(nav.y);
1104
+ return;
1105
+ } else return;
1106
+ e.preventDefault();
1107
+ setNav((v) => ({ ...v, y }));
1108
+ focusGrid();
1109
+ };
1110
+ return /* @__PURE__ */ jsxRuntime.jsx(
1111
+ "div",
1112
+ {
1113
+ className: "orynn-calendar__pickgrid",
1114
+ role: "grid",
1115
+ "aria-label": `Years ${g.start}\u2013${g.start + 9}`,
1116
+ onKeyDown,
1117
+ 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) => {
1118
+ const isFocused = y === nav.y;
1119
+ const selected = value != null && value.getFullYear() === y;
1120
+ return /* @__PURE__ */ jsxRuntime.jsx(
1121
+ "button",
1122
+ {
1123
+ type: "button",
1124
+ className: "orynn-calendar__cell",
1125
+ tabIndex: isFocused ? 0 : -1,
1126
+ "data-focused": isFocused || void 0,
1127
+ "data-outside": y < g.start || y > g.start + 9 || void 0,
1128
+ "data-selected": selected || void 0,
1129
+ disabled: yearOutOfRange(y),
1130
+ onClick: () => pickYear(y),
1131
+ children: y
1132
+ },
1133
+ y
1134
+ );
1135
+ }) }, rowStart))
1136
+ }
1137
+ );
1138
+ };
1139
+ const pickYear = (y) => {
1140
+ if (yearOutOfRange(y)) return;
1141
+ setNav((v) => ({ ...v, y }));
1142
+ if (precision === "year") {
1143
+ onSelect(new Date(y, 0, 1));
1144
+ } else {
1145
+ setView("month");
1146
+ focusGrid();
1147
+ }
1148
+ };
1149
+ const dayView = panels <= 1 ? dayGrid() : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "orynn-calendar__panels", children: Array.from({ length: panels }, (_, i) => dayGrid(i)) });
1150
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1151
+ "div",
1152
+ {
1153
+ className: cx("orynn-calendar", className),
1154
+ style,
1155
+ ref: gridRef,
1156
+ "data-view": view,
1157
+ "data-months": panels > 1 ? panels : void 0,
1158
+ "data-fill": fillWidth || void 0,
1159
+ children: [
1160
+ header,
1161
+ " ",
1162
+ view === "day" ? dayView : view === "month" ? monthGrid() : yearGrid(),
1163
+ (showToday || showClear) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "orynn-calendar__footer", children: [
1164
+ showToday ? /* @__PURE__ */ jsxRuntime.jsx(
1165
+ "button",
1166
+ {
1167
+ type: "button",
1168
+ className: "orynn-calendar__action",
1169
+ disabled: outOfRange(today),
1170
+ onClick: () => onSelect(today),
1171
+ children: "Today"
1172
+ }
1173
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", {}),
1174
+ showClear && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "orynn-calendar__action", onClick: onClear, children: "Clear" })
1175
+ ] })
1176
+ ]
1177
+ }
1178
+ );
1179
+ }
1180
+ function addMonthNav(v, dir) {
1181
+ const total = v.y * 12 + v.m + dir;
1182
+ return { y: Math.floor(total / 12), m: (total % 12 + 12) % 12 };
1183
+ }
1184
+
1185
+ // src/controls/DatePicker/time-utils.ts
1186
+ var timeParts = (d) => ({
1187
+ h: d.getHours(),
1188
+ m: d.getMinutes(),
1189
+ s: d.getSeconds()
1190
+ });
1191
+ function withTime(date, time) {
1192
+ const r = new Date(date);
1193
+ r.setHours(time.h, time.m, time.s, 0);
1194
+ return r;
1195
+ }
1196
+ function minuteOptions(step) {
1197
+ const s = Math.max(1, Math.min(30, Math.floor(step)));
1198
+ const out = [];
1199
+ for (let m = 0; m < 60; m += s) out.push(m);
1200
+ return out;
719
1201
  }
1202
+ var pad2 = (n) => String(n).padStart(2, "0");
1203
+ var normDates = (v) => Array.isArray(v) ? v.map((x) => toDate(x)).filter(Boolean) : [];
1204
+ var normRange = (v) => {
1205
+ const r = v ?? {};
1206
+ return { start: toDate(r.start ?? null), end: toDate(r.end ?? null) };
1207
+ };
720
1208
  var DatePicker = /* @__PURE__ */ react.forwardRef(
721
- function DatePicker2(props, ref) {
1209
+ function DatePicker2(propsIn, ref) {
1210
+ const props = propsIn;
722
1211
  const {
723
1212
  label,
724
1213
  description,
@@ -736,53 +1225,132 @@ var DatePicker = /* @__PURE__ */ react.forwardRef(
736
1225
  id: idProp,
737
1226
  name,
738
1227
  className,
1228
+ mode = "single",
739
1229
  value: valueProp,
740
1230
  defaultValue,
741
1231
  onChange,
742
- format = "yyyy-MM-dd",
1232
+ format = "dd-MMM-yyyy",
743
1233
  minDate,
744
1234
  maxDate,
745
1235
  disabledDates,
1236
+ isDateUnavailable,
746
1237
  firstDayOfWeek = 0,
747
1238
  locale,
748
1239
  showWeekNumbers,
749
1240
  showToday = true,
750
1241
  showClear,
1242
+ defaultView,
1243
+ precision = "day",
1244
+ fixedWeeks = true,
1245
+ numberOfMonths,
1246
+ calendarWidth,
1247
+ showTime = false,
1248
+ timeStep = 5,
1249
+ defaultMonth,
1250
+ presets,
751
1251
  inline = false,
752
1252
  allowInput = true,
753
- closeOnSelect = true,
1253
+ closeOnSelect: closeOnSelectProp,
1254
+ open: openProp,
1255
+ defaultOpen = false,
1256
+ onOpenChange,
754
1257
  placeholder
755
1258
  } = props;
756
1259
  const reactId = react.useId();
757
1260
  const id = idProp ?? reactId;
758
1261
  const panelId = `${id}-cal`;
1262
+ const withTimePicker = showTime && mode === "single";
1263
+ const closeOnSelect = closeOnSelectProp ?? !withTimePicker;
1264
+ const fmt = withTimePicker && format === "dd-MMM-yyyy" ? "dd-MMM-yyyy HH:mm" : format;
759
1265
  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);
1266
+ const [innerRaw, setInnerRaw] = react.useState(() => defaultValue ?? null);
1267
+ const raw = isControlled ? valueProp : innerRaw;
1268
+ const toDateKeepTime = (v) => {
1269
+ if (v == null) return null;
1270
+ const d = new Date(v);
1271
+ return Number.isNaN(d.getTime()) ? null : d;
1272
+ };
1273
+ const curDate = mode === "single" ? withTimePicker ? toDateKeepTime(raw) : toDate(raw) : null;
1274
+ const curRange = mode === "range" ? normRange(raw) : { start: null, end: null };
1275
+ const curDates = mode === "multiple" ? normDates(raw) : [];
1276
+ const commit = (next) => {
1277
+ if (!isControlled) {
1278
+ setInnerRaw(next);
1279
+ }
1280
+ onChange?.(next);
1281
+ };
1282
+ const openIsControlled = openProp !== void 0;
1283
+ const [innerOpen, setInnerOpen] = react.useState(defaultOpen);
1284
+ const open = openIsControlled ? openProp : innerOpen;
1285
+ const setOpen = (next) => {
1286
+ if (!openIsControlled) setInnerOpen(next);
1287
+ onOpenChange?.(next);
1288
+ };
763
1289
  const [gridFocus, setGridFocus] = react.useState(false);
764
- const [text, setText] = react.useState(() => value ? formatDate(value, format, locale) : "");
1290
+ const [hoverDate, setHoverDate] = react.useState(null);
1291
+ const displayText = () => {
1292
+ if (mode === "single") return curDate ? formatDate(curDate, fmt, locale) : "";
1293
+ if (mode === "multiple") {
1294
+ if (curDates.length === 0) return "";
1295
+ if (curDates.length === 1) return formatDate(curDates[0], fmt, locale);
1296
+ return `${curDates.length} selected`;
1297
+ }
1298
+ if (!curRange.start && !curRange.end) return "";
1299
+ const s = curRange.start ? formatDate(curRange.start, fmt, locale) : "\u2026";
1300
+ const e = curRange.end ? formatDate(curRange.end, fmt, locale) : "\u2026";
1301
+ return `${s} \u2013 ${e}`;
1302
+ };
1303
+ const [text, setText] = react.useState(displayText);
765
1304
  const boxRef = react.useRef(null);
766
1305
  const inputRef = react.useRef(null);
1306
+ const skipFocusOpen = react.useRef(false);
1307
+ const valueKey = [
1308
+ mode,
1309
+ typeof fmt === "string" ? fmt : JSON.stringify(fmt),
1310
+ locale ?? "",
1311
+ curDate?.getTime() ?? "",
1312
+ curRange.start?.getTime() ?? "",
1313
+ curRange.end?.getTime() ?? "",
1314
+ curDates.map((d) => d.getTime()).join(",")
1315
+ ].join("|");
767
1316
  react.useEffect(() => {
768
- setText(value ? formatDate(value, format, locale) : "");
769
- }, [value, format, locale]);
1317
+ setText(displayText());
1318
+ }, [valueKey]);
770
1319
  const isDisabledDate = (d) => {
1320
+ if (isDateUnavailable?.(d)) return true;
771
1321
  if (Array.isArray(disabledDates))
772
1322
  return disabledDates.some((x) => isSameDay(startOfDay(x), d));
773
1323
  return Boolean(disabledDates?.(d));
774
1324
  };
775
- const commit = (d) => {
776
- if (!isControlled) setInner(d);
777
- onChange?.(d);
1325
+ const closeAndRefocus = () => {
1326
+ setOpen(false);
1327
+ setGridFocus(false);
1328
+ skipFocusOpen.current = true;
1329
+ inputRef.current?.focus();
778
1330
  };
779
1331
  const select = (d) => {
780
1332
  if (isDisabledDate(d)) return;
781
- commit(startOfDay(d));
782
- if (closeOnSelect) {
783
- setOpen(false);
784
- inputRef.current?.focus();
1333
+ const day = startOfDay(d);
1334
+ if (mode === "single") {
1335
+ const picked = withTimePicker ? withTime(day, curDate ? timeParts(curDate) : { h: 0, m: 0, s: 0 }) : day;
1336
+ commit(picked);
1337
+ if (closeOnSelect) closeAndRefocus();
1338
+ return;
785
1339
  }
1340
+ if (mode === "multiple") {
1341
+ const has = curDates.some((x) => isSameDay(x, day));
1342
+ commit(has ? curDates.filter((x) => !isSameDay(x, day)) : [...curDates, day]);
1343
+ return;
1344
+ }
1345
+ const next = nextRange(curRange, day);
1346
+ commit(next);
1347
+ if (isRangeComplete(next) && closeOnSelect) closeAndRefocus();
1348
+ };
1349
+ const clearValue = () => {
1350
+ if (mode === "single") commit(null);
1351
+ else if (mode === "multiple") commit([]);
1352
+ else commit({ start: null, end: null });
1353
+ onClear?.();
786
1354
  };
787
1355
  const onKeyDown = (e) => {
788
1356
  if (disabled || readOnly) return;
@@ -793,31 +1361,103 @@ var DatePicker = /* @__PURE__ */ react.forwardRef(
793
1361
  } else if (e.key === "Escape" && open) {
794
1362
  e.preventDefault();
795
1363
  setOpen(false);
796
- } else if (e.key === "Enter") {
797
- const parsed = parseDate(text, format);
1364
+ } else if (e.key === "Enter" && mode === "single") {
1365
+ const parsed = parseDate(text, fmt, locale);
798
1366
  if (parsed && !isDisabledDate(startOfDay(parsed))) select(parsed);
799
1367
  }
800
1368
  };
801
- const cal = /* @__PURE__ */ jsxRuntime.jsx(
1369
+ const preview = mode === "range" && curRange.start && !curRange.end ? previewRange(curRange.start, hoverDate) : null;
1370
+ const applyPreset = (p) => {
1371
+ const resolved = p.getValue ? p.getValue() : p.value;
1372
+ if (resolved == null) return;
1373
+ commit(resolved);
1374
+ if (closeOnSelect && !inline) closeAndRefocus();
1375
+ };
1376
+ const setTimeField = (part, n) => {
1377
+ const base3 = curDate ?? startOfDay(/* @__PURE__ */ new Date());
1378
+ const t = timeParts(base3);
1379
+ commit(withTime(base3, { ...t, [part]: n, s: 0 }));
1380
+ };
1381
+ const timeColumn = withTimePicker ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "orynn-datepicker__time", children: [
1382
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-datepicker__time-label", children: "Time" }),
1383
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "orynn-datepicker__time-fields", children: [
1384
+ /* @__PURE__ */ jsxRuntime.jsx(
1385
+ "select",
1386
+ {
1387
+ "aria-label": "Hour",
1388
+ className: "orynn-datepicker__time-select",
1389
+ value: curDate ? curDate.getHours() : 0,
1390
+ onChange: (e) => setTimeField("h", Number(e.target.value)),
1391
+ children: Array.from({ length: 24 }, (_, h) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: h, children: pad2(h) }, h))
1392
+ }
1393
+ ),
1394
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: ":" }),
1395
+ /* @__PURE__ */ jsxRuntime.jsx(
1396
+ "select",
1397
+ {
1398
+ "aria-label": "Minute",
1399
+ className: "orynn-datepicker__time-select",
1400
+ value: curDate ? curDate.getMinutes() - curDate.getMinutes() % timeStep : 0,
1401
+ onChange: (e) => setTimeField("m", Number(e.target.value)),
1402
+ children: minuteOptions(timeStep).map((m) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: m, children: pad2(m) }, m))
1403
+ }
1404
+ )
1405
+ ] })
1406
+ ] }) : null;
1407
+ const calendarStyle = calendarWidth != null ? {
1408
+ "--orynn-calendar-min-width": typeof calendarWidth === "number" ? `${calendarWidth}px` : calendarWidth
1409
+ } : void 0;
1410
+ const hasExtras = (numberOfMonths ?? 1) > 1 || (presets?.length ?? 0) > 0 || withTimePicker;
1411
+ const fillWidth = inline || !hasExtras;
1412
+ const calendar = /* @__PURE__ */ jsxRuntime.jsx(
802
1413
  Calendar,
803
1414
  {
804
- value,
1415
+ value: curDate,
805
1416
  onSelect: select,
1417
+ style: calendarStyle,
1418
+ fillWidth,
1419
+ mode,
1420
+ selectedDates: mode === "multiple" ? curDates : void 0,
1421
+ range: mode === "range" ? curRange : void 0,
1422
+ previewRange: preview,
1423
+ onHoverDate: mode === "range" ? setHoverDate : void 0,
806
1424
  minDate,
807
1425
  maxDate,
808
1426
  isDisabled: isDisabledDate,
809
1427
  firstDayOfWeek,
810
1428
  locale,
811
1429
  showWeekNumbers,
812
- showToday,
1430
+ showToday: showToday && mode === "single",
813
1431
  showClear,
1432
+ defaultView,
1433
+ precision,
1434
+ fixedWeeks,
1435
+ numberOfMonths,
1436
+ defaultMonth,
814
1437
  onClear: () => {
815
- commit(null);
1438
+ clearValue();
816
1439
  setOpen(false);
817
1440
  },
818
1441
  autoFocus: open && gridFocus || inline
819
1442
  }
820
1443
  );
1444
+ const body = timeColumn ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "orynn-datepicker__withtime", children: [
1445
+ calendar,
1446
+ timeColumn
1447
+ ] }) : calendar;
1448
+ const cal = presets && presets.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "orynn-datepicker__panel", children: [
1449
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "orynn-datepicker__presets", children: presets.map((p) => /* @__PURE__ */ jsxRuntime.jsx(
1450
+ "button",
1451
+ {
1452
+ type: "button",
1453
+ className: "orynn-datepicker__preset",
1454
+ onClick: () => applyPreset(p),
1455
+ children: p.label
1456
+ },
1457
+ p.label
1458
+ )) }),
1459
+ body
1460
+ ] }) : body;
821
1461
  if (inline) {
822
1462
  return /* @__PURE__ */ jsxRuntime.jsx(
823
1463
  Field,
@@ -834,7 +1474,8 @@ var DatePicker = /* @__PURE__ */ react.forwardRef(
834
1474
  }
835
1475
  );
836
1476
  }
837
- const hasValue = value != null;
1477
+ const hasValue = mode === "single" ? curDate != null : mode === "multiple" ? curDates.length > 0 : Boolean(curRange.start || curRange.end);
1478
+ const typingAllowed = mode === "single" && allowInput && !readOnly;
838
1479
  return /* @__PURE__ */ jsxRuntime.jsx(
839
1480
  Field,
840
1481
  {
@@ -869,10 +1510,7 @@ var DatePicker = /* @__PURE__ */ react.forwardRef(
869
1510
  className: "orynn-box__iconbtn",
870
1511
  "aria-label": "Clear",
871
1512
  tabIndex: -1,
872
- onClick: () => {
873
- commit(null);
874
- onClear?.();
875
- },
1513
+ onClick: clearValue,
876
1514
  children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, {})
877
1515
  }
878
1516
  ),
@@ -888,7 +1526,7 @@ var DatePicker = /* @__PURE__ */ react.forwardRef(
888
1526
  onClick: () => {
889
1527
  if (disabled || readOnly) return;
890
1528
  setGridFocus(true);
891
- setOpen((o) => !o);
1529
+ setOpen(!open);
892
1530
  },
893
1531
  children: /* @__PURE__ */ jsxRuntime.jsx(CalendarIcon, {})
894
1532
  }
@@ -907,7 +1545,7 @@ var DatePicker = /* @__PURE__ */ react.forwardRef(
907
1545
  name,
908
1546
  className: "orynn-box__field",
909
1547
  type: "text",
910
- inputMode: "numeric",
1548
+ inputMode: typingAllowed ? "numeric" : void 0,
911
1549
  autoComplete: "off",
912
1550
  role: "combobox",
913
1551
  "aria-expanded": open,
@@ -917,19 +1555,26 @@ var DatePicker = /* @__PURE__ */ react.forwardRef(
917
1555
  "aria-describedby": describedById,
918
1556
  value: text,
919
1557
  disabled,
920
- readOnly: !allowInput || readOnly,
1558
+ readOnly: !typingAllowed,
921
1559
  required,
922
- placeholder: floatingLabel ? void 0 : placeholder ?? format.toLowerCase(),
1560
+ placeholder: floatingLabel ? void 0 : placeholder ?? (typeof fmt === "string" ? fmt.toLowerCase() : void 0),
923
1561
  onChange: (e) => setText(e.target.value),
924
1562
  onKeyDown,
925
- onFocus: () => setOpen(true),
1563
+ onFocus: () => {
1564
+ if (skipFocusOpen.current) {
1565
+ skipFocusOpen.current = false;
1566
+ return;
1567
+ }
1568
+ setOpen(true);
1569
+ },
926
1570
  onBlur: () => {
927
- const parsed = parseDate(text, format);
1571
+ if (mode !== "single") return;
1572
+ const parsed = parseDate(text, fmt, locale);
928
1573
  if (parsed && !isDisabledDate(startOfDay(parsed))) {
929
1574
  commit(startOfDay(parsed));
930
- setText(formatDate(startOfDay(parsed), format, locale));
1575
+ setText(formatDate(startOfDay(parsed), fmt, locale));
931
1576
  } else {
932
- setText(value ? formatDate(value, format, locale) : "");
1577
+ setText(curDate ? formatDate(curDate, fmt, locale) : "");
933
1578
  }
934
1579
  }
935
1580
  }
@@ -945,7 +1590,7 @@ var DatePicker = /* @__PURE__ */ react.forwardRef(
945
1590
  setOpen(false);
946
1591
  setGridFocus(false);
947
1592
  },
948
- matchWidth: false,
1593
+ matchWidth: fillWidth,
949
1594
  id: panelId,
950
1595
  role: "dialog",
951
1596
  children: cal
@@ -956,7 +1601,13 @@ var DatePicker = /* @__PURE__ */ react.forwardRef(
956
1601
  );
957
1602
  }
958
1603
  );
1604
+ var CREATE_VALUE = "\0orynn-create";
959
1605
  var toArray = (v) => v == null ? [] : Array.isArray(v) ? v : [v];
1606
+ var isGrouped = (o) => {
1607
+ const first = o[0];
1608
+ return first != null && "items" in first && Array.isArray(first.items);
1609
+ };
1610
+ var flattenOptions = (o) => isGrouped(o) ? o.flatMap((g) => g.items.map((it) => ({ ...it, group: it.group ?? g.group }))) : o;
960
1611
  function Highlight({ text, query }) {
961
1612
  if (!query) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: text });
962
1613
  const i = text.toLowerCase().indexOf(query.toLowerCase());
@@ -998,52 +1649,103 @@ var Dropdown = /* @__PURE__ */ react.forwardRef(
998
1649
  highlightMatch,
999
1650
  renderOption,
1000
1651
  renderValue,
1652
+ renderGroupLabel,
1001
1653
  placeholder,
1002
1654
  emptyMessage = "No results",
1655
+ loadingMessage = "Loading\u2026",
1003
1656
  closeOnSelect = !multiple,
1004
1657
  maxSelectedLabels,
1658
+ maxValues,
1659
+ hidePickedOptions = false,
1660
+ showSelectAll = false,
1005
1661
  native = false,
1006
- startContent
1662
+ startContent,
1663
+ creatable = false,
1664
+ onCreate,
1665
+ isValidNewOption,
1666
+ formatCreateLabel,
1667
+ createPosition = "last",
1668
+ onSearch,
1669
+ searchDebounce = 0,
1670
+ open: openProp,
1671
+ defaultOpen = false,
1672
+ onOpenChange,
1673
+ maxHeight
1007
1674
  } = props;
1008
1675
  const reactId = react.useId();
1009
1676
  const id = idProp ?? reactId;
1010
1677
  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;
1678
+ const baseOptions = react.useMemo(() => flattenOptions(options), [options]);
1679
+ const [createdOptions, setCreatedOptions] = react.useState([]);
1680
+ const allOptions = react.useMemo(
1681
+ () => [...baseOptions, ...createdOptions],
1682
+ [baseOptions, createdOptions]
1683
+ );
1684
+ const [selectedValues, setSelectedValues] = useControllableState({
1685
+ value: valueProp === void 0 ? void 0 : toArray(valueProp),
1686
+ defaultValue: toArray(defaultValue),
1687
+ onChange: void 0
1688
+ });
1014
1689
  const commit = (next) => {
1015
- if (!isControlled) setInner(next);
1690
+ setSelectedValues(next);
1016
1691
  onChange?.(multiple ? next : next[0] ?? null);
1017
1692
  };
1018
- const [open, setOpen] = react.useState(false);
1693
+ const openIsControlled = openProp !== void 0;
1694
+ const [innerOpen, setInnerOpen] = react.useState(defaultOpen);
1695
+ const open = openIsControlled ? openProp : innerOpen;
1696
+ const setOpen = (next) => {
1697
+ if (!openIsControlled) setInnerOpen(next);
1698
+ onOpenChange?.(next);
1699
+ };
1019
1700
  const [query, setQuery] = react.useState("");
1020
1701
  const [activeIndex, setActiveIndex] = react.useState(0);
1021
1702
  const boxRef = react.useRef(null);
1022
1703
  const inputRef = react.useRef(null);
1023
1704
  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);
1705
+ const byValue = react.useMemo(() => new Map(allOptions.map((o) => [o.value, o])), [allOptions]);
1706
+ const selectedOptions = selectedValues.map((v) => byValue.get(v) ?? { label: v, value: v }).filter(Boolean);
1707
+ react.useEffect(() => {
1708
+ if (!onSearch) return;
1709
+ const t = setTimeout(() => onSearch(query), searchDebounce);
1710
+ return () => clearTimeout(t);
1711
+ }, [query, onSearch, searchDebounce]);
1026
1712
  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]);
1713
+ let list = allOptions;
1714
+ if (searchable && query && !onSearch) {
1715
+ const q = query.toLowerCase();
1716
+ const match = filterFn ?? ((o) => filterMode === "startsWith" ? o.label.toLowerCase().startsWith(q) : o.label.toLowerCase().includes(q));
1717
+ list = list.filter((o) => match(o, query));
1718
+ }
1719
+ if (multiple && hidePickedOptions) {
1720
+ list = list.filter((o) => !selectedValues.includes(o.value));
1721
+ }
1722
+ return list;
1723
+ }, [
1724
+ allOptions,
1725
+ searchable,
1726
+ query,
1727
+ onSearch,
1728
+ filterMode,
1729
+ filterFn,
1730
+ multiple,
1731
+ hidePickedOptions,
1732
+ selectedValues
1733
+ ]);
1734
+ const showCreateRow = creatable && searchable && query.trim().length > 0 && (isValidNewOption ? isValidNewOption(query.trim(), allOptions) : !allOptions.some((o) => o.label.toLowerCase() === query.trim().toLowerCase()));
1735
+ const createRow = { value: CREATE_VALUE, label: query.trim() };
1736
+ const selectableOptions = showCreateRow ? createPosition === "first" ? [createRow, ...filtered] : [...filtered, createRow] : filtered;
1032
1737
  const rows = react.useMemo(() => {
1033
1738
  const out = [];
1034
1739
  let seenGroup;
1035
- let optionIndex = 0;
1036
- for (const o of filtered) {
1037
- if (o.group && o.group !== seenGroup) {
1740
+ selectableOptions.forEach((o, optionIndex) => {
1741
+ if (o.value !== CREATE_VALUE && o.group && o.group !== seenGroup) {
1038
1742
  seenGroup = o.group;
1039
1743
  out.push({ type: "group", label: o.group });
1040
1744
  }
1041
1745
  out.push({ type: "option", option: o, optionIndex });
1042
- optionIndex += 1;
1043
- }
1746
+ });
1044
1747
  return out;
1045
- }, [filtered]);
1046
- const selectableOptions = filtered;
1748
+ }, [selectableOptions]);
1047
1749
  react.useEffect(() => {
1048
1750
  setActiveIndex((i) => Math.min(Math.max(0, i), Math.max(0, selectableOptions.length - 1)));
1049
1751
  }, [selectableOptions.length]);
@@ -1064,10 +1766,34 @@ var Dropdown = /* @__PURE__ */ react.forwardRef(
1064
1766
  setActiveIndex(firstRelevantIndex());
1065
1767
  setOpen(true);
1066
1768
  };
1769
+ const handleCreate = (raw) => {
1770
+ const input = raw.trim();
1771
+ if (!input) return;
1772
+ onCreate?.(input);
1773
+ setCreatedOptions(
1774
+ (prev) => prev.some((o) => o.value === input) || baseOptions.some((o) => o.value === input) ? prev : [...prev, { label: input, value: input }]
1775
+ );
1776
+ if (multiple) {
1777
+ if (maxValues != null && selectedValues.length >= maxValues) return;
1778
+ commit([...selectedValues, input]);
1779
+ } else {
1780
+ commit([input]);
1781
+ }
1782
+ setQuery("");
1783
+ if (closeOnSelect) {
1784
+ setOpen(false);
1785
+ inputRef.current?.focus();
1786
+ }
1787
+ };
1067
1788
  const pick = (opt) => {
1789
+ if (opt.value === CREATE_VALUE) {
1790
+ handleCreate(opt.label);
1791
+ return;
1792
+ }
1068
1793
  if (opt.disabled) return;
1069
1794
  if (multiple) {
1070
1795
  const has = selectedValues.includes(opt.value);
1796
+ if (!has && maxValues != null && selectedValues.length >= maxValues) return;
1071
1797
  commit(
1072
1798
  has ? selectedValues.filter((v) => v !== opt.value) : [...selectedValues, opt.value]
1073
1799
  );
@@ -1085,6 +1811,16 @@ var Dropdown = /* @__PURE__ */ react.forwardRef(
1085
1811
  onClear?.();
1086
1812
  setQuery("");
1087
1813
  };
1814
+ const selectAllTargets = filtered.filter((o) => !o.disabled).map((o) => o.value);
1815
+ const allSelected = selectAllTargets.length > 0 && selectAllTargets.every((v) => selectedValues.includes(v));
1816
+ const toggleSelectAll = () => {
1817
+ if (allSelected) {
1818
+ commit(selectedValues.filter((v) => !selectAllTargets.includes(v)));
1819
+ } else {
1820
+ const merged = Array.from(/* @__PURE__ */ new Set([...selectedValues, ...selectAllTargets]));
1821
+ commit(maxValues != null ? merged.slice(0, maxValues) : merged);
1822
+ }
1823
+ };
1088
1824
  const onKeyDown = (e) => {
1089
1825
  if (disabled || readOnly) return;
1090
1826
  switch (e.key) {
@@ -1149,6 +1885,7 @@ var Dropdown = /* @__PURE__ */ react.forwardRef(
1149
1885
  };
1150
1886
  const hasValue = selectedValues.length > 0;
1151
1887
  const doHighlight = highlightMatch ?? searchable;
1888
+ const isLoadingEmpty = Boolean(loading) && selectableOptions.length === 0;
1152
1889
  if (native && !multiple && !searchable) {
1153
1890
  return /* @__PURE__ */ jsxRuntime.jsx(
1154
1891
  Field,
@@ -1175,7 +1912,7 @@ var Dropdown = /* @__PURE__ */ react.forwardRef(
1175
1912
  onChange: (e) => commit(e.target.value ? [e.target.value] : []),
1176
1913
  children: [
1177
1914
  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))
1915
+ baseOptions.map((o) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: o.value, disabled: o.disabled, children: o.label }, o.value))
1179
1916
  ]
1180
1917
  }
1181
1918
  )
@@ -1262,7 +1999,6 @@ var Dropdown = /* @__PURE__ */ react.forwardRef(
1262
1999
  {
1263
2000
  ...ref ? { ref: setRefs(ref, inputRef) } : { ref: inputRef },
1264
2001
  id,
1265
- name,
1266
2002
  className: "orynn-select__search",
1267
2003
  role: "combobox",
1268
2004
  "aria-expanded": open,
@@ -1274,7 +2010,11 @@ var Dropdown = /* @__PURE__ */ react.forwardRef(
1274
2010
  autoComplete: "off",
1275
2011
  readOnly: !searchable || readOnly,
1276
2012
  disabled,
1277
- placeholder: !hasValue && (searchable || !floatingLabel) ? placeholder : void 0,
2013
+ placeholder: (
2014
+ // only the search input shows the placeholder; when not
2015
+ // searchable the sibling span owns it (avoids a double render)
2016
+ !hasValue && searchable ? placeholder : void 0
2017
+ ),
1278
2018
  value: query,
1279
2019
  size: 1,
1280
2020
  onChange: (e) => {
@@ -1284,7 +2024,8 @@ var Dropdown = /* @__PURE__ */ react.forwardRef(
1284
2024
  onKeyDown,
1285
2025
  onFocus: openPanel
1286
2026
  }
1287
- )
2027
+ ),
2028
+ 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
2029
  ] })
1289
2030
  }
1290
2031
  ),
@@ -1295,44 +2036,61 @@ var Dropdown = /* @__PURE__ */ react.forwardRef(
1295
2036
  anchorRef: boxRef,
1296
2037
  onClose: () => setOpen(false),
1297
2038
  popoverRef: listRef,
1298
- children: /* @__PURE__ */ jsxRuntime.jsx(
2039
+ maxHeight,
2040
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
1299
2041
  "div",
1300
2042
  {
1301
2043
  className: "orynn-listbox",
1302
2044
  id: listId,
1303
2045
  role: "listbox",
1304
2046
  "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
2047
+ children: [
2048
+ multiple && showSelectAll && selectAllTargets.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "orynn-listbox__action", onClick: toggleSelectAll, children: allSelected ? "Clear all" : "Select all" }),
2049
+ 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(
2050
+ (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(
2051
+ "div",
2052
+ {
2053
+ id: `${id}-opt-${CREATE_VALUE}`,
2054
+ role: "option",
2055
+ "aria-selected": false,
2056
+ className: "orynn-option orynn-option--create",
2057
+ "data-active": row.optionIndex === activeIndex || void 0,
2058
+ onMouseEnter: () => setActiveIndex(row.optionIndex),
2059
+ onClick: () => pick(row.option),
2060
+ 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}"` }) })
2061
+ },
2062
+ CREATE_VALUE
2063
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
2064
+ "div",
2065
+ {
2066
+ id: `${id}-opt-${row.option.value}`,
2067
+ role: "option",
2068
+ "aria-selected": selectedValues.includes(row.option.value),
2069
+ "aria-disabled": row.option.disabled || void 0,
2070
+ className: "orynn-option",
2071
+ "data-active": row.optionIndex === activeIndex || void 0,
2072
+ "data-selected": selectedValues.includes(row.option.value) || void 0,
2073
+ "data-disabled": row.option.disabled || void 0,
2074
+ onMouseEnter: () => setActiveIndex(row.optionIndex),
2075
+ onClick: () => pick(row.option),
2076
+ children: renderOption ? renderOption(row.option, {
2077
+ selected: selectedValues.includes(row.option.value),
2078
+ active: row.optionIndex === activeIndex,
2079
+ disabled: Boolean(row.option.disabled),
2080
+ query
2081
+ }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2082
+ row.option.icon,
2083
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "orynn-option__body", children: [
2084
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-option__label", children: doHighlight ? /* @__PURE__ */ jsxRuntime.jsx(Highlight, { text: row.option.label, query }) : row.option.label }),
2085
+ row.option.description != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-option__desc", children: row.option.description })
2086
+ ] }),
2087
+ selectedValues.includes(row.option.value) && /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { className: "orynn-option__check" })
2088
+ ] })
2089
+ },
2090
+ row.option.value
2091
+ )
1334
2092
  )
1335
- )
2093
+ ]
1336
2094
  }
1337
2095
  )
1338
2096
  }
@@ -1380,8 +2138,14 @@ var Input = /* @__PURE__ */ react.forwardRef(
1380
2138
  type = "text",
1381
2139
  prefix,
1382
2140
  suffix,
2141
+ addonBefore,
2142
+ addonAfter,
1383
2143
  passwordToggle,
2144
+ passwordVisible,
2145
+ onPasswordVisibleChange,
2146
+ visibilityToggleIcon,
1384
2147
  showCount,
2148
+ debounce,
1385
2149
  maxLength,
1386
2150
  placeholder,
1387
2151
  ...rest
@@ -1393,14 +2157,32 @@ var Input = /* @__PURE__ */ react.forwardRef(
1393
2157
  defaultValue: defaultValue ?? "",
1394
2158
  onChange: void 0
1395
2159
  });
1396
- const [reveal, setReveal] = react.useState(false);
2160
+ const revealIsControlled = passwordVisible !== void 0;
2161
+ const [innerReveal, setInnerReveal] = react.useState(false);
2162
+ const reveal = revealIsControlled ? passwordVisible : innerReveal;
2163
+ const setReveal = (next) => {
2164
+ if (!revealIsControlled) setInnerReveal(next);
2165
+ onPasswordVisibleChange?.(next);
2166
+ };
2167
+ const debounceTimer = react.useRef();
2168
+ react.useEffect(() => () => clearTimeout(debounceTimer.current), []);
2169
+ const emitChange = (v, e) => {
2170
+ if (debounce && debounce > 0) {
2171
+ clearTimeout(debounceTimer.current);
2172
+ debounceTimer.current = setTimeout(() => onChange?.(v, e), debounce);
2173
+ } else {
2174
+ onChange?.(v, e);
2175
+ }
2176
+ };
1397
2177
  const showToggle = (passwordToggle ?? type === "password") && !disabled && !readOnly;
1398
2178
  const effectiveType = type === "password" && reveal ? "text" : type;
1399
2179
  const hasValue = value.length > 0;
1400
2180
  const showClear = clearable && hasValue && !disabled && !readOnly;
2181
+ const countMax = showCount && typeof showCount === "object" ? showCount.max ?? maxLength : maxLength;
2182
+ const countNode = showCount && typeof showCount === "object" && showCount.formatter ? showCount.formatter(value.length, countMax) : `${value.length}${countMax ? `/${countMax}` : ""}`;
1401
2183
  const handleChange = (e) => {
1402
2184
  setValue(e.target.value);
1403
- onChange?.(e.target.value, e);
2185
+ emitChange(e.target.value, e);
1404
2186
  };
1405
2187
  const handleKeyDown = (e) => {
1406
2188
  if (e.key === "Enter") onEnter?.(value);
@@ -1432,16 +2214,15 @@ var Input = /* @__PURE__ */ react.forwardRef(
1432
2214
  invalid,
1433
2215
  valid,
1434
2216
  active: hasValue,
2217
+ addonBefore,
2218
+ addonAfter,
1435
2219
  left: startContent != null || prefix != null ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1436
2220
  startContent,
1437
2221
  prefix != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-box__adornment--affix", children: prefix })
1438
2222
  ] }) : void 0,
1439
2223
  right: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1440
2224
  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
- ] }),
2225
+ showCount && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-box__adornment--affix", children: countNode }),
1445
2226
  showClear && /* @__PURE__ */ jsxRuntime.jsx(
1446
2227
  "button",
1447
2228
  {
@@ -1461,8 +2242,8 @@ var Input = /* @__PURE__ */ react.forwardRef(
1461
2242
  "aria-label": reveal ? "Hide" : "Show",
1462
2243
  "aria-pressed": reveal,
1463
2244
  tabIndex: -1,
1464
- onClick: () => setReveal((v) => !v),
1465
- children: reveal ? /* @__PURE__ */ jsxRuntime.jsx(EyeOffIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(EyeIcon, {})
2245
+ onClick: () => setReveal(!reveal),
2246
+ children: visibilityToggleIcon ? visibilityToggleIcon(reveal) : reveal ? /* @__PURE__ */ jsxRuntime.jsx(EyeOffIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(EyeIcon, {})
1466
2247
  }
1467
2248
  ),
1468
2249
  loading && /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, {}),
@@ -1499,6 +2280,11 @@ var Input = /* @__PURE__ */ react.forwardRef(
1499
2280
  );
1500
2281
  var clamp = (n, min, max) => Math.min(max ?? Number.POSITIVE_INFINITY, Math.max(min ?? Number.NEGATIVE_INFINITY, n));
1501
2282
  var round = (n, p) => p == null ? n : Math.round(n * 10 ** p) / 10 ** p;
2283
+ var SENTINEL_GROUP = "[[g]]";
2284
+ var SENTINEL_DECIMAL = "[[d]]";
2285
+ function swapSeparators(out, seps, thousandSeparator, decimalSeparator) {
2286
+ 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);
2287
+ }
1502
2288
  var NumberField = /* @__PURE__ */ react.forwardRef(
1503
2289
  function NumberField2(props, ref) {
1504
2290
  const {
@@ -1521,19 +2307,37 @@ var NumberField = /* @__PURE__ */ react.forwardRef(
1521
2307
  value: valueProp,
1522
2308
  defaultValue = null,
1523
2309
  onChange,
2310
+ onValueChange,
1524
2311
  min,
1525
2312
  max,
1526
2313
  step = 1,
1527
2314
  shiftStep,
2315
+ smallStep,
1528
2316
  precision,
2317
+ decimalScale,
2318
+ fixedDecimalScale,
1529
2319
  buttons = "stacked",
2320
+ hideControls,
1530
2321
  grouping,
2322
+ thousandSeparator,
2323
+ decimalSeparator,
1531
2324
  locale,
2325
+ style,
1532
2326
  currency,
2327
+ unit,
2328
+ formatOptions,
2329
+ format: formatFn,
2330
+ parse: parseFn,
1533
2331
  prefix,
1534
2332
  suffix,
1535
- clampOnBlur = true,
2333
+ allowNegative = true,
2334
+ clampBehavior,
2335
+ clampOnBlur,
2336
+ emptyValue = null,
1536
2337
  allowMouseWheel,
2338
+ isWheelDisabled,
2339
+ stepHoldDelay = 300,
2340
+ stepHoldInterval = 60,
1537
2341
  placeholder,
1538
2342
  ...rest
1539
2343
  } = props;
@@ -1544,15 +2348,45 @@ var NumberField = /* @__PURE__ */ react.forwardRef(
1544
2348
  defaultValue,
1545
2349
  onChange: void 0
1546
2350
  });
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
- );
2351
+ const resolvedStyle = style ?? (currency ? "currency" : "decimal");
2352
+ const scale = decimalScale ?? precision;
2353
+ const clampMode = clampBehavior ?? (clampOnBlur === false ? "none" : "blur");
2354
+ const effMin = allowNegative ? min : Math.max(0, min ?? 0);
2355
+ const showButtons = hideControls ? false : buttons;
2356
+ const wheelEnabled = isWheelDisabled != null ? !isWheelDisabled : Boolean(allowMouseWheel);
2357
+ const fmt = react.useMemo(() => {
2358
+ if (formatOptions) return new Intl.NumberFormat(locale, formatOptions);
2359
+ const opts = {
2360
+ useGrouping: grouping ?? (thousandSeparator != null || resolvedStyle === "currency")
2361
+ };
2362
+ if (resolvedStyle === "currency") {
2363
+ opts.style = "currency";
2364
+ opts.currency = currency ?? "USD";
2365
+ } else if (resolvedStyle === "percent") {
2366
+ opts.style = "percent";
2367
+ } else if (resolvedStyle === "unit" && unit) {
2368
+ opts.style = "unit";
2369
+ opts.unit = unit;
2370
+ }
2371
+ if (scale != null) {
2372
+ opts.maximumFractionDigits = scale;
2373
+ if (fixedDecimalScale) opts.minimumFractionDigits = scale;
2374
+ } else {
2375
+ opts.maximumFractionDigits = resolvedStyle === "currency" ? 2 : 20;
2376
+ opts.minimumFractionDigits = resolvedStyle === "currency" ? 2 : 0;
2377
+ }
2378
+ return new Intl.NumberFormat(locale, opts);
2379
+ }, [
2380
+ locale,
2381
+ formatOptions,
2382
+ grouping,
2383
+ thousandSeparator,
2384
+ resolvedStyle,
2385
+ currency,
2386
+ unit,
2387
+ scale,
2388
+ fixedDecimalScale
2389
+ ]);
1556
2390
  const seps = react.useMemo(() => {
1557
2391
  const parts = new Intl.NumberFormat(locale).formatToParts(11111.1);
1558
2392
  return {
@@ -1561,23 +2395,47 @@ var NumberField = /* @__PURE__ */ react.forwardRef(
1561
2395
  };
1562
2396
  }, [locale]);
1563
2397
  const display = react.useCallback(
1564
- (n) => n == null || Number.isNaN(n) ? "" : `${prefix ?? ""}${fmt.format(n)}${suffix ?? ""}`,
1565
- [fmt, prefix, suffix]
2398
+ (n) => {
2399
+ if (n == null || Number.isNaN(n)) return "";
2400
+ if (formatFn) return formatFn(n);
2401
+ let out = fmt.format(n);
2402
+ if (thousandSeparator != null || decimalSeparator != null) {
2403
+ out = swapSeparators(out, seps, thousandSeparator, decimalSeparator);
2404
+ }
2405
+ return `${prefix ?? ""}${out}${suffix ?? ""}`;
2406
+ },
2407
+ [fmt, formatFn, prefix, suffix, seps, thousandSeparator, decimalSeparator]
1566
2408
  );
1567
2409
  const parse = react.useCallback(
1568
2410
  (raw) => {
2411
+ if (parseFn) return parseFn(raw);
1569
2412
  let s = raw;
1570
2413
  if (prefix) s = s.split(prefix).join("");
1571
2414
  if (suffix) s = s.split(suffix).join("");
1572
- s = s.split(seps.group).join("").split(seps.decimal).join(".");
2415
+ const grp = thousandSeparator ?? seps.group;
2416
+ const dec = decimalSeparator ?? seps.decimal;
2417
+ s = s.split(grp).join("").split(dec).join(".");
1573
2418
  s = s.replace(/[^\d.\-]/g, "");
2419
+ if (!allowNegative) s = s.replace(/-/g, "");
1574
2420
  if (s === "" || s === "-" || s === ".") return null;
1575
2421
  const first = s.indexOf(".");
1576
2422
  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;
2423
+ let n = Number(s);
2424
+ if (Number.isNaN(n)) return null;
2425
+ if (resolvedStyle === "percent" && !formatFn) n = n / 100;
2426
+ return n;
1579
2427
  },
1580
- [prefix, suffix, seps]
2428
+ [
2429
+ parseFn,
2430
+ prefix,
2431
+ suffix,
2432
+ seps,
2433
+ thousandSeparator,
2434
+ decimalSeparator,
2435
+ allowNegative,
2436
+ resolvedStyle,
2437
+ formatFn
2438
+ ]
1581
2439
  );
1582
2440
  const [text, setText] = react.useState(() => display(value));
1583
2441
  const [editing, setEditing] = react.useState(false);
@@ -1587,65 +2445,76 @@ var NumberField = /* @__PURE__ */ react.forwardRef(
1587
2445
  const commit = (n) => {
1588
2446
  setValue(n);
1589
2447
  onChange?.(n);
2448
+ onValueChange?.({ value: n, formatted: display(n) });
1590
2449
  };
1591
2450
  const setNumber = (n) => {
1592
2451
  commit(n);
1593
2452
  setText(display(n));
1594
2453
  };
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));
2454
+ const bump = (dir, mode = "n") => {
2455
+ const amount = mode === "l" ? shiftStep ?? step * 10 : mode === "s" ? smallStep ?? step / 10 : step;
2456
+ const base3 = value ?? (dir > 0 ? effMin ?? 0 : max ?? 0);
2457
+ let next = round(base3 + amount * dir, scale);
2458
+ if (clampMode !== "none") next = clamp(next, effMin, max);
2459
+ else if (!allowNegative && next < 0) next = 0;
2460
+ setNumber(next);
1599
2461
  };
1600
2462
  const innerRef = react.useRef(null);
1601
2463
  react.useEffect(() => {
1602
2464
  const el = innerRef.current;
1603
- if (!el || !allowMouseWheel) return;
2465
+ if (!el || !wheelEnabled) return;
1604
2466
  const onWheel = (e) => {
1605
2467
  if (document.activeElement !== el) return;
1606
2468
  e.preventDefault();
1607
- bump(e.deltaY < 0 ? 1 : -1, e.shiftKey);
2469
+ bump(e.deltaY < 0 ? 1 : -1, e.shiftKey ? "l" : e.altKey ? "s" : "n");
1608
2470
  };
1609
2471
  el.addEventListener("wheel", onWheel, { passive: false });
1610
2472
  return () => el.removeEventListener("wheel", onWheel);
1611
2473
  });
1612
2474
  const onKeyDown = (e) => {
1613
2475
  if (disabled || readOnly) return;
2476
+ const mode = e.shiftKey ? "l" : e.altKey ? "s" : "n";
1614
2477
  if (e.key === "ArrowUp") {
1615
2478
  e.preventDefault();
1616
- bump(1, e.shiftKey);
2479
+ bump(1, mode);
1617
2480
  } else if (e.key === "ArrowDown") {
1618
2481
  e.preventDefault();
1619
- bump(-1, e.shiftKey);
2482
+ bump(-1, mode);
1620
2483
  } else if (e.key === "PageUp") {
1621
2484
  e.preventDefault();
1622
- bump(1, true);
2485
+ bump(1, "l");
1623
2486
  } else if (e.key === "PageDown") {
1624
2487
  e.preventDefault();
1625
- bump(-1, true);
1626
- } else if (e.key === "Home" && min != null) {
2488
+ bump(-1, "l");
2489
+ } else if (e.key === "Home" && effMin != null) {
1627
2490
  e.preventDefault();
1628
- setNumber(min);
2491
+ setNumber(effMin);
1629
2492
  } else if (e.key === "End" && max != null) {
1630
2493
  e.preventDefault();
1631
2494
  setNumber(max);
1632
2495
  }
1633
2496
  };
1634
2497
  const holdTimer = react.useRef();
2498
+ const holdDelay = react.useRef();
1635
2499
  const startHold = (dir) => {
1636
2500
  bump(dir);
1637
2501
  let count = 0;
1638
- holdTimer.current = setInterval(() => {
1639
- count += 1;
1640
- bump(dir, count > 6);
1641
- }, 110);
2502
+ holdDelay.current = setTimeout(() => {
2503
+ holdTimer.current = setInterval(() => {
2504
+ count += 1;
2505
+ bump(dir, count > 6 ? "l" : "n");
2506
+ }, stepHoldInterval);
2507
+ }, stepHoldDelay);
1642
2508
  };
1643
- const stopHold = () => clearInterval(holdTimer.current);
1644
- react.useEffect(() => () => clearInterval(holdTimer.current), []);
1645
- const StepButtons = buttons && /* @__PURE__ */ jsxRuntime.jsxs(
2509
+ const stopHold = () => {
2510
+ clearTimeout(holdDelay.current);
2511
+ clearInterval(holdTimer.current);
2512
+ };
2513
+ react.useEffect(() => stopHold, []);
2514
+ const StepButtons = showButtons && /* @__PURE__ */ jsxRuntime.jsxs(
1646
2515
  "div",
1647
2516
  {
1648
- className: `orynn-number__buttons${buttons === "horizontal" ? " orynn-number__buttons--horizontal" : ""}`,
2517
+ className: `orynn-number__buttons${showButtons === "horizontal" ? " orynn-number__buttons--horizontal" : ""}`,
1649
2518
  children: [
1650
2519
  /* @__PURE__ */ jsxRuntime.jsx(
1651
2520
  "button",
@@ -1658,7 +2527,7 @@ var NumberField = /* @__PURE__ */ react.forwardRef(
1658
2527
  onPointerDown: () => startHold(1),
1659
2528
  onPointerUp: stopHold,
1660
2529
  onPointerLeave: stopHold,
1661
- children: /* @__PURE__ */ jsxRuntime.jsx(PlusIcon, {})
2530
+ children: /* @__PURE__ */ jsxRuntime.jsx(ChevronUpIcon, { className: "orynn-number__caret" })
1662
2531
  }
1663
2532
  ),
1664
2533
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1668,11 +2537,11 @@ var NumberField = /* @__PURE__ */ react.forwardRef(
1668
2537
  className: "orynn-number__btn",
1669
2538
  "aria-label": "Decrement",
1670
2539
  tabIndex: -1,
1671
- disabled: disabled || readOnly || min != null && (value ?? 0) <= min,
2540
+ disabled: disabled || readOnly || effMin != null && (value ?? 0) <= effMin,
1672
2541
  onPointerDown: () => startHold(-1),
1673
2542
  onPointerUp: stopHold,
1674
2543
  onPointerLeave: stopHold,
1675
- children: /* @__PURE__ */ jsxRuntime.jsx(MinusIcon, {})
2544
+ children: /* @__PURE__ */ jsxRuntime.jsx(ChevronDownIcon, { className: "orynn-number__caret" })
1676
2545
  }
1677
2546
  )
1678
2547
  ]
@@ -1723,7 +2592,7 @@ var NumberField = /* @__PURE__ */ react.forwardRef(
1723
2592
  inputMode: "decimal",
1724
2593
  role: "spinbutton",
1725
2594
  "aria-valuenow": value ?? void 0,
1726
- "aria-valuemin": min,
2595
+ "aria-valuemin": effMin,
1727
2596
  "aria-valuemax": max,
1728
2597
  value: text,
1729
2598
  disabled,
@@ -1735,13 +2604,16 @@ var NumberField = /* @__PURE__ */ react.forwardRef(
1735
2604
  onFocus: () => setEditing(true),
1736
2605
  onChange: (e) => {
1737
2606
  setText(e.target.value);
1738
- commit(parse(e.target.value));
2607
+ const parsed = parse(e.target.value);
2608
+ commit(
2609
+ parsed == null ? null : clampMode === "strict" ? clamp(parsed, effMin, max) : parsed
2610
+ );
1739
2611
  },
1740
2612
  onKeyDown,
1741
2613
  onBlur: () => {
1742
2614
  setEditing(false);
1743
2615
  const parsed = parse(text);
1744
- const final = parsed == null ? null : round(clampOnBlur ? clamp(parsed, min, max) : parsed, precision);
2616
+ const final = parsed == null ? emptyValue : round(clampMode !== "none" ? clamp(parsed, effMin, max) : parsed, scale);
1745
2617
  if (final !== value) commit(final);
1746
2618
  setText(display(final));
1747
2619
  }
@@ -1754,7 +2626,7 @@ var NumberField = /* @__PURE__ */ react.forwardRef(
1754
2626
  }
1755
2627
  );
1756
2628
  var RadioGroupContext = react.createContext(null);
1757
- var sizePx2 = { sm: "0.95rem", md: "1.125rem", lg: "1.35rem" };
2629
+ var sizePx2 = { sm: "0.875rem", md: "1rem", lg: "1.125rem" };
1758
2630
  var Radio = /* @__PURE__ */ react.forwardRef(
1759
2631
  function Radio2(props, ref) {
1760
2632
  const group = react.useContext(RadioGroupContext);
@@ -1831,6 +2703,7 @@ function RadioGroup(props) {
1831
2703
  defaultValue = null,
1832
2704
  onChange,
1833
2705
  orientation = "vertical",
2706
+ columns,
1834
2707
  options,
1835
2708
  children,
1836
2709
  className,
@@ -1866,12 +2739,17 @@ function RadioGroup(props) {
1866
2739
  "aria-labelledby": label != null ? `${id}-label` : void 0,
1867
2740
  "aria-required": required || void 0,
1868
2741
  className: "orynn-choice-group",
1869
- "data-orientation": orientation,
2742
+ "data-orientation": columns ? void 0 : orientation,
2743
+ "data-columns": columns ? "" : void 0,
2744
+ style: columns ? { "--orynn-choice-columns": columns } : void 0,
1870
2745
  children: options ? options.map((o) => /* @__PURE__ */ jsxRuntime.jsx(
1871
2746
  Radio,
1872
2747
  {
1873
2748
  value: o.value,
1874
- label: o.label,
2749
+ label: o.icon != null ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2750
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-choice__icon", children: o.icon }),
2751
+ o.label
2752
+ ] }) : o.label,
1875
2753
  description: o.description,
1876
2754
  disabled: o.disabled
1877
2755
  },
@@ -1898,12 +2776,17 @@ var Textarea = /* @__PURE__ */ react.forwardRef(
1898
2776
  floatingLabel,
1899
2777
  clearable,
1900
2778
  onClear,
2779
+ loading,
2780
+ endContent,
1901
2781
  id: idProp,
1902
2782
  name,
1903
2783
  className,
1904
2784
  value: valueProp,
1905
2785
  defaultValue,
1906
2786
  onChange,
2787
+ onEnter,
2788
+ submitOnEnter,
2789
+ onKeyDown,
1907
2790
  autoResize,
1908
2791
  minRows = 3,
1909
2792
  maxRows = 10,
@@ -1928,9 +2811,9 @@ var Textarea = /* @__PURE__ */ react.forwardRef(
1928
2811
  el.style.height = "auto";
1929
2812
  const cs = getComputedStyle(el);
1930
2813
  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;
2814
+ const pad3 = Number.parseFloat(cs.paddingTop) + Number.parseFloat(cs.paddingBottom);
2815
+ const min = line * minRows + pad3;
2816
+ const max = line * maxRows + pad3;
1934
2817
  el.style.height = `${Math.min(Math.max(el.scrollHeight, min), max)}px`;
1935
2818
  el.style.overflowY = el.scrollHeight > max ? "auto" : "hidden";
1936
2819
  }, [autoResize, minRows, maxRows]);
@@ -1939,7 +2822,16 @@ var Textarea = /* @__PURE__ */ react.forwardRef(
1939
2822
  setValue(e.target.value);
1940
2823
  onChange?.(e.target.value, e);
1941
2824
  };
2825
+ const handleKeyDown = (e) => {
2826
+ if (e.key === "Enter" && !e.shiftKey && (onEnter || submitOnEnter)) {
2827
+ if (submitOnEnter) e.preventDefault();
2828
+ onEnter?.(value);
2829
+ }
2830
+ onKeyDown?.(e);
2831
+ };
1942
2832
  const hasValue = value.length > 0;
2833
+ const countMax = showCount && typeof showCount === "object" ? showCount.max ?? maxLength : maxLength;
2834
+ const countNode = showCount && typeof showCount === "object" && showCount.formatter ? showCount.formatter(value.length, countMax) : `${value.length}${countMax ? `/${countMax}` : ""}`;
1943
2835
  return /* @__PURE__ */ jsxRuntime.jsx(
1944
2836
  Field,
1945
2837
  {
@@ -1964,20 +2856,24 @@ var Textarea = /* @__PURE__ */ react.forwardRef(
1964
2856
  valid,
1965
2857
  active: hasValue,
1966
2858
  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,
2859
+ right: clearable && hasValue || loading || endContent != null ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2860
+ clearable && hasValue && !disabled && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
2861
+ "button",
2862
+ {
2863
+ type: "button",
2864
+ className: "orynn-box__iconbtn",
2865
+ "aria-label": "Clear",
2866
+ tabIndex: -1,
2867
+ onClick: () => {
2868
+ setValue("");
2869
+ onClear?.();
2870
+ },
2871
+ children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, {})
2872
+ }
2873
+ ),
2874
+ loading && /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, {}),
2875
+ endContent
2876
+ ] }) : void 0,
1981
2877
  children: [
1982
2878
  /* @__PURE__ */ jsxRuntime.jsx(
1983
2879
  "textarea",
@@ -1997,13 +2893,11 @@ var Textarea = /* @__PURE__ */ react.forwardRef(
1997
2893
  placeholder: floatingLabel ? void 0 : placeholder,
1998
2894
  "aria-invalid": invalid || void 0,
1999
2895
  "aria-describedby": describedById,
2000
- onChange: handleChange
2896
+ onChange: handleChange,
2897
+ onKeyDown: handleKeyDown
2001
2898
  }
2002
2899
  ),
2003
- showCount && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "orynn-box__footer", children: [
2004
- value.length,
2005
- maxLength ? `/${maxLength}` : ""
2006
- ] })
2900
+ showCount && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-box__footer", children: countNode })
2007
2901
  ]
2008
2902
  }
2009
2903
  )
@@ -2060,24 +2954,63 @@ function registerField(type, registration) {
2060
2954
 
2061
2955
  // src/layout/resolve-columns.ts
2062
2956
  var DEFAULT_COLUMNS = { desktop: 3, tablet: 2, mobile: 1 };
2957
+ var DEFAULT_MIN_COL = 192;
2063
2958
  var clampCount = (n, fallback) => typeof n === "number" && Number.isFinite(n) && n >= 1 ? Math.floor(n) : fallback;
2064
- function resolveColumns(partial) {
2959
+ function resolveColumns(input) {
2960
+ if (typeof input === "number") {
2961
+ const n = clampCount(input, DEFAULT_COLUMNS.desktop);
2962
+ return { desktop: n, tablet: n, mobile: n };
2963
+ }
2065
2964
  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)
2965
+ desktop: clampCount(input?.desktop, DEFAULT_COLUMNS.desktop),
2966
+ tablet: clampCount(input?.tablet, DEFAULT_COLUMNS.tablet),
2967
+ mobile: clampCount(input?.mobile, DEFAULT_COLUMNS.mobile)
2069
2968
  };
2070
2969
  }
2970
+ function resolveGrid(width, cols, minColWidth = DEFAULT_MIN_COL) {
2971
+ const { desktop: d, tablet: t, mobile: m } = cols;
2972
+ if (!width || !Number.isFinite(width) || width <= 0) {
2973
+ return { count: d, tier: "desktop" };
2974
+ }
2975
+ const per = minColWidth > 0 ? minColWidth : DEFAULT_MIN_COL;
2976
+ const maxFit = Math.max(1, Math.floor(width / per));
2977
+ if (d === t && t === m) {
2978
+ const count = Math.min(d, maxFit);
2979
+ return { count, tier: count >= d ? "desktop" : count > 1 ? "tablet" : "mobile" };
2980
+ }
2981
+ if (maxFit >= d) return { count: d, tier: "desktop" };
2982
+ if (t < d && maxFit >= t) return { count: t, tier: "tablet" };
2983
+ if (maxFit >= m) return { count: m, tier: "mobile" };
2984
+ return { count: Math.min(m, maxFit), tier: "mobile" };
2985
+ }
2071
2986
  function clampSpan(span, columnCount) {
2072
2987
  const n = clampCount(span, 1);
2073
2988
  return Math.min(n, Math.max(columnCount, 1));
2074
2989
  }
2990
+ function pickSpan(layout, tier) {
2991
+ if (!layout) return 1;
2992
+ const v = layout[tier] ?? layout.desktop ?? layout.tablet ?? layout.mobile;
2993
+ return typeof v === "number" && Number.isFinite(v) && v >= 1 ? Math.floor(v) : 1;
2994
+ }
2075
2995
 
2076
2996
  // src/core/config/normalize.ts
2077
2997
  function normalizeGap(gap) {
2078
2998
  if (gap == null) return void 0;
2079
2999
  return typeof gap === "number" ? `${gap}px` : gap;
2080
3000
  }
3001
+ function normalizeMinColWidth(value) {
3002
+ if (typeof value === "number") return value > 0 ? value : DEFAULT_MIN_COL;
3003
+ if (typeof value === "string") {
3004
+ const n = Number.parseFloat(value);
3005
+ if (!Number.isFinite(n) || n <= 0) return DEFAULT_MIN_COL;
3006
+ if (/rem|em/i.test(value)) {
3007
+ const root = typeof document !== "undefined" ? Number.parseFloat(getComputedStyle(document.documentElement).fontSize) || 16 : 16;
3008
+ return n * root;
3009
+ }
3010
+ return n;
3011
+ }
3012
+ return DEFAULT_MIN_COL;
3013
+ }
2081
3014
  function normalizeConfig(config, registry2) {
2082
3015
  invariant(
2083
3016
  config != null && typeof config === "object" && Array.isArray(config.fields),
@@ -2111,18 +3044,52 @@ function normalizeConfig(config, registry2) {
2111
3044
  return {
2112
3045
  fields: config.fields,
2113
3046
  columns: resolveColumns(config.columns),
3047
+ minColWidth: normalizeMinColWidth(config.minColumnWidth),
2114
3048
  gap: normalizeGap(config.gap),
2115
3049
  legend: config.legend,
2116
3050
  id: config.id
2117
3051
  };
2118
3052
  }
2119
3053
 
3054
+ // src/core/config/conditions.ts
3055
+ function evalCondition(when, values) {
3056
+ if (when == null) return true;
3057
+ if (typeof when === "function") return Boolean(when(values));
3058
+ const actual = values[when.field];
3059
+ if ("eq" in when) return actual === when.eq;
3060
+ if ("ne" in when) return actual !== when.ne;
3061
+ if ("in" in when) return Array.isArray(when.in) && when.in.includes(actual);
3062
+ if ("notIn" in when) return Array.isArray(when.notIn) && !when.notIn.includes(actual);
3063
+ if ("truthy" in when) return Boolean(actual) === Boolean(when.truthy);
3064
+ return Boolean(actual);
3065
+ }
3066
+
2120
3067
  // src/core/validation/messages.ts
2121
3068
  var plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
3069
+ var dateStr = (p) => {
3070
+ const d = p instanceof Date ? p : new Date(String(p));
3071
+ return Number.isNaN(d.getTime()) ? String(p) : d.toLocaleDateString();
3072
+ };
2122
3073
  var defaultMessages = {
2123
3074
  required: ({ label }) => `${label} is required`,
2124
3075
  minLength: ({ param, label }) => `${label} must be at least ${plural(param, "character")}`,
2125
- maxLength: ({ param, label }) => `${label} must be at most ${plural(param, "character")}`
3076
+ maxLength: ({ param, label }) => `${label} must be at most ${plural(param, "character")}`,
3077
+ minItems: ({ param, label }) => `Select at least ${plural(param, "option")} for ${label}`,
3078
+ maxItems: ({ param, label }) => `Select at most ${plural(param, "option")} for ${label}`,
3079
+ min: ({ param, label }) => `${label} must be at least ${param}`,
3080
+ max: ({ param, label }) => `${label} must be at most ${param}`,
3081
+ integer: ({ label }) => `${label} must be a whole number`,
3082
+ minDate: ({ param, label }) => `${label} must be on or after ${dateStr(param)}`,
3083
+ maxDate: ({ param, label }) => `${label} must be on or before ${dateStr(param)}`,
3084
+ minRangeDays: ({ param, label }) => `${label} must span at least ${plural(param, "day")}`,
3085
+ maxRangeDays: ({ param, label }) => `${label} must span at most ${plural(param, "day")}`,
3086
+ email: ({ label }) => `${label} must be a valid email address`,
3087
+ url: ({ label }) => `${label} must be a valid URL`,
3088
+ pattern: ({ label }) => `${label} is not in the expected format`,
3089
+ oneOf: ({ label }) => `${label} is not an allowed value`,
3090
+ equals: ({ label }) => `${label} does not match`,
3091
+ validate: ({ label }) => `${label} is invalid`,
3092
+ schema: ({ label }) => `${label} is invalid`
2126
3093
  };
2127
3094
  var FALLBACK_LABEL = "This field";
2128
3095
  function resolveMessage(token, param, label, overrides) {
@@ -2136,6 +3103,50 @@ function resolveMessage(token, param, label, overrides) {
2136
3103
  // src/core/validation/rules.ts
2137
3104
  var isEmpty = (value) => value == null || value === "" || Array.isArray(value) && value.length === 0;
2138
3105
  var asLength = (value) => typeof value === "string" || Array.isArray(value) ? value.length : String(value ?? "").length;
3106
+ var toNumber = (value) => {
3107
+ if (typeof value === "number") return Number.isNaN(value) ? null : value;
3108
+ if (typeof value === "string" && value.trim() !== "") {
3109
+ const n = Number(value);
3110
+ return Number.isNaN(n) ? null : n;
3111
+ }
3112
+ return null;
3113
+ };
3114
+ var toTime = (value) => {
3115
+ if (value == null || value === "") return null;
3116
+ const d = value instanceof Date ? value : new Date(value);
3117
+ const t = d.getTime();
3118
+ return Number.isNaN(t) ? null : t;
3119
+ };
3120
+ var rangeSpanDays = (value) => {
3121
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
3122
+ const { start, end } = value;
3123
+ const s = toTime(start);
3124
+ const e = toTime(end);
3125
+ if (s == null || e == null) return null;
3126
+ return Math.abs(Math.round((e - s) / 864e5)) + 1;
3127
+ };
3128
+ var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
3129
+ var URL_RE = /^(https?:\/\/)[^\s.]+\.\S{2,}$/i;
3130
+ function toRegExp(param) {
3131
+ if (param instanceof RegExp) return param;
3132
+ if (typeof param === "string") {
3133
+ try {
3134
+ return new RegExp(param);
3135
+ } catch {
3136
+ return null;
3137
+ }
3138
+ }
3139
+ if (param && typeof param === "object" && "value" in param) {
3140
+ const { value, flags } = param;
3141
+ if (value instanceof RegExp) return value;
3142
+ try {
3143
+ return new RegExp(value, flags);
3144
+ } catch {
3145
+ return null;
3146
+ }
3147
+ }
3148
+ return null;
3149
+ }
2139
3150
  var builtInRules = {
2140
3151
  required: (value, param) => {
2141
3152
  if (param === false) return null;
@@ -2148,6 +3159,84 @@ var builtInRules = {
2148
3159
  maxLength: (value, param) => {
2149
3160
  if (typeof param !== "number" || isEmpty(value)) return null;
2150
3161
  return asLength(value) > param ? "maxLength" : null;
3162
+ },
3163
+ minItems: (value, param) => {
3164
+ if (typeof param !== "number" || !Array.isArray(value)) return null;
3165
+ return value.length < param ? "minItems" : null;
3166
+ },
3167
+ maxItems: (value, param) => {
3168
+ if (typeof param !== "number" || !Array.isArray(value)) return null;
3169
+ return value.length > param ? "maxItems" : null;
3170
+ },
3171
+ email: (value, param) => {
3172
+ if (param === false || isEmpty(value)) return null;
3173
+ return EMAIL_RE.test(String(value)) ? null : "email";
3174
+ },
3175
+ url: (value, param) => {
3176
+ if (param === false || isEmpty(value)) return null;
3177
+ return URL_RE.test(String(value)) ? null : "url";
3178
+ },
3179
+ pattern: (value, param) => {
3180
+ if (isEmpty(value)) return null;
3181
+ const re = toRegExp(param);
3182
+ if (!re) return null;
3183
+ return re.test(String(value)) ? null : "pattern";
3184
+ },
3185
+ integer: (value, param) => {
3186
+ if (param === false || isEmpty(value)) return null;
3187
+ const n = toNumber(value);
3188
+ return n != null && Number.isInteger(n) ? null : "integer";
3189
+ },
3190
+ min: (value, param) => {
3191
+ if (typeof param !== "number" || isEmpty(value)) return null;
3192
+ const n = toNumber(value);
3193
+ return n != null && n < param ? "min" : null;
3194
+ },
3195
+ max: (value, param) => {
3196
+ if (typeof param !== "number" || isEmpty(value)) return null;
3197
+ const n = toNumber(value);
3198
+ return n != null && n > param ? "max" : null;
3199
+ },
3200
+ minDate: (value, param) => {
3201
+ if (isEmpty(value)) return null;
3202
+ const v = toTime(value);
3203
+ const p = toTime(param);
3204
+ return v != null && p != null && v < p ? "minDate" : null;
3205
+ },
3206
+ maxDate: (value, param) => {
3207
+ if (isEmpty(value)) return null;
3208
+ const v = toTime(value);
3209
+ const p = toTime(param);
3210
+ return v != null && p != null && v > p ? "maxDate" : null;
3211
+ },
3212
+ minRangeDays: (value, param) => {
3213
+ if (typeof param !== "number") return null;
3214
+ const span = rangeSpanDays(value);
3215
+ return span != null && span < param ? "minRangeDays" : null;
3216
+ },
3217
+ maxRangeDays: (value, param) => {
3218
+ if (typeof param !== "number") return null;
3219
+ const span = rangeSpanDays(value);
3220
+ return span != null && span > param ? "maxRangeDays" : null;
3221
+ },
3222
+ oneOf: (value, param) => {
3223
+ if (isEmpty(value) || !Array.isArray(param)) return null;
3224
+ return param.includes(value) ? null : "oneOf";
3225
+ },
3226
+ equals: (value, param, allValues) => {
3227
+ if (isEmpty(value)) return null;
3228
+ const target = param && typeof param === "object" && "field" in param ? allValues[param.field] : param;
3229
+ return value == target ? null : "equals";
3230
+ },
3231
+ validate: (value, param, allValues) => {
3232
+ if (typeof param !== "function") return null;
3233
+ const out = param(value, allValues);
3234
+ if (out instanceof Promise) {
3235
+ return out.then(
3236
+ (r) => typeof r === "string" ? r : r === false ? "validate" : null
3237
+ );
3238
+ }
3239
+ return typeof out === "string" ? out : out === false ? "validate" : null;
2151
3240
  }
2152
3241
  };
2153
3242
  var registry = new Map(Object.entries(builtInRules));
@@ -2161,12 +3250,12 @@ function getRule(name) {
2161
3250
  // src/core/validation/resolver.ts
2162
3251
  function createRuleResolver() {
2163
3252
  return (values, config) => {
2164
- const result = {};
3253
+ const pending = [];
2165
3254
  for (const field of config.fields) {
2166
3255
  const validation = field.validation;
2167
3256
  if (!validation) continue;
3257
+ if (!evalCondition(field.when, values)) continue;
2168
3258
  const value = values[field.name];
2169
- const errors = [];
2170
3259
  for (const key of Object.keys(validation)) {
2171
3260
  if (key === "messages") continue;
2172
3261
  const param = validation[key];
@@ -2178,16 +3267,34 @@ function createRuleResolver() {
2178
3267
  }
2179
3268
  continue;
2180
3269
  }
2181
- const token = rule(value, param, values);
2182
- if (token == null) continue;
2183
- errors.push({
3270
+ pending.push({
3271
+ field: field.name,
2184
3272
  rule: key,
2185
- message: resolveMessage(token, param, field.label, validation.messages)
3273
+ param,
3274
+ label: field.label,
3275
+ overrides: validation.messages,
3276
+ token: rule(value, param, values)
2186
3277
  });
2187
3278
  }
2188
- if (errors.length > 0) result[field.name] = errors;
2189
3279
  }
2190
- return result;
3280
+ const collect = (settled) => {
3281
+ const result = {};
3282
+ pending.forEach((p, i) => {
3283
+ const token = settled[i];
3284
+ if (token == null) return;
3285
+ const bucket = result[p.field] ?? [];
3286
+ bucket.push({
3287
+ rule: p.rule,
3288
+ message: resolveMessage(token, p.param, p.label, p.overrides)
3289
+ });
3290
+ result[p.field] = bucket;
3291
+ });
3292
+ return result;
3293
+ };
3294
+ if (pending.some((p) => p.token instanceof Promise)) {
3295
+ return Promise.all(pending.map((p) => Promise.resolve(p.token))).then(collect);
3296
+ }
3297
+ return collect(pending.map((p) => p.token));
2191
3298
  };
2192
3299
  }
2193
3300
  var defaultResolver = /* @__PURE__ */ createRuleResolver();
@@ -2419,23 +3526,43 @@ function useFieldsetState(config, options = {}) {
2419
3526
  getFieldProps
2420
3527
  };
2421
3528
  }
2422
- function FieldGrid({ columns, gap, className, children }) {
3529
+ var useBrowserLayoutEffect = typeof window !== "undefined" ? react.useLayoutEffect : react.useEffect;
3530
+ var GridContext = react.createContext({ count: 1, tier: "desktop" });
3531
+ function FieldGrid({ columns, minColWidth, gap, className, children }) {
3532
+ const ref = react.useRef(null);
3533
+ const per = minColWidth && minColWidth > 0 ? minColWidth : DEFAULT_MIN_COL;
3534
+ const { desktop, tablet, mobile } = columns;
3535
+ const [grid, setGrid] = react.useState(() => resolveGrid(0, columns, per));
3536
+ useBrowserLayoutEffect(() => {
3537
+ const el = ref.current;
3538
+ if (!el) return;
3539
+ const cols = { desktop, tablet, mobile };
3540
+ const apply = (width) => {
3541
+ const next = resolveGrid(width, cols, per);
3542
+ setGrid((cur) => cur.count === next.count && cur.tier === next.tier ? cur : next);
3543
+ };
3544
+ apply(el.getBoundingClientRect().width);
3545
+ if (typeof ResizeObserver === "undefined") return;
3546
+ const ro = new ResizeObserver((entries) => {
3547
+ for (const entry of entries) {
3548
+ const box = entry.contentBoxSize?.[0];
3549
+ apply(box ? box.inlineSize : entry.contentRect.width);
3550
+ }
3551
+ });
3552
+ ro.observe(el);
3553
+ return () => ro.disconnect();
3554
+ }, [desktop, tablet, mobile, per]);
2423
3555
  const style = {
2424
- "--orynn-cols-desktop": columns.desktop,
2425
- "--orynn-cols-tablet": columns.tablet,
2426
- "--orynn-cols-mobile": columns.mobile,
3556
+ "--orynn-cols": grid.count,
2427
3557
  ...gap ? { "--orynn-grid-gap": gap } : {}
2428
3558
  };
2429
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cx("orynn-grid", className), style, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "orynn-grid__track", children }) });
3559
+ 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
3560
  }
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 });
3561
+ function FieldGridItem({ span, children }) {
3562
+ const { count, tier } = react.useContext(GridContext);
3563
+ const effective = span ? clampSpan(pickSpan(span, tier), count) : 1;
3564
+ if (effective <= 1) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
3565
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "orynn-grid__item", style: { "--orynn-span": effective }, children });
2439
3566
  }
2440
3567
  var noop = () => {
2441
3568
  };
@@ -2445,6 +3572,7 @@ function FieldRendererImpl({ field, registry: registry2, slice, disabled }) {
2445
3572
  registration,
2446
3573
  `No control registered for field type "${field.type}" (field "${field.name}").`
2447
3574
  );
3575
+ const fallbackId = react.useId();
2448
3576
  if (!registration) return null;
2449
3577
  const Control = registration.component;
2450
3578
  const required = field.validation?.required === true;
@@ -2454,6 +3582,36 @@ function FieldRendererImpl({ field, registry: registry2, slice, disabled }) {
2454
3582
  const handleChange = (next) => {
2455
3583
  slice.setValue(registration.formatValue ? registration.formatValue(next) : next);
2456
3584
  };
3585
+ if (registration.selfContained) {
3586
+ return /* @__PURE__ */ jsxRuntime.jsx(
3587
+ "div",
3588
+ {
3589
+ style: { display: "contents" },
3590
+ onBlur: (e) => {
3591
+ if (!e.currentTarget.contains(e.relatedTarget)) slice.markTouched();
3592
+ },
3593
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3594
+ Control,
3595
+ {
3596
+ id: fallbackId,
3597
+ name: field.name,
3598
+ value,
3599
+ onChange: handleChange,
3600
+ onBlur: slice.markTouched,
3601
+ onFocus: noop,
3602
+ disabled: isDisabled,
3603
+ readOnly: field.readOnly === true,
3604
+ required,
3605
+ invalid: messages.length > 0,
3606
+ label: field.label,
3607
+ description: field.description,
3608
+ errors: messages,
3609
+ config: field
3610
+ }
3611
+ )
3612
+ }
3613
+ );
3614
+ }
2457
3615
  return /* @__PURE__ */ jsxRuntime.jsx(
2458
3616
  Field,
2459
3617
  {
@@ -2486,90 +3644,290 @@ var FieldRenderer = /* @__PURE__ */ react.memo(
2486
3644
  FieldRendererImpl,
2487
3645
  (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
3646
  );
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;
3647
+ function FieldsetView({ state, registry: registry2, disabled, className, id }) {
3648
+ const activeRegistry = registry2 ?? defaultRegistry;
3649
+ const { columns, minColWidth, gap, legend, fields, id: configId } = state.config;
3650
+ const visible = fields.filter((f) => evalCondition(f.when, state.values));
3651
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3652
+ "fieldset",
3653
+ {
3654
+ className: cx("orynn-fieldset", className),
3655
+ id: id ?? configId,
3656
+ disabled: disabled || void 0,
3657
+ children: [
3658
+ legend != null && /* @__PURE__ */ jsxRuntime.jsx("legend", { className: "orynn-fieldset__legend", children: legend }),
3659
+ /* @__PURE__ */ jsxRuntime.jsx(FieldGrid, { columns, minColWidth, gap, children: visible.map((field) => /* @__PURE__ */ jsxRuntime.jsx(FieldGridItem, { span: field.layout, children: /* @__PURE__ */ jsxRuntime.jsx(
3660
+ FieldRenderer,
3661
+ {
3662
+ field,
3663
+ registry: activeRegistry,
3664
+ slice: state.getFieldProps(field.name),
3665
+ disabled: disabled === true
3666
+ }
3667
+ ) }, field.name)) })
3668
+ ]
3669
+ }
3670
+ );
3671
+ }
3672
+ var str = (v) => typeof v === "string" ? v : v == null ? "" : String(v);
3673
+ var toISO = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
3674
+ function shell(p) {
3675
+ const c = p.config;
3676
+ return {
3677
+ id: p.id,
3678
+ name: p.name,
3679
+ label: p.label,
3680
+ description: p.description,
3681
+ error: p.errors,
3682
+ required: p.required,
3683
+ disabled: p.disabled,
3684
+ readOnly: p.readOnly,
3685
+ size: c.size,
3686
+ variant: c.variant,
3687
+ floatingLabel: c.floatingLabel
3688
+ };
3689
+ }
3690
+ var InputControl = (p) => {
3691
+ const c = p.config;
2505
3692
  return /* @__PURE__ */ jsxRuntime.jsx(
2506
- "input",
3693
+ Input,
2507
3694
  {
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
3695
+ ...shell(p),
3696
+ type: c.inputType ?? "text",
3697
+ prefix: c.prefix,
3698
+ suffix: c.suffix,
3699
+ addonBefore: c.addonBefore,
3700
+ addonAfter: c.addonAfter,
3701
+ clearable: c.clearable,
3702
+ maxLength: c.maxLength,
3703
+ showCount: c.showCount,
3704
+ debounce: c.debounce,
3705
+ inputMode: c.inputMode,
3706
+ pattern: c.pattern,
3707
+ placeholder: c.placeholder,
3708
+ value: str(p.value),
3709
+ onChange: (v) => p.onChange(v),
3710
+ ...c.props
2523
3711
  }
2524
3712
  );
2525
3713
  };
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",
3714
+ var TextareaControl = (p) => {
3715
+ const c = p.config;
3716
+ return /* @__PURE__ */ jsxRuntime.jsx(
3717
+ Textarea,
2542
3718
  {
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
- ]
3719
+ ...shell(p),
3720
+ minRows: c.rows,
3721
+ maxRows: c.maxRows,
3722
+ autoResize: c.autoResize,
3723
+ resize: c.resize,
3724
+ submitOnEnter: c.submitOnEnter,
3725
+ maxLength: c.maxLength,
3726
+ showCount: c.showCount,
3727
+ placeholder: c.placeholder,
3728
+ value: str(p.value),
3729
+ onChange: (v) => p.onChange(v),
3730
+ ...c.props
2559
3731
  }
2560
3732
  );
2561
3733
  };
2562
- function registerBuiltins(registry2) {
2563
- if (!registry2.has("input")) {
2564
- registry2.register("input", { component: InputControl, defaultValue: "" });
3734
+ var NumberFieldControl = (p) => {
3735
+ const c = p.config;
3736
+ return /* @__PURE__ */ jsxRuntime.jsx(
3737
+ NumberField,
3738
+ {
3739
+ ...shell(p),
3740
+ min: c.min,
3741
+ max: c.max,
3742
+ step: c.step,
3743
+ precision: c.precision,
3744
+ decimalScale: c.decimalScale,
3745
+ currency: c.currency,
3746
+ style: c.style,
3747
+ locale: c.locale,
3748
+ prefix: c.prefix,
3749
+ suffix: c.suffix,
3750
+ thousandSeparator: c.thousandSeparator,
3751
+ allowNegative: c.allowNegative,
3752
+ clampBehavior: c.clampBehavior,
3753
+ hideControls: c.hideControls,
3754
+ placeholder: c.placeholder,
3755
+ value: typeof p.value === "number" ? p.value : null,
3756
+ onChange: (v) => p.onChange(v),
3757
+ ...c.props
3758
+ }
3759
+ );
3760
+ };
3761
+ var DropdownControl = (p) => {
3762
+ const c = p.config;
3763
+ const value = c.multiple ? Array.isArray(p.value) ? p.value : [] : Array.isArray(p.value) ? p.value[0] ?? "" : str(p.value);
3764
+ return /* @__PURE__ */ jsxRuntime.jsx(
3765
+ Dropdown,
3766
+ {
3767
+ ...shell(p),
3768
+ options: c.options,
3769
+ searchable: c.searchable,
3770
+ multiple: c.multiple,
3771
+ clearable: c.clearable,
3772
+ creatable: c.creatable,
3773
+ maxValues: c.maxValues,
3774
+ hidePickedOptions: c.hidePickedOptions,
3775
+ showSelectAll: c.showSelectAll,
3776
+ searchDebounce: c.searchDebounce,
3777
+ placeholder: c.placeholder,
3778
+ value,
3779
+ onChange: (v) => p.onChange(v ?? (c.multiple ? [] : "")),
3780
+ ...c.props
3781
+ }
3782
+ );
3783
+ };
3784
+ var DateControl = (p) => {
3785
+ const c = p.config;
3786
+ const toDate2 = (x) => x == null ? void 0 : x instanceof Date ? x : new Date(x);
3787
+ const mode = c.mode ?? "single";
3788
+ const common = {
3789
+ ...shell(p),
3790
+ format: c.format,
3791
+ precision: c.precision,
3792
+ minDate: toDate2(c.minDate),
3793
+ maxDate: toDate2(c.maxDate),
3794
+ showToday: c.showToday,
3795
+ clearable: c.clearable,
3796
+ placeholder: c.placeholder,
3797
+ presets: c.presets,
3798
+ ...c.props
3799
+ };
3800
+ if (mode === "range") {
3801
+ const v = p.value ?? {};
3802
+ return /* @__PURE__ */ jsxRuntime.jsx(
3803
+ DatePicker,
3804
+ {
3805
+ ...common,
3806
+ mode: "range",
3807
+ value: { start: v.start ?? null, end: v.end ?? null },
3808
+ onChange: (r) => p.onChange({
3809
+ start: r.start ? toISO(r.start) : "",
3810
+ end: r.end ? toISO(r.end) : ""
3811
+ })
3812
+ }
3813
+ );
2565
3814
  }
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
- });
3815
+ if (mode === "multiple") {
3816
+ return /* @__PURE__ */ jsxRuntime.jsx(
3817
+ DatePicker,
3818
+ {
3819
+ ...common,
3820
+ mode: "multiple",
3821
+ value: Array.isArray(p.value) ? p.value : [],
3822
+ onChange: (arr) => p.onChange(arr.map(toISO))
3823
+ }
3824
+ );
2572
3825
  }
3826
+ return /* @__PURE__ */ jsxRuntime.jsx(
3827
+ DatePicker,
3828
+ {
3829
+ ...common,
3830
+ value: typeof p.value === "string" && p.value ? p.value : null,
3831
+ onChange: (d) => p.onChange(d instanceof Date ? toISO(d) : "")
3832
+ }
3833
+ );
3834
+ };
3835
+ var CheckboxControl = (p) => {
3836
+ const c = p.config;
3837
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "orynn-field", children: [
3838
+ /* @__PURE__ */ jsxRuntime.jsx(
3839
+ Checkbox,
3840
+ {
3841
+ id: p.id,
3842
+ name: p.name,
3843
+ label: c.checkboxLabel ?? c.label,
3844
+ description: c.description,
3845
+ checked: Boolean(p.value),
3846
+ onChange: (v) => p.onChange(v),
3847
+ disabled: p.disabled,
3848
+ required: p.required,
3849
+ invalid: (p.errors?.length ?? 0) > 0,
3850
+ ...c.props
3851
+ }
3852
+ ),
3853
+ p.errors?.[0] && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "orynn-field__error", role: "alert", children: p.errors[0] })
3854
+ ] });
3855
+ };
3856
+ var CheckboxGroupControl = (p) => {
3857
+ const c = p.config;
3858
+ return /* @__PURE__ */ jsxRuntime.jsx(
3859
+ CheckboxGroup,
3860
+ {
3861
+ id: p.id,
3862
+ name: p.name,
3863
+ label: p.label,
3864
+ description: p.description,
3865
+ error: p.errors,
3866
+ required: p.required,
3867
+ disabled: p.disabled,
3868
+ options: c.options.map((o) => ({
3869
+ label: o.label,
3870
+ value: o.value,
3871
+ description: o.description,
3872
+ icon: o.icon,
3873
+ disabled: o.disabled
3874
+ })),
3875
+ orientation: c.orientation,
3876
+ columns: c.columns,
3877
+ showSelectAll: c.showSelectAll,
3878
+ min: c.min,
3879
+ max: c.max,
3880
+ value: Array.isArray(p.value) ? p.value : [],
3881
+ onChange: (v) => p.onChange(v),
3882
+ ...c.props
3883
+ }
3884
+ );
3885
+ };
3886
+ var RadioControl = (p) => {
3887
+ const c = p.config;
3888
+ return /* @__PURE__ */ jsxRuntime.jsx(
3889
+ RadioGroup,
3890
+ {
3891
+ id: p.id,
3892
+ name: p.name,
3893
+ label: p.label,
3894
+ description: p.description,
3895
+ error: p.errors,
3896
+ required: p.required,
3897
+ disabled: p.disabled,
3898
+ options: c.options.map((o) => ({
3899
+ label: o.label,
3900
+ value: o.value,
3901
+ description: o.description,
3902
+ icon: o.icon,
3903
+ disabled: o.disabled
3904
+ })),
3905
+ orientation: c.orientation,
3906
+ columns: c.columns,
3907
+ variant: c.radioVariant,
3908
+ value: typeof p.value === "string" && p.value ? p.value : null,
3909
+ onChange: (v) => p.onChange(v),
3910
+ ...c.props
3911
+ }
3912
+ );
3913
+ };
3914
+ var needsOptions = (config) => Array.isArray(config.options) ? null : "requires an `options` array";
3915
+ function registerBuiltins(registry2) {
3916
+ const add = (type, component, extra = {}) => {
3917
+ if (!registry2.has(type)) {
3918
+ registry2.register(type, { component, selfContained: true, defaultValue: "", ...extra });
3919
+ }
3920
+ };
3921
+ add("input", InputControl);
3922
+ add("textarea", TextareaControl);
3923
+ add("number", NumberFieldControl, {
3924
+ parseValue: (raw) => typeof raw === "number" ? raw : raw === "" || raw == null ? null : Number(raw)
3925
+ });
3926
+ add("dropdown", DropdownControl, { validateConfig: needsOptions });
3927
+ add("date", DateControl);
3928
+ add("checkbox", CheckboxControl, { defaultValue: false });
3929
+ add("checkbox-group", CheckboxGroupControl, { defaultValue: [], validateConfig: needsOptions });
3930
+ add("radio", RadioControl, { validateConfig: needsOptions });
2573
3931
  }
2574
3932
  var seeded = false;
2575
3933
  function ensureBuiltins(defaultRegistry2) {
@@ -2603,110 +3961,1605 @@ function Fieldset(props) {
2603
3961
  ...validateOn ? { validateOn } : {}
2604
3962
  };
2605
3963
  const fs = useFieldsetState(config, options);
2606
- const { columns, gap, legend, fields, id: configId } = fs.config;
2607
- return /* @__PURE__ */ jsxRuntime.jsxs(
2608
- "fieldset",
3964
+ return /* @__PURE__ */ jsxRuntime.jsx(
3965
+ FieldsetView,
2609
3966
  {
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
- ]
3967
+ state: fs,
3968
+ registry: activeRegistry,
3969
+ disabled: disabled === true,
3970
+ className,
3971
+ id
2625
3972
  }
2626
3973
  );
2627
3974
  }
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);
3975
+ function Form(props) {
3976
+ const {
3977
+ config,
3978
+ value,
3979
+ defaultValue,
3980
+ onChange,
3981
+ onSubmit,
3982
+ onInvalid,
3983
+ resolver,
3984
+ validateOn,
3985
+ disabled,
3986
+ children,
3987
+ className,
3988
+ ...rest
3989
+ } = props;
3990
+ ensureBuiltins(defaultRegistry);
3991
+ const fs = useFieldsetState(config, {
3992
+ registry: defaultRegistry,
3993
+ ...value !== void 0 ? { value } : {},
3994
+ ...defaultValue !== void 0 ? { defaultValue } : {},
3995
+ ...onChange ? { onChange } : {},
3996
+ ...resolver ? { resolver } : {},
3997
+ ...validateOn ? { validateOn } : {}
3998
+ });
3999
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4000
+ "form",
4001
+ {
4002
+ ...rest,
4003
+ className: cx("orynn-form", className),
4004
+ noValidate: true,
4005
+ onSubmit: fs.handleSubmit(
4006
+ (values) => onSubmit(values, fs),
4007
+ (errors) => onInvalid?.(errors)
4008
+ ),
4009
+ children: [
4010
+ /* @__PURE__ */ jsxRuntime.jsx(FieldsetView, { state: fs, disabled }),
4011
+ typeof children === "function" ? children(fs) : children
4012
+ ]
4013
+ }
4014
+ );
4015
+ }
4016
+ var Accordion = /* @__PURE__ */ react.forwardRef(
4017
+ function Accordion2({ className, variant = "bordered", size = "md", ...props }, ref) {
4018
+ return /* @__PURE__ */ jsxRuntime.jsx(
4019
+ radixUi.Accordion.Root,
4020
+ {
4021
+ ref,
4022
+ "data-slot": "accordion",
4023
+ "data-variant": variant,
4024
+ "data-size": size,
4025
+ className: cx("orynn-accordion", className),
4026
+ ...props
4027
+ }
4028
+ );
4029
+ }
4030
+ );
4031
+ var AccordionItem = /* @__PURE__ */ react.forwardRef(function AccordionItem2({ className, ...props }, ref) {
4032
+ return /* @__PURE__ */ jsxRuntime.jsx(
4033
+ radixUi.Accordion.Item,
4034
+ {
4035
+ ref,
4036
+ "data-slot": "accordion-item",
4037
+ className: cx("orynn-accordion__item", className),
4038
+ ...props
4039
+ }
4040
+ );
4041
+ });
4042
+ var AccordionTrigger = /* @__PURE__ */ react.forwardRef(function AccordionTrigger2({ className, children, icon, iconPosition = "end", hideIcon = false, ...props }, ref) {
4043
+ const indicator = hideIcon ? null : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-accordion__icon", "data-slot": "accordion-icon", "aria-hidden": "true", children: icon ?? /* @__PURE__ */ jsxRuntime.jsx(ChevronDownIcon, {}) });
4044
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Accordion.Header, { className: "orynn-accordion__header", "data-slot": "accordion-header", children: /* @__PURE__ */ jsxRuntime.jsxs(
4045
+ radixUi.Accordion.Trigger,
4046
+ {
4047
+ ref,
4048
+ "data-slot": "accordion-trigger",
4049
+ "data-icon-position": iconPosition,
4050
+ className: cx("orynn-accordion__trigger", className),
4051
+ ...props,
4052
+ children: [
4053
+ iconPosition === "start" && indicator,
4054
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-accordion__label", children }),
4055
+ iconPosition === "end" && indicator
4056
+ ]
4057
+ }
4058
+ ) });
4059
+ });
4060
+ var AccordionContent = /* @__PURE__ */ react.forwardRef(function AccordionContent2({ className, children, ...props }, ref) {
4061
+ return /* @__PURE__ */ jsxRuntime.jsx(
4062
+ radixUi.Accordion.Content,
4063
+ {
4064
+ ref,
4065
+ "data-slot": "accordion-content",
4066
+ className: cx("orynn-accordion__content", className),
4067
+ ...props,
4068
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "orynn-accordion__body", children })
4069
+ }
4070
+ );
4071
+ });
4072
+
4073
+ // src/primitives/shared/classes.ts
4074
+ var mod = (base3, key, fallback) => key === fallback ? void 0 : `${base3}--${key}`;
4075
+ function buttonClasses(variant = "default", size = "default") {
4076
+ return cx(
4077
+ "orynn-button",
4078
+ mod("orynn-button", variant, "default"),
4079
+ mod("orynn-button", size, "default")
4080
+ );
4081
+ }
4082
+ function badgeClasses(variant = "default") {
4083
+ return cx("orynn-badge", mod("orynn-badge", variant, "default"));
4084
+ }
4085
+ var Button = /* @__PURE__ */ react.forwardRef(function Button2({
4086
+ className,
4087
+ variant = "default",
4088
+ size = "default",
4089
+ asChild = false,
4090
+ type,
4091
+ loading = false,
4092
+ loadingText,
4093
+ leftIcon,
4094
+ rightIcon,
4095
+ fullWidth = false,
4096
+ hoverEffect,
4097
+ focusStyle,
4098
+ selected,
4099
+ cursor,
4100
+ disabled,
4101
+ children,
4102
+ ...props
4103
+ }, ref) {
4104
+ const shared = {
4105
+ "data-slot": "button",
4106
+ "data-variant": variant,
4107
+ "data-size": size,
4108
+ "data-loading": loading || void 0,
4109
+ "data-full-width": fullWidth || void 0,
4110
+ "data-hover-effect": hoverEffect,
4111
+ "data-focus-style": focusStyle,
4112
+ "data-selected": selected || void 0,
4113
+ "data-cursor": cursor,
4114
+ "aria-pressed": selected,
4115
+ className: cx(buttonClasses(variant, size), className),
4116
+ ...props
4117
+ };
4118
+ if (asChild) {
4119
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Slot.Root, { ref, "aria-busy": loading || void 0, ...shared, children });
4120
+ }
4121
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4122
+ "button",
4123
+ {
4124
+ ref,
4125
+ type: type ?? "button",
4126
+ disabled: disabled || loading,
4127
+ "aria-busy": loading || void 0,
4128
+ ...shared,
4129
+ children: [
4130
+ loading ? /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, { className: "orynn-button__spinner" }) : leftIcon,
4131
+ loading && loadingText != null ? loadingText : children,
4132
+ !loading && rightIcon
4133
+ ]
4134
+ }
4135
+ );
4136
+ });
4137
+ var toArray2 = (v) => v == null ? void 0 : Array.isArray(v) ? v : [v];
4138
+ var Slider = /* @__PURE__ */ react.forwardRef(function Slider2({
4139
+ className,
4140
+ value,
4141
+ defaultValue,
4142
+ min = 0,
4143
+ max = 100,
4144
+ step = 1,
4145
+ size = "md",
4146
+ marks,
4147
+ showTicks = false,
4148
+ tooltip = "none",
4149
+ formatValue,
4150
+ orientation = "horizontal",
4151
+ onValueChange,
4152
+ ...props
4153
+ }, ref) {
4154
+ const controlled = value !== void 0;
4155
+ const initial = toArray2(value) ?? toArray2(defaultValue) ?? [min];
4156
+ const [internal, setInternal] = react.useState(initial);
4157
+ const current = controlled ? toArray2(value) : internal;
4158
+ const handleChange = (next) => {
4159
+ if (!controlled) setInternal(next);
4160
+ onValueChange?.(next);
4161
+ };
4162
+ const fmt = (n) => formatValue ? formatValue(n) : n;
4163
+ const ticks = react.useMemo(() => {
4164
+ if (!showTicks) return [];
4165
+ const count = Math.floor((max - min) / step);
4166
+ if (count < 1 || count > 60) return [];
4167
+ return Array.from({ length: count + 1 }, (_, i) => min + i * step);
4168
+ }, [showTicks, min, max, step]);
4169
+ const pct = (n) => (n - min) / (max - min) * 100;
4170
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4171
+ radixUi.Slider.Root,
4172
+ {
4173
+ ref,
4174
+ "data-slot": "slider",
4175
+ "data-size": size,
4176
+ "data-orientation": orientation,
4177
+ "data-tooltip": tooltip === "none" ? void 0 : tooltip,
4178
+ className: cx("orynn-slider", className),
4179
+ min,
4180
+ max,
4181
+ step,
4182
+ orientation,
4183
+ onValueChange: handleChange,
4184
+ ...controlled ? { value: current } : { defaultValue: initial },
4185
+ ...props,
4186
+ children: [
4187
+ /* @__PURE__ */ jsxRuntime.jsxs(radixUi.Slider.Track, { "data-slot": "slider-track", className: "orynn-slider__track", children: [
4188
+ /* @__PURE__ */ jsxRuntime.jsx(radixUi.Slider.Range, { "data-slot": "slider-range", className: "orynn-slider__range" }),
4189
+ ticks.map((t) => /* @__PURE__ */ jsxRuntime.jsx(
4190
+ "span",
4191
+ {
4192
+ className: "orynn-slider__tick",
4193
+ "aria-hidden": "true",
4194
+ style: { insetInlineStart: `${pct(t)}%` }
4195
+ },
4196
+ `tick-${t}`
4197
+ ))
4198
+ ] }),
4199
+ current.map((v, i) => /* @__PURE__ */ jsxRuntime.jsx(
4200
+ radixUi.Slider.Thumb,
4201
+ {
4202
+ "data-slot": "slider-thumb",
4203
+ className: "orynn-slider__thumb",
4204
+ "aria-label": props["aria-label"] ?? `Value ${i + 1}`,
4205
+ children: tooltip !== "none" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-slider__tooltip", "data-slot": "slider-tooltip", children: fmt(v) })
4206
+ },
4207
+ `thumb-${i}`
4208
+ )),
4209
+ marks && marks.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "orynn-slider__marks", "aria-hidden": "true", children: marks.map((m) => /* @__PURE__ */ jsxRuntime.jsxs(
4210
+ "span",
4211
+ {
4212
+ className: "orynn-slider__mark",
4213
+ style: { insetInlineStart: `${pct(m.value)}%` },
4214
+ children: [
4215
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-slider__mark-dot" }),
4216
+ m.label != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-slider__mark-label", children: m.label })
4217
+ ]
4218
+ },
4219
+ `mark-${m.value}`
4220
+ )) })
4221
+ ]
4222
+ }
4223
+ );
4224
+ });
4225
+ var CIRCLE_SIZE = { sm: 40, md: 56, lg: 72 };
4226
+ var CIRCLE_STROKE = { sm: 4, md: 5, lg: 6 };
4227
+ var Progress = /* @__PURE__ */ react.forwardRef(function Progress2({
4228
+ className,
4229
+ value,
4230
+ max = 100,
4231
+ shape = "line",
4232
+ size = "md",
4233
+ variant = "default",
4234
+ showValue = false,
4235
+ label,
4236
+ striped = false,
4237
+ animated = false,
4238
+ radius,
4239
+ circleThickness,
4240
+ circleSize,
4241
+ formatValue,
4242
+ style,
4243
+ ...props
4244
+ }, ref) {
4245
+ const indeterminate = value == null;
4246
+ const clamped = indeterminate ? 0 : Math.min(max, Math.max(0, value));
4247
+ const pct = max > 0 ? clamped / max * 100 : 0;
4248
+ const text = formatValue ? formatValue(clamped, max) : `${Math.round(pct)}%`;
4249
+ const shared = {
4250
+ ref,
4251
+ "data-slot": "progress",
4252
+ "data-shape": shape,
4253
+ "data-size": size,
4254
+ "data-variant": variant,
4255
+ "data-indeterminate": indeterminate || void 0,
4256
+ value: indeterminate ? null : clamped,
4257
+ max,
4258
+ ...props
4259
+ };
4260
+ if (shape === "circle") {
4261
+ const dim = circleSize ?? CIRCLE_SIZE[size];
4262
+ const stroke = circleThickness ?? CIRCLE_STROKE[size];
4263
+ const r = (dim - stroke) / 2;
4264
+ const circ = 2 * Math.PI * r;
4265
+ const offset = indeterminate ? circ * 0.7 : circ * (1 - pct / 100);
4266
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4267
+ radixUi.Progress.Root,
4268
+ {
4269
+ ...shared,
4270
+ className: cx("orynn-progress", "orynn-progress--circle", className),
4271
+ style: { inlineSize: dim, blockSize: dim, ...style },
4272
+ children: [
4273
+ /* @__PURE__ */ jsxRuntime.jsxs("svg", { className: "orynn-progress__svg", viewBox: `0 0 ${dim} ${dim}`, "aria-hidden": "true", children: [
4274
+ /* @__PURE__ */ jsxRuntime.jsx(
4275
+ "circle",
4276
+ {
4277
+ className: "orynn-progress__ring-track",
4278
+ cx: dim / 2,
4279
+ cy: dim / 2,
4280
+ r,
4281
+ fill: "none",
4282
+ strokeWidth: stroke
4283
+ }
4284
+ ),
4285
+ /* @__PURE__ */ jsxRuntime.jsx(
4286
+ "circle",
4287
+ {
4288
+ className: "orynn-progress__ring",
4289
+ cx: dim / 2,
4290
+ cy: dim / 2,
4291
+ r,
4292
+ fill: "none",
4293
+ strokeWidth: stroke,
4294
+ strokeLinecap: "round",
4295
+ strokeDasharray: circ,
4296
+ strokeDashoffset: offset
4297
+ }
4298
+ )
4299
+ ] }),
4300
+ showValue && !indeterminate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-progress__circle-value", children: text })
4301
+ ]
4302
+ }
4303
+ );
4304
+ }
4305
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "orynn-progress__wrap", children: [
4306
+ (label != null || showValue && !indeterminate) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "orynn-progress__meta", children: [
4307
+ label != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-progress__label", children: label }),
4308
+ showValue && !indeterminate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-progress__value", children: text })
4309
+ ] }),
4310
+ /* @__PURE__ */ jsxRuntime.jsx(
4311
+ radixUi.Progress.Root,
4312
+ {
4313
+ ...shared,
4314
+ "data-striped": striped || animated || void 0,
4315
+ "data-animated": animated || void 0,
4316
+ className: cx("orynn-progress", className),
4317
+ style: { borderRadius: radius != null ? radius : void 0, ...style },
4318
+ children: /* @__PURE__ */ jsxRuntime.jsx(
4319
+ radixUi.Progress.Indicator,
4320
+ {
4321
+ className: "orynn-progress__bar",
4322
+ "data-slot": "progress-bar",
4323
+ style: {
4324
+ transform: indeterminate ? void 0 : `translateX(-${100 - pct}%)`
4325
+ }
4326
+ }
4327
+ )
4328
+ }
4329
+ )
4330
+ ] });
4331
+ });
4332
+ var toasts = [];
4333
+ var listeners = /* @__PURE__ */ new Set();
4334
+ var counter = 0;
4335
+ var emit = () => {
4336
+ for (const l of listeners) l(toasts);
4337
+ };
4338
+ var subscribe = (l) => {
4339
+ listeners.add(l);
4340
+ return () => {
4341
+ listeners.delete(l);
4342
+ };
4343
+ };
4344
+ var getSnapshot = () => toasts;
4345
+ var upsert = (record) => {
4346
+ const i = toasts.findIndex((t) => t.id === record.id);
4347
+ toasts = i === -1 ? [...toasts, record] : toasts.map((t) => t.id === record.id ? record : t);
4348
+ emit();
4349
+ };
4350
+ var dismissToast = (id) => {
4351
+ toasts = id == null ? [] : toasts.filter((t) => t.id !== id);
4352
+ emit();
4353
+ };
4354
+ var isMessage = (v) => typeof v === "string" || typeof v === "number" || react.isValidElement(v);
4355
+ var normalize = (input, extra) => isMessage(input) ? { title: input, ...extra } : { ...input, ...extra };
4356
+ function base2(input, extra) {
4357
+ const opts = normalize(input, extra);
4358
+ const id = opts.id ?? `toast-${++counter}`;
4359
+ upsert({ ...opts, id, createdAt: Date.now() });
4360
+ return id;
4361
+ }
4362
+ var withVariant = (variant) => (message, opts) => base2(message, { ...opts, variant });
4363
+ var toast = base2;
4364
+ toast.success = withVariant("success");
4365
+ toast.error = withVariant("error");
4366
+ toast.warning = withVariant("warning");
4367
+ toast.info = withVariant("info");
4368
+ toast.loading = withVariant("loading");
4369
+ toast.dismiss = dismissToast;
4370
+ toast.promise = (promise, msgs, opts) => {
4371
+ const id = base2(msgs.loading, {
4372
+ ...opts,
4373
+ variant: "loading",
4374
+ duration: Number.POSITIVE_INFINITY
4375
+ });
4376
+ promise.then(
4377
+ (value) => {
4378
+ const m = typeof msgs.success === "function" ? msgs.success(value) : msgs.success;
4379
+ base2(m, { ...opts, id, variant: "success", duration: opts?.duration });
4380
+ },
4381
+ (err) => {
4382
+ const m = typeof msgs.error === "function" ? msgs.error(err) : msgs.error;
4383
+ base2(m, { ...opts, id, variant: "error", duration: opts?.duration });
4384
+ }
4385
+ );
4386
+ return promise;
4387
+ };
4388
+ function AngleIcon() {
4389
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "1em", height: "1em", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx(
4390
+ "path",
4391
+ {
4392
+ d: "M12 8v5M12 16h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z",
4393
+ stroke: "currentColor",
4394
+ strokeWidth: "2",
4395
+ strokeLinecap: "round",
4396
+ strokeLinejoin: "round"
4397
+ }
4398
+ ) });
4399
+ }
4400
+ function InfoIcon() {
4401
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "1em", height: "1em", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
4402
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "9", stroke: "currentColor", strokeWidth: "2" }),
4403
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 11v5M12 8h.01", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
4404
+ ] });
4405
+ }
4406
+ function CircleXIcon() {
4407
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "1em", height: "1em", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
4408
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "9", stroke: "currentColor", strokeWidth: "2" }),
4409
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m15 9-6 6M9 9l6 6", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
4410
+ ] });
4411
+ }
4412
+ var VARIANT_ICON = {
4413
+ default: null,
4414
+ success: /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, {}),
4415
+ error: /* @__PURE__ */ jsxRuntime.jsx(CircleXIcon, {}),
4416
+ warning: /* @__PURE__ */ jsxRuntime.jsx(AngleIcon, {}),
4417
+ info: /* @__PURE__ */ jsxRuntime.jsx(InfoIcon, {}),
4418
+ loading: /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, { className: "orynn-spin" })
4419
+ };
4420
+ var SWIPE = {
4421
+ "top-left": "left",
4422
+ "top-center": "up",
4423
+ "top-right": "right",
4424
+ "bottom-left": "left",
4425
+ "bottom-center": "down",
4426
+ "bottom-right": "right"
4427
+ };
4428
+ function ToastItem({
4429
+ record,
4430
+ duration,
4431
+ closeButton
4432
+ }) {
4433
+ const variant = record.variant ?? "default";
4434
+ const icon = record.icon === null ? null : record.icon ?? VARIANT_ICON[variant];
4435
+ const showClose = record.dismissible ?? closeButton;
4436
+ const d = record.duration ?? (variant === "loading" ? Number.POSITIVE_INFINITY : duration);
4437
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4438
+ radixUi.Toast.Root,
4439
+ {
4440
+ "data-slot": "toast",
4441
+ "data-variant": variant,
4442
+ className: "orynn-toast",
4443
+ duration: Number.isFinite(d) ? d : 1e9,
4444
+ onOpenChange: (open) => {
4445
+ record.onOpenChange?.(open);
4446
+ if (!open) dismissToast(record.id);
4447
+ },
4448
+ children: [
4449
+ icon != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-toast__icon", "data-slot": "toast-icon", "aria-hidden": "true", children: icon }),
4450
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "orynn-toast__content", children: [
4451
+ record.title != null && /* @__PURE__ */ jsxRuntime.jsx(radixUi.Toast.Title, { className: "orynn-toast__title", children: record.title }),
4452
+ record.description != null && /* @__PURE__ */ jsxRuntime.jsx(radixUi.Toast.Description, { className: "orynn-toast__description", children: record.description })
4453
+ ] }),
4454
+ record.action && /* @__PURE__ */ jsxRuntime.jsx(
4455
+ radixUi.Toast.Action,
4456
+ {
4457
+ className: "orynn-toast__action",
4458
+ altText: record.action.altText ?? (typeof record.action.label === "string" ? record.action.label : "Action"),
4459
+ onClick: record.action.onClick,
4460
+ children: record.action.label
4461
+ }
4462
+ ),
4463
+ showClose && /* @__PURE__ */ jsxRuntime.jsx(
4464
+ radixUi.Toast.Close,
4465
+ {
4466
+ className: "orynn-toast__close",
4467
+ "aria-label": "Dismiss",
4468
+ "data-slot": "toast-close",
4469
+ children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, {})
4470
+ }
4471
+ )
4472
+ ]
4473
+ }
4474
+ );
4475
+ }
4476
+ function Toaster({
4477
+ position = "bottom-right",
4478
+ duration = 4e3,
4479
+ visibleToasts = 4,
4480
+ closeButton = true,
4481
+ richColors = false,
4482
+ gap = 12,
4483
+ offset = 24,
4484
+ className
4485
+ }) {
4486
+ const records = react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
4487
+ const shown = records.slice(-visibleToasts);
4488
+ const [side] = position.split("-");
4489
+ return /* @__PURE__ */ jsxRuntime.jsxs(radixUi.Toast.Provider, { swipeDirection: SWIPE[position], duration, children: [
4490
+ shown.map((record) => /* @__PURE__ */ jsxRuntime.jsx(ToastItem, { record, duration, closeButton }, record.id)),
4491
+ /* @__PURE__ */ jsxRuntime.jsx(
4492
+ radixUi.Toast.Viewport,
4493
+ {
4494
+ "data-slot": "toaster",
4495
+ "data-position": position,
4496
+ "data-rich-colors": richColors || void 0,
4497
+ className: cx("orynn-toaster", className),
4498
+ style: {
4499
+ "--orynn-toaster-gap": `${gap}px`,
4500
+ "--orynn-toaster-offset": `${offset}px`,
4501
+ [side]: `${offset}px`
4502
+ }
4503
+ }
4504
+ )
4505
+ ] });
4506
+ }
4507
+ function useToast() {
4508
+ const toasts2 = react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
4509
+ return { toast, dismiss: dismissToast, toasts: toasts2 };
4510
+ }
4511
+
4512
+ // src/primitives/Pagination/use-pagination.ts
4513
+ var range = (start, end) => Array.from({ length: Math.max(0, end - start + 1) }, (_, i) => start + i);
4514
+ function usePagination({
4515
+ page,
4516
+ count,
4517
+ total,
4518
+ pageSize = 10,
4519
+ siblingCount = 1,
4520
+ boundaryCount = 1
4521
+ }) {
4522
+ const pageCount = Math.max(
4523
+ 1,
4524
+ count ?? (total != null ? Math.ceil(total / Math.max(1, pageSize)) : 1)
4525
+ );
4526
+ const current = Math.min(Math.max(1, page), pageCount);
4527
+ const startPages = range(1, Math.min(boundaryCount, pageCount));
4528
+ const endPages = range(Math.max(pageCount - boundaryCount + 1, boundaryCount + 1), pageCount);
4529
+ const siblingsStart = Math.max(
4530
+ Math.min(current - siblingCount, pageCount - boundaryCount - siblingCount * 2 - 1),
4531
+ boundaryCount + 2
4532
+ );
4533
+ const siblingsEnd = Math.min(
4534
+ Math.max(current + siblingCount, boundaryCount + siblingCount * 2 + 2),
4535
+ endPages.length > 0 ? endPages[0] - 2 : pageCount - 1
4536
+ );
4537
+ const slots = [
4538
+ ...startPages,
4539
+ ...siblingsStart > boundaryCount + 2 ? ["ellipsis-start"] : boundaryCount + 1 < pageCount - boundaryCount ? [boundaryCount + 1] : [],
4540
+ ...range(siblingsStart, siblingsEnd),
4541
+ ...siblingsEnd < pageCount - boundaryCount - 1 ? ["ellipsis-end"] : pageCount - boundaryCount > boundaryCount ? [pageCount - boundaryCount] : [],
4542
+ ...endPages
4543
+ ];
4544
+ return {
4545
+ pageCount,
4546
+ page: current,
4547
+ hasPrev: current > 1,
4548
+ hasNext: current < pageCount,
4549
+ slots
4550
+ };
4551
+ }
4552
+ var PaginationContent = /* @__PURE__ */ react.forwardRef(
4553
+ function PaginationContent2({ className, ...props }, ref) {
4554
+ return /* @__PURE__ */ jsxRuntime.jsx(
4555
+ "ul",
4556
+ {
4557
+ ref,
4558
+ "data-slot": "pagination-content",
4559
+ className: cx("orynn-pagination__list", className),
4560
+ ...props
4561
+ }
4562
+ );
4563
+ }
4564
+ );
4565
+ var PaginationItem = /* @__PURE__ */ react.forwardRef(
4566
+ function PaginationItem2({ className, ...props }, ref) {
4567
+ return /* @__PURE__ */ jsxRuntime.jsx(
4568
+ "li",
4569
+ {
4570
+ ref,
4571
+ "data-slot": "pagination-item",
4572
+ className: cx("orynn-pagination__item", className),
4573
+ ...props
4574
+ }
4575
+ );
4576
+ }
4577
+ );
4578
+ var PaginationLink = /* @__PURE__ */ react.forwardRef(
4579
+ function PaginationLink2({ className, isActive, size = "md", type, ...props }, ref) {
4580
+ return /* @__PURE__ */ jsxRuntime.jsx(
4581
+ "button",
4582
+ {
4583
+ ref,
4584
+ type: type ?? "button",
4585
+ "data-slot": "pagination-link",
4586
+ "data-active": isActive || void 0,
4587
+ "data-size": size,
4588
+ "aria-current": isActive ? "page" : void 0,
4589
+ className: cx("orynn-pagination__link", className),
4590
+ ...props
4591
+ }
4592
+ );
4593
+ }
4594
+ );
4595
+ var PaginationEllipsis = /* @__PURE__ */ react.forwardRef(function PaginationEllipsis2({ className, children, ...props }, ref) {
4596
+ return /* @__PURE__ */ jsxRuntime.jsx(
4597
+ "span",
4598
+ {
4599
+ ref,
4600
+ "aria-hidden": "true",
4601
+ "data-slot": "pagination-ellipsis",
4602
+ className: cx("orynn-pagination__ellipsis", className),
4603
+ ...props,
4604
+ children: children ?? "\u2026"
4605
+ }
4606
+ );
4607
+ });
4608
+ var Pagination = /* @__PURE__ */ react.forwardRef(
4609
+ function Pagination2({
4610
+ className,
4611
+ page,
4612
+ count,
4613
+ total,
4614
+ pageSize,
4615
+ siblingCount,
4616
+ boundaryCount,
4617
+ onPageChange,
4618
+ showFirstLast = false,
4619
+ showPrevNext = true,
4620
+ iconOnly = false,
4621
+ size = "md",
4622
+ variant = "outline",
4623
+ disabled = false,
4624
+ labels,
4625
+ prevIcon,
4626
+ nextIcon,
4627
+ ...props
4628
+ }, ref) {
4629
+ const model = usePagination({ page, count, total, pageSize, siblingCount, boundaryCount });
4630
+ const L = {
4631
+ previous: labels?.previous ?? "Previous",
4632
+ next: labels?.next ?? "Next",
4633
+ first: labels?.first ?? "First page",
4634
+ last: labels?.last ?? "Last page",
4635
+ ariaLabel: labels?.ariaLabel ?? "Pagination",
4636
+ page: labels?.page ?? ((n) => `Page ${n}`)
4637
+ };
4638
+ const go = (p) => {
4639
+ if (disabled) return;
4640
+ const next = Math.min(Math.max(1, p), model.pageCount);
4641
+ if (next !== model.page) onPageChange(next);
4642
+ };
4643
+ return /* @__PURE__ */ jsxRuntime.jsx(
4644
+ "nav",
4645
+ {
4646
+ ref,
4647
+ "aria-label": L.ariaLabel,
4648
+ "data-slot": "pagination",
4649
+ "data-size": size,
4650
+ "data-variant": variant,
4651
+ "data-disabled": disabled || void 0,
4652
+ className: cx("orynn-pagination", className),
4653
+ ...props,
4654
+ children: /* @__PURE__ */ jsxRuntime.jsxs(PaginationContent, { children: [
4655
+ showFirstLast && /* @__PURE__ */ jsxRuntime.jsx(PaginationItem, { children: /* @__PURE__ */ jsxRuntime.jsxs(
4656
+ PaginationLink,
4657
+ {
4658
+ size,
4659
+ "aria-label": L.first,
4660
+ disabled: disabled || !model.hasPrev,
4661
+ onClick: () => go(1),
4662
+ children: [
4663
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronLeftIcon, {}),
4664
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronLeftIcon, { style: { marginInlineStart: "-0.65em" } })
4665
+ ]
4666
+ }
4667
+ ) }),
4668
+ showPrevNext && /* @__PURE__ */ jsxRuntime.jsx(PaginationItem, { children: /* @__PURE__ */ jsxRuntime.jsxs(
4669
+ PaginationLink,
4670
+ {
4671
+ size,
4672
+ "aria-label": L.previous,
4673
+ disabled: disabled || !model.hasPrev,
4674
+ className: "orynn-pagination__nav",
4675
+ onClick: () => go(model.page - 1),
4676
+ children: [
4677
+ prevIcon ?? /* @__PURE__ */ jsxRuntime.jsx(ChevronLeftIcon, {}),
4678
+ !iconOnly && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-pagination__nav-label", children: L.previous })
4679
+ ]
4680
+ }
4681
+ ) }),
4682
+ model.slots.map(
4683
+ (slot) => typeof slot === "number" ? /* @__PURE__ */ jsxRuntime.jsx(PaginationItem, { children: /* @__PURE__ */ jsxRuntime.jsx(
4684
+ PaginationLink,
4685
+ {
4686
+ size,
4687
+ isActive: slot === model.page,
4688
+ "aria-label": L.page(slot),
4689
+ disabled,
4690
+ onClick: () => go(slot),
4691
+ children: slot
4692
+ }
4693
+ ) }, slot) : /* @__PURE__ */ jsxRuntime.jsx(PaginationItem, { children: /* @__PURE__ */ jsxRuntime.jsx(PaginationEllipsis, {}) }, slot)
4694
+ ),
4695
+ showPrevNext && /* @__PURE__ */ jsxRuntime.jsx(PaginationItem, { children: /* @__PURE__ */ jsxRuntime.jsxs(
4696
+ PaginationLink,
4697
+ {
4698
+ size,
4699
+ "aria-label": L.next,
4700
+ disabled: disabled || !model.hasNext,
4701
+ className: "orynn-pagination__nav",
4702
+ onClick: () => go(model.page + 1),
4703
+ children: [
4704
+ !iconOnly && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-pagination__nav-label", children: L.next }),
4705
+ nextIcon ?? /* @__PURE__ */ jsxRuntime.jsx(ChevronRightIcon, {})
4706
+ ]
4707
+ }
4708
+ ) }),
4709
+ showFirstLast && /* @__PURE__ */ jsxRuntime.jsx(PaginationItem, { children: /* @__PURE__ */ jsxRuntime.jsxs(
4710
+ PaginationLink,
4711
+ {
4712
+ size,
4713
+ "aria-label": L.last,
4714
+ disabled: disabled || !model.hasNext,
4715
+ onClick: () => go(model.pageCount),
4716
+ children: [
4717
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronRightIcon, {}),
4718
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronRightIcon, { style: { marginInlineStart: "-0.65em" } })
4719
+ ]
4720
+ }
4721
+ ) })
4722
+ ] })
4723
+ }
4724
+ );
4725
+ }
4726
+ );
4727
+ var Label = /* @__PURE__ */ react.forwardRef(function Label2({ className, required, optional, children, ...props }, ref) {
4728
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4729
+ radixUi.Label.Root,
4730
+ {
4731
+ ref,
4732
+ "data-slot": "label",
4733
+ className: cx("orynn-label", className),
4734
+ ...props,
4735
+ children: [
4736
+ children,
4737
+ required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-label__required", "aria-hidden": "true", children: "*" }),
4738
+ !required && optional && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-label__optional", children: optional === true ? "(optional)" : optional })
4739
+ ]
4740
+ }
4741
+ );
4742
+ });
4743
+ var Card = /* @__PURE__ */ react.forwardRef(function Card2({ className, asChild = false, interactive = false, ...props }, ref) {
4744
+ const shared = {
4745
+ "data-slot": "card",
4746
+ "data-interactive": interactive || void 0,
4747
+ className: cx("orynn-card", className),
4748
+ ...props
4749
+ };
4750
+ return asChild ? /* @__PURE__ */ jsxRuntime.jsx(radixUi.Slot.Root, { ref, ...shared }) : /* @__PURE__ */ jsxRuntime.jsx("div", { ref, ...shared });
4751
+ });
4752
+ var CardHeader = /* @__PURE__ */ react.forwardRef(function CardHeader2({ className, ...props }, ref) {
4753
+ return /* @__PURE__ */ jsxRuntime.jsx(
4754
+ "div",
4755
+ {
4756
+ ref,
4757
+ "data-slot": "card-header",
4758
+ className: cx("orynn-card__header", className),
4759
+ ...props
4760
+ }
4761
+ );
4762
+ });
4763
+ var CardTitle = /* @__PURE__ */ react.forwardRef(function CardTitle2({ className, ...props }, ref) {
4764
+ return /* @__PURE__ */ jsxRuntime.jsx(
4765
+ "div",
4766
+ {
4767
+ ref,
4768
+ "data-slot": "card-title",
4769
+ className: cx("orynn-card__title", className),
4770
+ ...props
4771
+ }
4772
+ );
4773
+ });
4774
+ var CardDescription = /* @__PURE__ */ react.forwardRef(
4775
+ function CardDescription2({ className, ...props }, ref) {
4776
+ return /* @__PURE__ */ jsxRuntime.jsx(
4777
+ "div",
4778
+ {
4779
+ ref,
4780
+ "data-slot": "card-description",
4781
+ className: cx("orynn-card__description", className),
4782
+ ...props
4783
+ }
4784
+ );
4785
+ }
4786
+ );
4787
+ var CardAction = /* @__PURE__ */ react.forwardRef(function CardAction2({ className, ...props }, ref) {
4788
+ return /* @__PURE__ */ jsxRuntime.jsx(
4789
+ "div",
4790
+ {
4791
+ ref,
4792
+ "data-slot": "card-action",
4793
+ className: cx("orynn-card__action", className),
4794
+ ...props
4795
+ }
4796
+ );
4797
+ });
4798
+ var CardContent = /* @__PURE__ */ react.forwardRef(
4799
+ function CardContent2({ className, ...props }, ref) {
4800
+ return /* @__PURE__ */ jsxRuntime.jsx(
4801
+ "div",
4802
+ {
4803
+ ref,
4804
+ "data-slot": "card-content",
4805
+ className: cx("orynn-card__content", className),
4806
+ ...props
4807
+ }
4808
+ );
4809
+ }
4810
+ );
4811
+ var CardFooter = /* @__PURE__ */ react.forwardRef(function CardFooter2({ className, ...props }, ref) {
4812
+ return /* @__PURE__ */ jsxRuntime.jsx(
4813
+ "div",
4814
+ {
4815
+ ref,
4816
+ "data-slot": "card-footer",
4817
+ className: cx("orynn-card__footer", className),
4818
+ ...props
4819
+ }
4820
+ );
4821
+ });
4822
+ var Badge = /* @__PURE__ */ react.forwardRef(function Badge2({
4823
+ className,
4824
+ variant = "default",
4825
+ size = "md",
4826
+ dot = false,
4827
+ icon,
4828
+ removable = false,
4829
+ onRemove,
4830
+ asChild = false,
4831
+ children,
4832
+ ...props
4833
+ }, ref) {
4834
+ const shared = {
4835
+ "data-slot": "badge",
4836
+ "data-variant": variant,
4837
+ "data-size": size,
4838
+ className: cx(badgeClasses(variant), className),
4839
+ ...props
4840
+ };
4841
+ if (asChild) {
4842
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Slot.Root, { ref, ...shared, children });
4843
+ }
4844
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { ref, ...shared, children: [
4845
+ dot && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-badge__dot", "aria-hidden": "true" }),
4846
+ icon,
4847
+ children,
4848
+ removable && /* @__PURE__ */ jsxRuntime.jsx(
4849
+ "button",
4850
+ {
4851
+ type: "button",
4852
+ className: "orynn-badge__remove",
4853
+ "aria-label": "Remove",
4854
+ onClick: onRemove,
4855
+ children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, {})
4856
+ }
4857
+ )
4858
+ ] });
4859
+ });
4860
+ var Alert = /* @__PURE__ */ react.forwardRef(function Alert2({ className, variant = "default", icon, title, dismissible, onDismiss, children, ...props }, ref) {
4861
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4862
+ "div",
4863
+ {
4864
+ ref,
4865
+ "data-slot": "alert",
4866
+ "data-variant": variant,
4867
+ "data-dismissible": dismissible || void 0,
4868
+ role: "alert",
4869
+ className: cx("orynn-alert", variant !== "default" && `orynn-alert--${variant}`, className),
4870
+ ...props,
4871
+ children: [
4872
+ icon,
4873
+ title != null && /* @__PURE__ */ jsxRuntime.jsx(AlertTitle, { children: title }),
4874
+ children,
4875
+ dismissible && /* @__PURE__ */ jsxRuntime.jsx(
4876
+ "button",
4877
+ {
4878
+ type: "button",
4879
+ className: "orynn-alert__dismiss",
4880
+ "aria-label": "Dismiss",
4881
+ onClick: onDismiss,
4882
+ children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, {})
4883
+ }
4884
+ )
4885
+ ]
4886
+ }
4887
+ );
4888
+ });
4889
+ var AlertTitle = /* @__PURE__ */ react.forwardRef(
4890
+ function AlertTitle2({ className, ...props }, ref) {
4891
+ return /* @__PURE__ */ jsxRuntime.jsx(
4892
+ "div",
4893
+ {
4894
+ ref,
4895
+ "data-slot": "alert-title",
4896
+ className: cx("orynn-alert__title", className),
4897
+ ...props
4898
+ }
4899
+ );
2664
4900
  }
2665
- Object.assign(vars, config.vars ?? {});
2666
- return vars;
4901
+ );
4902
+ var AlertDescription = /* @__PURE__ */ react.forwardRef(
4903
+ function AlertDescription2({ className, ...props }, ref) {
4904
+ return /* @__PURE__ */ jsxRuntime.jsx(
4905
+ "div",
4906
+ {
4907
+ ref,
4908
+ "data-slot": "alert-description",
4909
+ className: cx("orynn-alert__description", className),
4910
+ ...props
4911
+ }
4912
+ );
4913
+ }
4914
+ );
4915
+ var Separator = /* @__PURE__ */ react.forwardRef(
4916
+ function Separator2({
4917
+ className,
4918
+ orientation = "horizontal",
4919
+ decorative = true,
4920
+ variant = "solid",
4921
+ children,
4922
+ ...props
4923
+ }, ref) {
4924
+ if (children != null && orientation === "horizontal") {
4925
+ return /* @__PURE__ */ jsxRuntime.jsx(
4926
+ "div",
4927
+ {
4928
+ ref,
4929
+ role: "separator",
4930
+ "aria-orientation": "horizontal",
4931
+ "data-slot": "separator",
4932
+ "data-orientation": "horizontal",
4933
+ "data-variant": variant,
4934
+ className: cx("orynn-separator", "orynn-separator--labelled", className),
4935
+ ...props,
4936
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-separator__label", children })
4937
+ }
4938
+ );
4939
+ }
4940
+ return /* @__PURE__ */ jsxRuntime.jsx(
4941
+ radixUi.Separator.Root,
4942
+ {
4943
+ ref,
4944
+ "data-slot": "separator",
4945
+ "data-variant": variant,
4946
+ orientation,
4947
+ decorative,
4948
+ className: cx("orynn-separator", className),
4949
+ ...props
4950
+ }
4951
+ );
4952
+ }
4953
+ );
4954
+ var Table = /* @__PURE__ */ react.forwardRef(function Table2({ className, size, dense, ...props }, ref) {
4955
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot": "table-container", className: "orynn-table__wrap", children: /* @__PURE__ */ jsxRuntime.jsx(
4956
+ "table",
4957
+ {
4958
+ ref,
4959
+ "data-slot": "table",
4960
+ "data-size": dense ? "sm" : size,
4961
+ className: cx("orynn-table", className),
4962
+ ...props
4963
+ }
4964
+ ) });
4965
+ });
4966
+ var TableHeader = /* @__PURE__ */ react.forwardRef(
4967
+ function TableHeader2({ className, sticky, ...props }, ref) {
4968
+ return /* @__PURE__ */ jsxRuntime.jsx(
4969
+ "thead",
4970
+ {
4971
+ ref,
4972
+ "data-slot": "table-header",
4973
+ "data-sticky": sticky || void 0,
4974
+ className: cx("orynn-table__header", className),
4975
+ ...props
4976
+ }
4977
+ );
4978
+ }
4979
+ );
4980
+ var TableBody = /* @__PURE__ */ react.forwardRef(function TableBody2({ className, ...props }, ref) {
4981
+ return /* @__PURE__ */ jsxRuntime.jsx(
4982
+ "tbody",
4983
+ {
4984
+ ref,
4985
+ "data-slot": "table-body",
4986
+ className: cx("orynn-table__body", className),
4987
+ ...props
4988
+ }
4989
+ );
4990
+ });
4991
+ var TableFooter = /* @__PURE__ */ react.forwardRef(function TableFooter2({ className, ...props }, ref) {
4992
+ return /* @__PURE__ */ jsxRuntime.jsx(
4993
+ "tfoot",
4994
+ {
4995
+ ref,
4996
+ "data-slot": "table-footer",
4997
+ className: cx("orynn-table__footer", className),
4998
+ ...props
4999
+ }
5000
+ );
5001
+ });
5002
+ var TableRow = /* @__PURE__ */ react.forwardRef(
5003
+ function TableRow2({ className, selected, ...props }, ref) {
5004
+ return /* @__PURE__ */ jsxRuntime.jsx(
5005
+ "tr",
5006
+ {
5007
+ ref,
5008
+ "data-slot": "table-row",
5009
+ "data-state": selected ? "selected" : void 0,
5010
+ className: cx("orynn-table__row", className),
5011
+ ...props
5012
+ }
5013
+ );
5014
+ }
5015
+ );
5016
+ var TableHead = /* @__PURE__ */ react.forwardRef(
5017
+ function TableHead2({ className, ...props }, ref) {
5018
+ return /* @__PURE__ */ jsxRuntime.jsx(
5019
+ "th",
5020
+ {
5021
+ ref,
5022
+ "data-slot": "table-head",
5023
+ className: cx("orynn-table__head", className),
5024
+ ...props
5025
+ }
5026
+ );
5027
+ }
5028
+ );
5029
+ var TableCell = /* @__PURE__ */ react.forwardRef(
5030
+ function TableCell2({ className, ...props }, ref) {
5031
+ return /* @__PURE__ */ jsxRuntime.jsx(
5032
+ "td",
5033
+ {
5034
+ ref,
5035
+ "data-slot": "table-cell",
5036
+ className: cx("orynn-table__cell", className),
5037
+ ...props
5038
+ }
5039
+ );
5040
+ }
5041
+ );
5042
+ var TableCaption = /* @__PURE__ */ react.forwardRef(function TableCaption2({ className, ...props }, ref) {
5043
+ return /* @__PURE__ */ jsxRuntime.jsx(
5044
+ "caption",
5045
+ {
5046
+ ref,
5047
+ "data-slot": "table-caption",
5048
+ className: cx("orynn-table__caption", className),
5049
+ ...props
5050
+ }
5051
+ );
5052
+ });
5053
+ var Switch = /* @__PURE__ */ react.forwardRef(function Switch2({
5054
+ className,
5055
+ size = "default",
5056
+ label,
5057
+ description,
5058
+ labelPlacement = "end",
5059
+ onLabel,
5060
+ offLabel,
5061
+ thumbIcon,
5062
+ id: idProp,
5063
+ ...props
5064
+ }, ref) {
5065
+ const reactId = react.useId();
5066
+ const id = idProp ?? reactId;
5067
+ const control = /* @__PURE__ */ jsxRuntime.jsxs(
5068
+ radixUi.Switch.Root,
5069
+ {
5070
+ ref,
5071
+ id,
5072
+ "data-slot": "switch",
5073
+ "data-size": size,
5074
+ className: cx("orynn-switch", className),
5075
+ ...props,
5076
+ children: [
5077
+ offLabel != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-switch__text orynn-switch__text--off", "aria-hidden": "true", children: offLabel }),
5078
+ onLabel != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-switch__text orynn-switch__text--on", "aria-hidden": "true", children: onLabel }),
5079
+ /* @__PURE__ */ jsxRuntime.jsx(radixUi.Switch.Thumb, { "data-slot": "switch-thumb", className: "orynn-switch__thumb", children: thumbIcon })
5080
+ ]
5081
+ }
5082
+ );
5083
+ if (label == null && description == null) return control;
5084
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5085
+ "label",
5086
+ {
5087
+ className: "orynn-switch-field",
5088
+ "data-slot": "switch-field",
5089
+ "data-placement": labelPlacement,
5090
+ htmlFor: id,
5091
+ children: [
5092
+ control,
5093
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "orynn-switch-field__text", children: [
5094
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-switch-field__label", children: label }),
5095
+ description != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-switch-field__description", children: description })
5096
+ ] })
5097
+ ]
5098
+ }
5099
+ );
5100
+ });
5101
+ var Tabs = /* @__PURE__ */ react.forwardRef(function Tabs2({ className, variant = "pill", size = "md", fitted = false, ...props }, ref) {
5102
+ return /* @__PURE__ */ jsxRuntime.jsx(
5103
+ radixUi.Tabs.Root,
5104
+ {
5105
+ ref,
5106
+ "data-slot": "tabs",
5107
+ "data-variant": variant,
5108
+ "data-size": size,
5109
+ "data-fitted": fitted || void 0,
5110
+ className: cx("orynn-tabs", className),
5111
+ ...props
5112
+ }
5113
+ );
5114
+ });
5115
+ var TabsList = /* @__PURE__ */ react.forwardRef(function TabsList2({ className, ...props }, ref) {
5116
+ return /* @__PURE__ */ jsxRuntime.jsx(
5117
+ radixUi.Tabs.List,
5118
+ {
5119
+ ref,
5120
+ "data-slot": "tabs-list",
5121
+ className: cx("orynn-tabs__list", className),
5122
+ ...props
5123
+ }
5124
+ );
5125
+ });
5126
+ var TabsTrigger = /* @__PURE__ */ react.forwardRef(function TabsTrigger2({ className, ...props }, ref) {
5127
+ return /* @__PURE__ */ jsxRuntime.jsx(
5128
+ radixUi.Tabs.Trigger,
5129
+ {
5130
+ ref,
5131
+ "data-slot": "tabs-trigger",
5132
+ className: cx("orynn-tabs__trigger", className),
5133
+ ...props
5134
+ }
5135
+ );
5136
+ });
5137
+ var TabsContent = /* @__PURE__ */ react.forwardRef(function TabsContent2({ className, ...props }, ref) {
5138
+ return /* @__PURE__ */ jsxRuntime.jsx(
5139
+ radixUi.Tabs.Content,
5140
+ {
5141
+ ref,
5142
+ "data-slot": "tabs-content",
5143
+ className: cx("orynn-tabs__content", className),
5144
+ ...props
5145
+ }
5146
+ );
5147
+ });
5148
+ var Dialog = (props) => /* @__PURE__ */ jsxRuntime.jsx(radixUi.Dialog.Root, { "data-slot": "dialog", ...props });
5149
+ var DialogTrigger = (props) => /* @__PURE__ */ jsxRuntime.jsx(radixUi.Dialog.Trigger, { "data-slot": "dialog-trigger", ...props });
5150
+ var DialogClose = (props) => /* @__PURE__ */ jsxRuntime.jsx(radixUi.Dialog.Close, { "data-slot": "dialog-close", ...props });
5151
+ var DialogPortal = radixUi.Dialog.Portal;
5152
+ var DialogContent = /* @__PURE__ */ react.forwardRef(
5153
+ function DialogContent2({ className, children, showCloseButton = true, size = "lg", scrollable, container, ...props }, ref) {
5154
+ const themed = usePortalContainer();
5155
+ return /* @__PURE__ */ jsxRuntime.jsxs(radixUi.Dialog.Portal, { container: container ?? themed ?? void 0, children: [
5156
+ /* @__PURE__ */ jsxRuntime.jsx(
5157
+ radixUi.Dialog.Overlay,
5158
+ {
5159
+ "data-slot": "dialog-overlay",
5160
+ className: "orynn-overlay orynn-dialog__overlay"
5161
+ }
5162
+ ),
5163
+ /* @__PURE__ */ jsxRuntime.jsxs(
5164
+ radixUi.Dialog.Content,
5165
+ {
5166
+ ref,
5167
+ "data-slot": "dialog-content",
5168
+ "data-size": size,
5169
+ "data-scrollable": scrollable || void 0,
5170
+ className: cx("orynn-dialog__content", className),
5171
+ ...props,
5172
+ children: [
5173
+ children,
5174
+ showCloseButton && /* @__PURE__ */ jsxRuntime.jsx(
5175
+ radixUi.Dialog.Close,
5176
+ {
5177
+ "data-slot": "dialog-close",
5178
+ className: "orynn-dialog__close",
5179
+ "aria-label": "Close",
5180
+ children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, {})
5181
+ }
5182
+ )
5183
+ ]
5184
+ }
5185
+ )
5186
+ ] });
5187
+ }
5188
+ );
5189
+ function DialogHeader({ className, ...props }) {
5190
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot": "dialog-header", className: cx("orynn-dialog__header", className), ...props });
2667
5191
  }
2668
- var OrynnContext = react.createContext({
2669
- preset: "default",
2670
- density: "comfortable",
2671
- config: {}
5192
+ function DialogFooter({ className, ...props }) {
5193
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot": "dialog-footer", className: cx("orynn-dialog__footer", className), ...props });
5194
+ }
5195
+ var DialogTitle = /* @__PURE__ */ react.forwardRef(function DialogTitle2({ className, ...props }, ref) {
5196
+ return /* @__PURE__ */ jsxRuntime.jsx(
5197
+ radixUi.Dialog.Title,
5198
+ {
5199
+ ref,
5200
+ "data-slot": "dialog-title",
5201
+ className: cx("orynn-dialog__title", className),
5202
+ ...props
5203
+ }
5204
+ );
2672
5205
  });
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]
5206
+ var DialogDescription = /* @__PURE__ */ react.forwardRef(function DialogDescription2({ className, ...props }, ref) {
5207
+ return /* @__PURE__ */ jsxRuntime.jsx(
5208
+ radixUi.Dialog.Description,
5209
+ {
5210
+ ref,
5211
+ "data-slot": "dialog-description",
5212
+ className: cx("orynn-dialog__description", className),
5213
+ ...props
5214
+ }
5215
+ );
5216
+ });
5217
+ var Popover2 = (props) => /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Root, { "data-slot": "popover", ...props });
5218
+ var PopoverTrigger = (props) => /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Trigger, { "data-slot": "popover-trigger", ...props });
5219
+ var PopoverAnchor = (props) => /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Anchor, { "data-slot": "popover-anchor", ...props });
5220
+ var PopoverClose = radixUi.Popover.Close;
5221
+ var PopoverArrow = (props) => /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Arrow, { "data-slot": "popover-arrow", className: "orynn-popover__arrow", ...props });
5222
+ var PopoverContent = /* @__PURE__ */ react.forwardRef(
5223
+ function PopoverContent2({ className, align = "center", sideOffset = 4, arrow = false, container, children, ...props }, ref) {
5224
+ const themed = usePortalContainer();
5225
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Portal, { container: container ?? themed ?? void 0, children: /* @__PURE__ */ jsxRuntime.jsxs(
5226
+ radixUi.Popover.Content,
5227
+ {
5228
+ ref,
5229
+ "data-slot": "popover-content",
5230
+ align,
5231
+ sideOffset,
5232
+ className: cx("orynn-content orynn-popover__content", className),
5233
+ ...props,
5234
+ children: [
5235
+ children,
5236
+ arrow && /* @__PURE__ */ jsxRuntime.jsx(PopoverArrow, {})
5237
+ ]
5238
+ }
5239
+ ) });
5240
+ }
5241
+ );
5242
+ function TooltipProvider({
5243
+ delayDuration = 0,
5244
+ ...props
5245
+ }) {
5246
+ return /* @__PURE__ */ jsxRuntime.jsx(
5247
+ radixUi.Tooltip.Provider,
5248
+ {
5249
+ "data-slot": "tooltip-provider",
5250
+ delayDuration,
5251
+ ...props
5252
+ }
2681
5253
  );
2682
- const dataProps = {
2683
- "data-orynn-theme": preset === "default" ? void 0 : preset,
2684
- "data-orynn-density": density === "comfortable" ? void 0 : density
2685
- };
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 }) });
2687
5254
  }
2688
- function useOrynnTheme() {
2689
- return react.useContext(OrynnContext);
5255
+ function Tooltip({
5256
+ delayDuration = 0,
5257
+ skipDelayDuration,
5258
+ disableHoverableContent,
5259
+ disableProvider = false,
5260
+ ...props
5261
+ }) {
5262
+ const root = /* @__PURE__ */ jsxRuntime.jsx(
5263
+ radixUi.Tooltip.Root,
5264
+ {
5265
+ "data-slot": "tooltip",
5266
+ delayDuration,
5267
+ disableHoverableContent,
5268
+ ...props
5269
+ }
5270
+ );
5271
+ if (disableProvider) return root;
5272
+ return /* @__PURE__ */ jsxRuntime.jsx(
5273
+ TooltipProvider,
5274
+ {
5275
+ delayDuration,
5276
+ skipDelayDuration,
5277
+ disableHoverableContent,
5278
+ children: root
5279
+ }
5280
+ );
5281
+ }
5282
+ var TooltipTrigger = (props) => /* @__PURE__ */ jsxRuntime.jsx(radixUi.Tooltip.Trigger, { "data-slot": "tooltip-trigger", ...props });
5283
+ var TooltipContent = /* @__PURE__ */ react.forwardRef(
5284
+ function TooltipContent2({ className, sideOffset = 0, arrow = true, arrowSize = 11, container, children, ...props }, ref) {
5285
+ const themed = usePortalContainer();
5286
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Tooltip.Portal, { container: container ?? themed ?? void 0, children: /* @__PURE__ */ jsxRuntime.jsxs(
5287
+ radixUi.Tooltip.Content,
5288
+ {
5289
+ ref,
5290
+ "data-slot": "tooltip-content",
5291
+ sideOffset,
5292
+ className: cx("orynn-content orynn-tooltip__content", className),
5293
+ ...props,
5294
+ children: [
5295
+ children,
5296
+ arrow && /* @__PURE__ */ jsxRuntime.jsx(
5297
+ radixUi.Tooltip.Arrow,
5298
+ {
5299
+ width: arrowSize,
5300
+ height: Math.round(arrowSize / 2.2),
5301
+ className: "orynn-tooltip__arrow"
5302
+ }
5303
+ )
5304
+ ]
5305
+ }
5306
+ ) });
5307
+ }
5308
+ );
5309
+ var DropdownMenu = (props) => /* @__PURE__ */ jsxRuntime.jsx(radixUi.DropdownMenu.Root, { "data-slot": "dropdown-menu", ...props });
5310
+ var DropdownMenuTrigger = (props) => /* @__PURE__ */ jsxRuntime.jsx(radixUi.DropdownMenu.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
5311
+ var DropdownMenuGroup = radixUi.DropdownMenu.Group;
5312
+ var DropdownMenuPortal = radixUi.DropdownMenu.Portal;
5313
+ var DropdownMenuSub = radixUi.DropdownMenu.Sub;
5314
+ var DropdownMenuRadioGroup = radixUi.DropdownMenu.RadioGroup;
5315
+ var DropdownMenuContent = /* @__PURE__ */ react.forwardRef(function DropdownMenuContent2({ className, sideOffset = 4, container, ...props }, ref) {
5316
+ const themed = usePortalContainer();
5317
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.DropdownMenu.Portal, { container: container ?? themed ?? void 0, children: /* @__PURE__ */ jsxRuntime.jsx(
5318
+ radixUi.DropdownMenu.Content,
5319
+ {
5320
+ ref,
5321
+ "data-slot": "dropdown-menu-content",
5322
+ sideOffset,
5323
+ className: cx("orynn-content orynn-menu__content", className),
5324
+ ...props
5325
+ }
5326
+ ) });
5327
+ });
5328
+ var DropdownMenuItem = /* @__PURE__ */ react.forwardRef(
5329
+ function DropdownMenuItem2({ className, inset, variant = "default", icon, shortcut, children, ...props }, ref) {
5330
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5331
+ radixUi.DropdownMenu.Item,
5332
+ {
5333
+ ref,
5334
+ "data-slot": "dropdown-menu-item",
5335
+ "data-inset": inset || void 0,
5336
+ "data-variant": variant,
5337
+ className: cx("orynn-menu__item", className),
5338
+ ...props,
5339
+ children: [
5340
+ icon,
5341
+ children,
5342
+ shortcut != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-menu__shortcut", children: shortcut })
5343
+ ]
5344
+ }
5345
+ );
5346
+ }
5347
+ );
5348
+ var DropdownMenuCheckboxItem = /* @__PURE__ */ react.forwardRef(function DropdownMenuCheckboxItem2({ className, children, checked, ...props }, ref) {
5349
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5350
+ radixUi.DropdownMenu.CheckboxItem,
5351
+ {
5352
+ ref,
5353
+ "data-slot": "dropdown-menu-checkbox-item",
5354
+ className: cx("orynn-menu__item", "orynn-menu__item--check", className),
5355
+ checked,
5356
+ ...props,
5357
+ children: [
5358
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-menu__indicator", children: /* @__PURE__ */ jsxRuntime.jsx(radixUi.DropdownMenu.ItemIndicator, { children: /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, {}) }) }),
5359
+ children
5360
+ ]
5361
+ }
5362
+ );
5363
+ });
5364
+ var DropdownMenuRadioItem = /* @__PURE__ */ react.forwardRef(function DropdownMenuRadioItem2({ className, children, ...props }, ref) {
5365
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5366
+ radixUi.DropdownMenu.RadioItem,
5367
+ {
5368
+ ref,
5369
+ "data-slot": "dropdown-menu-radio-item",
5370
+ className: cx("orynn-menu__item", "orynn-menu__item--check", className),
5371
+ ...props,
5372
+ children: [
5373
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-menu__indicator", children: /* @__PURE__ */ jsxRuntime.jsx(radixUi.DropdownMenu.ItemIndicator, { children: /* @__PURE__ */ jsxRuntime.jsx(CircleIcon, { className: "orynn-menu__dot" }) }) }),
5374
+ children
5375
+ ]
5376
+ }
5377
+ );
5378
+ });
5379
+ var DropdownMenuLabel = /* @__PURE__ */ react.forwardRef(
5380
+ function DropdownMenuLabel2({ className, inset, ...props }, ref) {
5381
+ return /* @__PURE__ */ jsxRuntime.jsx(
5382
+ radixUi.DropdownMenu.Label,
5383
+ {
5384
+ ref,
5385
+ "data-slot": "dropdown-menu-label",
5386
+ "data-inset": inset || void 0,
5387
+ className: cx("orynn-menu__label", className),
5388
+ ...props
5389
+ }
5390
+ );
5391
+ }
5392
+ );
5393
+ var DropdownMenuSeparator = /* @__PURE__ */ react.forwardRef(function DropdownMenuSeparator2({ className, ...props }, ref) {
5394
+ return /* @__PURE__ */ jsxRuntime.jsx(
5395
+ radixUi.DropdownMenu.Separator,
5396
+ {
5397
+ ref,
5398
+ "data-slot": "dropdown-menu-separator",
5399
+ className: cx("orynn-menu__separator", className),
5400
+ ...props
5401
+ }
5402
+ );
5403
+ });
5404
+ function DropdownMenuShortcut({ className, ...props }) {
5405
+ return /* @__PURE__ */ jsxRuntime.jsx(
5406
+ "span",
5407
+ {
5408
+ "data-slot": "dropdown-menu-shortcut",
5409
+ className: cx("orynn-menu__shortcut", className),
5410
+ ...props
5411
+ }
5412
+ );
2690
5413
  }
5414
+ var DropdownMenuSubTrigger = /* @__PURE__ */ react.forwardRef(function DropdownMenuSubTrigger2({ className, inset, children, ...props }, ref) {
5415
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5416
+ radixUi.DropdownMenu.SubTrigger,
5417
+ {
5418
+ ref,
5419
+ "data-slot": "dropdown-menu-sub-trigger",
5420
+ "data-inset": inset || void 0,
5421
+ className: cx("orynn-menu__item", "orynn-menu__sub-trigger", className),
5422
+ ...props,
5423
+ children: [
5424
+ children,
5425
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronRightIcon, { className: "orynn-menu__sub-chevron" })
5426
+ ]
5427
+ }
5428
+ );
5429
+ });
5430
+ var DropdownMenuSubContent = /* @__PURE__ */ react.forwardRef(function DropdownMenuSubContent2({ className, ...props }, ref) {
5431
+ return /* @__PURE__ */ jsxRuntime.jsx(
5432
+ radixUi.DropdownMenu.SubContent,
5433
+ {
5434
+ ref,
5435
+ "data-slot": "dropdown-menu-sub-content",
5436
+ className: cx("orynn-content", "orynn-menu__content", "orynn-menu__sub-content", className),
5437
+ ...props
5438
+ }
5439
+ );
5440
+ });
2691
5441
 
2692
5442
  // src/types/validation.ts
2693
5443
  var FORM_ERROR_KEY = "$form";
2694
5444
 
5445
+ // src/core/validation/standard-schema.ts
5446
+ function pathToField(path) {
5447
+ if (!path || path.length === 0) return { field: FORM_ERROR_KEY, rest: "" };
5448
+ const seg = (p) => String(typeof p === "object" && p !== null && "key" in p ? p.key : p);
5449
+ const parts = path.map(seg);
5450
+ return { field: parts[0] ?? FORM_ERROR_KEY, rest: parts.slice(1).join(".") };
5451
+ }
5452
+ function standardSchemaResolver(schema) {
5453
+ return (values) => {
5454
+ const out = schema["~standard"].validate(values);
5455
+ const toResult = (r) => {
5456
+ const result = {};
5457
+ if (!("issues" in r) || !r.issues) return result;
5458
+ for (const issue of r.issues) {
5459
+ const { field, rest } = pathToField(issue.path);
5460
+ const bucket = result[field] ?? [];
5461
+ bucket.push({ rule: "schema", message: issue.message, ...rest ? { path: rest } : {} });
5462
+ result[field] = bucket;
5463
+ }
5464
+ return result;
5465
+ };
5466
+ return out instanceof Promise ? out.then(toResult) : toResult(out);
5467
+ };
5468
+ }
5469
+
5470
+ exports.Accordion = Accordion;
5471
+ exports.AccordionContent = AccordionContent;
5472
+ exports.AccordionItem = AccordionItem;
5473
+ exports.AccordionTrigger = AccordionTrigger;
5474
+ exports.Alert = Alert;
5475
+ exports.AlertDescription = AlertDescription;
5476
+ exports.AlertTitle = AlertTitle;
5477
+ exports.Badge = Badge;
5478
+ exports.Button = Button;
5479
+ exports.Card = Card;
5480
+ exports.CardAction = CardAction;
5481
+ exports.CardContent = CardContent;
5482
+ exports.CardDescription = CardDescription;
5483
+ exports.CardFooter = CardFooter;
5484
+ exports.CardHeader = CardHeader;
5485
+ exports.CardTitle = CardTitle;
2695
5486
  exports.Checkbox = Checkbox;
2696
5487
  exports.CheckboxGroup = CheckboxGroup;
2697
5488
  exports.DatePicker = DatePicker;
5489
+ exports.Dialog = Dialog;
5490
+ exports.DialogClose = DialogClose;
5491
+ exports.DialogContent = DialogContent;
5492
+ exports.DialogDescription = DialogDescription;
5493
+ exports.DialogFooter = DialogFooter;
5494
+ exports.DialogHeader = DialogHeader;
5495
+ exports.DialogPortal = DialogPortal;
5496
+ exports.DialogTitle = DialogTitle;
5497
+ exports.DialogTrigger = DialogTrigger;
2698
5498
  exports.Dropdown = Dropdown;
5499
+ exports.DropdownMenu = DropdownMenu;
5500
+ exports.DropdownMenuCheckboxItem = DropdownMenuCheckboxItem;
5501
+ exports.DropdownMenuContent = DropdownMenuContent;
5502
+ exports.DropdownMenuGroup = DropdownMenuGroup;
5503
+ exports.DropdownMenuItem = DropdownMenuItem;
5504
+ exports.DropdownMenuLabel = DropdownMenuLabel;
5505
+ exports.DropdownMenuPortal = DropdownMenuPortal;
5506
+ exports.DropdownMenuRadioGroup = DropdownMenuRadioGroup;
5507
+ exports.DropdownMenuRadioItem = DropdownMenuRadioItem;
5508
+ exports.DropdownMenuSeparator = DropdownMenuSeparator;
5509
+ exports.DropdownMenuShortcut = DropdownMenuShortcut;
5510
+ exports.DropdownMenuSub = DropdownMenuSub;
5511
+ exports.DropdownMenuSubContent = DropdownMenuSubContent;
5512
+ exports.DropdownMenuSubTrigger = DropdownMenuSubTrigger;
5513
+ exports.DropdownMenuTrigger = DropdownMenuTrigger;
2699
5514
  exports.FORM_ERROR_KEY = FORM_ERROR_KEY;
2700
5515
  exports.Field = Field;
2701
5516
  exports.FieldBox = FieldBox;
2702
5517
  exports.Fieldset = Fieldset;
5518
+ exports.FieldsetView = FieldsetView;
5519
+ exports.Form = Form;
2703
5520
  exports.Input = Input;
5521
+ exports.Label = Label;
2704
5522
  exports.NumberField = NumberField;
2705
5523
  exports.OrynnProvider = OrynnProvider;
5524
+ exports.Pagination = Pagination;
5525
+ exports.PaginationContent = PaginationContent;
5526
+ exports.PaginationEllipsis = PaginationEllipsis;
5527
+ exports.PaginationItem = PaginationItem;
5528
+ exports.PaginationLink = PaginationLink;
5529
+ exports.Popover = Popover2;
5530
+ exports.PopoverAnchor = PopoverAnchor;
5531
+ exports.PopoverArrow = PopoverArrow;
5532
+ exports.PopoverClose = PopoverClose;
5533
+ exports.PopoverContent = PopoverContent;
5534
+ exports.PopoverTrigger = PopoverTrigger;
5535
+ exports.Progress = Progress;
2706
5536
  exports.Radio = Radio;
2707
5537
  exports.RadioGroup = RadioGroup;
2708
5538
  exports.Select = Dropdown;
5539
+ exports.Separator = Separator;
5540
+ exports.Slider = Slider;
5541
+ exports.Switch = Switch;
5542
+ exports.Table = Table;
5543
+ exports.TableBody = TableBody;
5544
+ exports.TableCaption = TableCaption;
5545
+ exports.TableCell = TableCell;
5546
+ exports.TableFooter = TableFooter;
5547
+ exports.TableHead = TableHead;
5548
+ exports.TableHeader = TableHeader;
5549
+ exports.TableRow = TableRow;
5550
+ exports.Tabs = Tabs;
5551
+ exports.TabsContent = TabsContent;
5552
+ exports.TabsList = TabsList;
5553
+ exports.TabsTrigger = TabsTrigger;
2709
5554
  exports.Textarea = Textarea;
5555
+ exports.Toaster = Toaster;
5556
+ exports.Tooltip = Tooltip;
5557
+ exports.TooltipContent = TooltipContent;
5558
+ exports.TooltipProvider = TooltipProvider;
5559
+ exports.TooltipTrigger = TooltipTrigger;
5560
+ exports.badgeClasses = badgeClasses;
5561
+ exports.builtInRules = builtInRules;
5562
+ exports.buttonClasses = buttonClasses;
2710
5563
  exports.createRegistry = createRegistry;
2711
5564
  exports.createRuleResolver = createRuleResolver;
2712
5565
  exports.defaultRegistry = defaultRegistry;
@@ -2715,7 +5568,12 @@ exports.normalizeConfig = normalizeConfig;
2715
5568
  exports.registerField = registerField;
2716
5569
  exports.registerRule = registerRule;
2717
5570
  exports.resolveThemeVars = resolveThemeVars;
5571
+ exports.standardSchemaResolver = standardSchemaResolver;
5572
+ exports.toast = toast;
2718
5573
  exports.useFieldsetState = useFieldsetState;
2719
5574
  exports.useOrynnTheme = useOrynnTheme;
5575
+ exports.usePagination = usePagination;
5576
+ exports.usePortalContainer = usePortalContainer;
5577
+ exports.useToast = useToast;
2720
5578
  //# sourceMappingURL=index.cjs.map
2721
5579
  //# sourceMappingURL=index.cjs.map