orynn 0.1.0 → 0.2.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
@@ -2,6 +2,7 @@
2
2
 
3
3
  var react = require('react');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
+ var reactDom = require('react-dom');
5
6
 
6
7
  var __typeError = (msg) => {
7
8
  throw TypeError(msg);
@@ -14,6 +15,167 @@ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot
14
15
  function cx(...names) {
15
16
  return names.filter(Boolean).join(" ");
16
17
  }
18
+ var base = (props) => ({
19
+ width: "1em",
20
+ height: "1em",
21
+ viewBox: "0 0 24 24",
22
+ fill: "none",
23
+ stroke: "currentColor",
24
+ strokeWidth: 2,
25
+ strokeLinecap: "round",
26
+ strokeLinejoin: "round",
27
+ "aria-hidden": true,
28
+ focusable: false,
29
+ ...props
30
+ });
31
+ var CheckIcon = (p) => /* @__PURE__ */ jsxRuntime.jsx("svg", { ...base(p), children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 6 9 17l-5-5" }) });
32
+ 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
+ var ChevronDownIcon = (p) => /* @__PURE__ */ jsxRuntime.jsx("svg", { ...base(p), children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 9 6 6 6-6" }) });
35
+ var ChevronLeftIcon = (p) => /* @__PURE__ */ jsxRuntime.jsx("svg", { ...base(p), children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m15 18-6-6 6-6" }) });
36
+ var ChevronRightIcon = (p) => /* @__PURE__ */ jsxRuntime.jsx("svg", { ...base(p), children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m9 18 6-6-6-6" }) });
37
+ var XIcon = (p) => /* @__PURE__ */ jsxRuntime.jsx("svg", { ...base(p), children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6 6 18M6 6l12 12" }) });
38
+ var SpinnerIcon = (p) => /* @__PURE__ */ jsxRuntime.jsx("svg", { ...base({ className: "orynn-spin", ...p }), children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12a9 9 0 1 1-6.2-8.6" }) });
39
+ var EyeIcon = (p) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...base(p), children: [
40
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z" }),
41
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "3" })
42
+ ] });
43
+ var EyeOffIcon = (p) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...base(p), children: [
44
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M10.7 5.1A9.9 9.9 0 0 1 12 5c6.5 0 10 7 10 7a17 17 0 0 1-3.4 4.2M6.6 6.6A17 17 0 0 0 2 12s3.5 7 10 7a9.7 9.7 0 0 0 5.4-1.6" }),
45
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9.9 9.9a3 3 0 0 0 4.2 4.2M2 2l20 20" })
46
+ ] });
47
+ var CalendarIcon = (p) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...base(p), children: [
48
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }),
49
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16 2v4M8 2v4M3 10h18" })
50
+ ] });
51
+ var CheckboxGroupContext = react.createContext(null);
52
+ var sizePx = {
53
+ sm: "0.95rem",
54
+ md: "1.125rem",
55
+ lg: "1.35rem"
56
+ };
57
+ var Checkbox = /* @__PURE__ */ react.forwardRef(
58
+ function Checkbox2(props, ref) {
59
+ const group = react.useContext(CheckboxGroupContext);
60
+ const {
61
+ label,
62
+ description,
63
+ checked: checkedProp,
64
+ defaultChecked,
65
+ indeterminate = false,
66
+ onChange,
67
+ value,
68
+ disabled: disabledProp,
69
+ readOnly,
70
+ required,
71
+ invalid,
72
+ size: sizeProp,
73
+ variant = "filled",
74
+ labelPlacement = "end",
75
+ card,
76
+ icon,
77
+ indeterminateIcon,
78
+ className,
79
+ id: idProp,
80
+ name: nameProp,
81
+ ...rest
82
+ } = props;
83
+ const reactId = react.useId();
84
+ const id = idProp ?? reactId;
85
+ const innerRef = react.useRef(null);
86
+ const inGroup = group != null && value != null;
87
+ const groupChecked = inGroup ? group.value.includes(value) : void 0;
88
+ const isControlled = checkedProp !== void 0 || groupChecked !== void 0;
89
+ const checked = checkedProp ?? groupChecked ?? void 0;
90
+ const size = sizeProp ?? group?.size ?? "md";
91
+ const disabled = disabledProp || group?.disabled || inGroup && checked === true && group.isDisabledToUncheck(value) || inGroup && checked !== true && group.isDisabledToCheck(value) || false;
92
+ react.useEffect(() => {
93
+ if (innerRef.current) innerRef.current.indeterminate = indeterminate;
94
+ }, [indeterminate]);
95
+ const handleChange = (e) => {
96
+ if (readOnly) {
97
+ e.preventDefault();
98
+ return;
99
+ }
100
+ const next = e.target.checked;
101
+ onChange?.(next, e);
102
+ if (inGroup) group.toggle(value, next);
103
+ };
104
+ return /* @__PURE__ */ jsxRuntime.jsxs(
105
+ "label",
106
+ {
107
+ className: cx(
108
+ "orynn-choice",
109
+ card && "orynn-choice--card",
110
+ variant === "outline" && "orynn-choice--outline",
111
+ labelPlacement === "start" && "orynn-choice--start",
112
+ className
113
+ ),
114
+ "data-disabled": disabled || void 0,
115
+ "data-invalid": invalid || void 0,
116
+ style: { "--orynn-check-size": sizePx[size] },
117
+ children: [
118
+ /* @__PURE__ */ jsxRuntime.jsx(
119
+ "input",
120
+ {
121
+ ...rest,
122
+ ref: (n) => {
123
+ innerRef.current = n;
124
+ if (typeof ref === "function") ref(n);
125
+ else if (ref) ref.current = n;
126
+ },
127
+ type: "checkbox",
128
+ className: "orynn-choice__native",
129
+ id,
130
+ name: nameProp ?? group?.name,
131
+ value,
132
+ checked: isControlled ? Boolean(checked) : void 0,
133
+ defaultChecked: isControlled ? void 0 : defaultChecked,
134
+ disabled,
135
+ required,
136
+ "aria-invalid": invalid || void 0,
137
+ onChange: handleChange
138
+ }
139
+ ),
140
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "orynn-choice__control orynn-choice__control--checkbox", "aria-hidden": "true", children: [
141
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-choice__mark orynn-choice__mark--check", children: icon ?? /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, {}) }),
142
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-choice__mark orynn-choice__mark--dash", children: indeterminateIcon ?? /* @__PURE__ */ jsxRuntime.jsx(MinusIcon, {}) })
143
+ ] }),
144
+ (label != null || description != null) && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "orynn-choice__text", children: [
145
+ label != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-choice__label", children: label }),
146
+ description != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-choice__desc", children: description })
147
+ ] })
148
+ ]
149
+ }
150
+ );
151
+ }
152
+ );
153
+ function useLatestRef(value) {
154
+ const ref = react.useRef(value);
155
+ ref.current = value;
156
+ return ref;
157
+ }
158
+
159
+ // src/core/state/use-controllable-state.ts
160
+ function useControllableState({
161
+ value,
162
+ defaultValue,
163
+ onChange
164
+ }) {
165
+ const isControlled = value !== void 0;
166
+ const [uncontrolled, setUncontrolled] = react.useState(defaultValue);
167
+ const current = isControlled ? value : uncontrolled;
168
+ const currentRef = useLatestRef(current);
169
+ const onChangeRef = useLatestRef(onChange);
170
+ const isControlledRef = useLatestRef(isControlled);
171
+ const setValue = react.useCallback((next) => {
172
+ const resolved = typeof next === "function" ? next(currentRef.current) : next;
173
+ if (Object.is(resolved, currentRef.current)) return;
174
+ if (!isControlledRef.current) setUncontrolled(resolved);
175
+ onChangeRef.current?.(resolved);
176
+ }, []);
177
+ return [current, setValue];
178
+ }
17
179
  function toMessages(error) {
18
180
  if (error == null) return [];
19
181
  return (Array.isArray(error) ? error : [error]).filter((m) => Boolean(m));
@@ -23,139 +185,1826 @@ function joinIds(...ids) {
23
185
  return list.length > 0 ? list.join(" ") : void 0;
24
186
  }
25
187
  function Field(props) {
26
- const { label, description, error, required, disabled, className, children } = props;
188
+ const { label, description, error, success, required, disabled, hideLabel, className, children } = props;
27
189
  const reactId = react.useId();
28
190
  const id = props.id ?? reactId;
29
191
  const messages = toMessages(error);
30
192
  const invalid = messages.length > 0;
193
+ const successMsg = !invalid && typeof success === "string" && success ? success : void 0;
194
+ const valid = !invalid && (success === true || Boolean(successMsg));
31
195
  const descriptionId = description != null ? `${id}-description` : void 0;
32
196
  const errorId = invalid ? `${id}-error` : void 0;
33
- const describedById = joinIds(descriptionId, errorId);
34
- const ctx = { id, describedById, invalid, required, disabled };
197
+ const successId = successMsg ? `${id}-success` : void 0;
198
+ const describedById = joinIds(descriptionId, errorId, successId);
199
+ const ctx = { id, describedById, invalid, valid, required, disabled };
35
200
  return /* @__PURE__ */ jsxRuntime.jsxs(
36
201
  "div",
37
202
  {
38
203
  className: cx("orynn-field", className),
39
204
  "data-invalid": invalid || void 0,
205
+ "data-valid": valid || void 0,
40
206
  "data-disabled": disabled || void 0,
41
207
  "data-required": required || void 0,
42
208
  children: [
43
- label != null && /* @__PURE__ */ jsxRuntime.jsx("label", { className: "orynn-field__label", htmlFor: id, children: label }),
209
+ label != null && !hideLabel && /* @__PURE__ */ jsxRuntime.jsx("label", { className: "orynn-field__label", htmlFor: id, id: `${id}-label`, children: label }),
44
210
  description != null && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "orynn-field__description", id: descriptionId, children: description }),
45
211
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "orynn-field__control", children: typeof children === "function" ? children(ctx) : children }),
46
- 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)) })
212
+ 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
+ )
47
222
  ]
48
223
  }
49
224
  );
50
225
  }
226
+ function CheckboxGroup(props) {
227
+ const {
228
+ label,
229
+ description,
230
+ error,
231
+ success,
232
+ required,
233
+ disabled,
234
+ name: nameProp,
235
+ size,
236
+ value: valueProp,
237
+ defaultValue,
238
+ onChange,
239
+ orientation = "vertical",
240
+ min,
241
+ max,
242
+ options,
243
+ children,
244
+ className,
245
+ id: idProp
246
+ } = props;
247
+ const reactId = react.useId();
248
+ const id = idProp ?? reactId;
249
+ const name = nameProp ?? id;
250
+ const [value, setValue] = useControllableState({
251
+ value: valueProp,
252
+ defaultValue: defaultValue ?? [],
253
+ onChange: void 0
254
+ });
255
+ const toggle = react.useCallback(
256
+ (v, next) => {
257
+ const set = new Set(value);
258
+ if (next) set.add(v);
259
+ else set.delete(v);
260
+ const arr = [...set];
261
+ setValue(arr);
262
+ onChange?.(arr);
263
+ },
264
+ [value, setValue, onChange]
265
+ );
266
+ const ctx = react.useMemo(
267
+ () => ({
268
+ name,
269
+ value,
270
+ toggle,
271
+ disabled,
272
+ size,
273
+ isDisabledToUncheck: () => min != null && value.length <= min,
274
+ isDisabledToCheck: (v) => max != null && value.length >= max && !value.includes(v)
275
+ }),
276
+ [name, value, toggle, disabled, size, min, max]
277
+ );
278
+ return /* @__PURE__ */ jsxRuntime.jsx(
279
+ Field,
280
+ {
281
+ label,
282
+ description,
283
+ error,
284
+ success,
285
+ required,
286
+ disabled,
287
+ id,
288
+ className,
289
+ children: () => /* @__PURE__ */ jsxRuntime.jsx(CheckboxGroupContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxRuntime.jsx(
290
+ "div",
291
+ {
292
+ role: "group",
293
+ "aria-labelledby": label != null ? `${id}-label` : void 0,
294
+ 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
306
+ }
307
+ ) })
308
+ }
309
+ );
310
+ }
311
+ var FieldBox = /* @__PURE__ */ react.forwardRef(
312
+ function FieldBox2(props, ref) {
313
+ const {
314
+ size = "md",
315
+ variant = "outline",
316
+ radius,
317
+ disabled,
318
+ invalid,
319
+ valid,
320
+ focused,
321
+ active,
322
+ open,
323
+ floatingLabel,
324
+ left,
325
+ right,
326
+ textarea,
327
+ className,
328
+ children,
329
+ onFocus,
330
+ onBlur,
331
+ ...rest
332
+ } = props;
333
+ const [focusWithin, setFocusWithin] = react.useState(false);
334
+ const isFocused = focused ?? focusWithin;
335
+ const handleFocus = (e) => {
336
+ if (focused === void 0) setFocusWithin(true);
337
+ onFocus?.(e);
338
+ };
339
+ const handleBlur = (e) => {
340
+ if (focused === void 0 && !e.currentTarget.contains(e.relatedTarget)) {
341
+ setFocusWithin(false);
342
+ }
343
+ onBlur?.(e);
344
+ };
345
+ return /* @__PURE__ */ jsxRuntime.jsxs(
346
+ "div",
347
+ {
348
+ ref,
349
+ className: cx(
350
+ "orynn-box",
351
+ floatingLabel != null && "orynn-box--floating",
352
+ textarea && "orynn-box--textarea",
353
+ className
354
+ ),
355
+ "data-size": size,
356
+ "data-variant": variant,
357
+ "data-radius": radius,
358
+ "data-disabled": disabled || void 0,
359
+ "data-invalid": invalid || void 0,
360
+ "data-valid": valid || void 0,
361
+ "data-focused": isFocused || void 0,
362
+ "data-active": active || isFocused || void 0,
363
+ "data-open": open || void 0,
364
+ onFocus: handleFocus,
365
+ onBlur: handleBlur,
366
+ ...rest,
367
+ children: [
368
+ left != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-box__adornment", children: left }),
369
+ children,
370
+ floatingLabel,
371
+ right != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-box__adornment", children: right })
372
+ ]
373
+ }
374
+ );
375
+ }
376
+ );
377
+ var useIsoLayoutEffect = typeof window === "undefined" ? react.useEffect : react.useLayoutEffect;
378
+ function Popover({
379
+ open,
380
+ anchorRef,
381
+ onClose,
382
+ children,
383
+ matchWidth = true,
384
+ offset = 6,
385
+ className,
386
+ popoverRef,
387
+ role,
388
+ id
389
+ }) {
390
+ const innerRef = react.useRef(null);
391
+ const setRef = (el) => {
392
+ innerRef.current = el;
393
+ if (popoverRef) popoverRef.current = el;
394
+ };
395
+ const [pos, setPos] = react.useState(null);
396
+ useIsoLayoutEffect(() => {
397
+ if (!open) return;
398
+ const anchor = anchorRef.current;
399
+ if (!anchor) return;
400
+ const update = () => {
401
+ const r = anchor.getBoundingClientRect();
402
+ const vh = window.innerHeight;
403
+ const below = vh - r.bottom - offset;
404
+ const above = r.top - offset;
405
+ const wanted = innerRef.current?.scrollHeight ?? 280;
406
+ const placeTop = below < Math.min(wanted, 240) && above > below;
407
+ setPos({
408
+ left: r.left,
409
+ top: placeTop ? r.top - offset : r.bottom + offset,
410
+ width: r.width,
411
+ placement: placeTop ? "top" : "bottom",
412
+ maxHeight: Math.max(140, (placeTop ? above : below) - 4)
413
+ });
414
+ };
415
+ update();
416
+ const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(update) : void 0;
417
+ ro?.observe(anchor);
418
+ window.addEventListener("scroll", update, true);
419
+ window.addEventListener("resize", update);
420
+ return () => {
421
+ ro?.disconnect();
422
+ window.removeEventListener("scroll", update, true);
423
+ window.removeEventListener("resize", update);
424
+ };
425
+ }, [open, anchorRef, offset]);
426
+ react.useEffect(() => {
427
+ if (!open) return;
428
+ const onPointerDown = (e) => {
429
+ const t = e.target;
430
+ if (innerRef.current?.contains(t) || anchorRef.current?.contains(t)) return;
431
+ onClose();
432
+ };
433
+ const onKey = (e) => {
434
+ if (e.key === "Escape") {
435
+ e.stopPropagation();
436
+ onClose();
437
+ }
438
+ };
439
+ document.addEventListener("pointerdown", onPointerDown, true);
440
+ document.addEventListener("keydown", onKey, true);
441
+ return () => {
442
+ document.removeEventListener("pointerdown", onPointerDown, true);
443
+ document.removeEventListener("keydown", onKey, true);
444
+ };
445
+ }, [open, anchorRef, onClose]);
446
+ if (!open || typeof document === "undefined" || !pos) return null;
447
+ return reactDom.createPortal(
448
+ /* @__PURE__ */ jsxRuntime.jsx(
449
+ "div",
450
+ {
451
+ ref: setRef,
452
+ id,
453
+ role,
454
+ className: cx("orynn-popover", className),
455
+ style: {
456
+ left: pos.left,
457
+ top: pos.top,
458
+ width: matchWidth ? pos.width : void 0,
459
+ minWidth: pos.width,
460
+ maxHeight: pos.maxHeight,
461
+ transform: pos.placement === "top" ? "translateY(-100%)" : void 0
462
+ },
463
+ children
464
+ }
465
+ ),
466
+ document.body
467
+ );
468
+ }
469
+
470
+ // src/controls/DatePicker/date-utils.ts
471
+ var startOfDay = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate());
472
+ var isSameDay = (a, b) => !!a && !!b && a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
473
+ var addDays = (d, n) => {
474
+ const r = new Date(d);
475
+ r.setDate(r.getDate() + n);
476
+ return r;
477
+ };
478
+ var addMonths = (d, n) => {
479
+ const r = new Date(d.getFullYear(), d.getMonth() + n, 1);
480
+ const day = Math.min(d.getDate(), daysInMonth(r.getFullYear(), r.getMonth()));
481
+ r.setDate(day);
482
+ return r;
483
+ };
484
+ var daysInMonth = (year, month) => new Date(year, month + 1, 0).getDate();
485
+ var clampDate = (d, min, max) => {
486
+ if (min && d < startOfDay(min)) return startOfDay(min);
487
+ if (max && d > startOfDay(max)) return startOfDay(max);
488
+ return d;
489
+ };
490
+ 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]);
500
+ 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);
512
+ return Number.isNaN(d.getTime()) ? null : d;
513
+ }
514
+ function toDate(v) {
515
+ if (v == null) return null;
516
+ if (v instanceof Date) return Number.isNaN(v.getTime()) ? null : startOfDay(v);
517
+ const d = new Date(v);
518
+ return Number.isNaN(d.getTime()) ? null : startOfDay(d);
519
+ }
520
+ function monthMatrix(viewYear, viewMonth, firstDayOfWeek) {
521
+ const first = new Date(viewYear, viewMonth, 1);
522
+ const offset = (first.getDay() - firstDayOfWeek + 7) % 7;
523
+ const start = addDays(first, -offset);
524
+ return Array.from({ length: 42 }, (_, i) => addDays(start, i));
525
+ }
526
+ function weekdayLabels(firstDayOfWeek, locale) {
527
+ const fmt = new Intl.DateTimeFormat(locale, { weekday: "short" });
528
+ return Array.from(
529
+ { length: 7 },
530
+ (_, i) => fmt.format(new Date(2023, 0, 1 + (firstDayOfWeek + i) % 7))
531
+ );
532
+ }
533
+ function isoWeek(d) {
534
+ const date = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
535
+ const dayNum = (date.getUTCDay() + 6) % 7;
536
+ date.setUTCDate(date.getUTCDate() - dayNum + 3);
537
+ const firstThursday = new Date(Date.UTC(date.getUTCFullYear(), 0, 4));
538
+ const diff = date.getTime() - firstThursday.getTime();
539
+ return 1 + Math.round(diff / (7 * 24 * 3600 * 1e3));
540
+ }
541
+ function Calendar({
542
+ value,
543
+ onSelect,
544
+ minDate,
545
+ maxDate,
546
+ isDisabled,
547
+ firstDayOfWeek = 0,
548
+ locale,
549
+ showWeekNumbers,
550
+ showToday,
551
+ showClear,
552
+ onClear,
553
+ autoFocus
554
+ }) {
555
+ 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() });
558
+ const [focused, setFocused] = react.useState(initial);
559
+ const gridRef = react.useRef(null);
560
+ react.useEffect(() => {
561
+ if (value) {
562
+ setView({ y: value.getFullYear(), m: value.getMonth() });
563
+ setFocused(value);
564
+ }
565
+ }, [value]);
566
+ react.useEffect(() => {
567
+ if (autoFocus) {
568
+ gridRef.current?.querySelector('[data-focused="true"]')?.focus();
569
+ }
570
+ }, [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
+ 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() });
581
+ requestAnimationFrame(() => {
582
+ gridRef.current?.querySelector('[data-focused="true"]')?.focus();
583
+ });
584
+ };
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);
604
+ };
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(
656
+ "table",
657
+ {
658
+ ref: gridRef,
659
+ className: "orynn-calendar__grid",
660
+ role: "grid",
661
+ "aria-label": monthName,
662
+ onKeyDown,
663
+ children: [
664
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
665
+ showWeekNumbers && /* @__PURE__ */ jsxRuntime.jsx("th", { "aria-hidden": "true" }),
666
+ weekdays.map((w) => /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", children: w }, w))
667
+ ] }) }),
668
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: Array.from({ length: 6 }, (_, week) => {
669
+ const row = cells.slice(week * 7, week * 7 + 7);
670
+ const firstOfRow = row[0];
671
+ return /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
672
+ showWeekNumbers && /* @__PURE__ */ jsxRuntime.jsx("td", { className: "orynn-calendar__weeknum", children: isoWeek(firstOfRow) }),
673
+ row.map((d) => {
674
+ const inMonth = d.getMonth() === view.m;
675
+ const disabled = outOfRange(d);
676
+ const selected = isSameDay(d, value);
677
+ const isFocused = isSameDay(d, focused);
678
+ return /* @__PURE__ */ jsxRuntime.jsx("td", { role: "gridcell", "aria-selected": selected, children: /* @__PURE__ */ jsxRuntime.jsx(
679
+ "button",
680
+ {
681
+ type: "button",
682
+ className: "orynn-calendar__day",
683
+ tabIndex: isFocused ? 0 : -1,
684
+ "data-focused": isFocused || void 0,
685
+ "data-outside": !inMonth || void 0,
686
+ "data-today": isSameDay(d, today) || void 0,
687
+ "data-selected": selected || void 0,
688
+ disabled,
689
+ "aria-label": new Intl.DateTimeFormat(locale, { dateStyle: "full" }).format(
690
+ d
691
+ ),
692
+ onClick: () => {
693
+ setFocused(d);
694
+ onSelect(d);
695
+ },
696
+ children: d.getDate()
697
+ }
698
+ ) }, d.toISOString());
699
+ })
700
+ ] }, firstOfRow.toISOString());
701
+ }) })
702
+ ]
703
+ }
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
+ ] });
719
+ }
720
+ var DatePicker = /* @__PURE__ */ react.forwardRef(
721
+ function DatePicker2(props, ref) {
722
+ const {
723
+ label,
724
+ description,
725
+ error,
726
+ success,
727
+ required,
728
+ disabled,
729
+ readOnly,
730
+ size,
731
+ variant,
732
+ radius,
733
+ floatingLabel,
734
+ clearable,
735
+ onClear,
736
+ id: idProp,
737
+ name,
738
+ className,
739
+ value: valueProp,
740
+ defaultValue,
741
+ onChange,
742
+ format = "yyyy-MM-dd",
743
+ minDate,
744
+ maxDate,
745
+ disabledDates,
746
+ firstDayOfWeek = 0,
747
+ locale,
748
+ showWeekNumbers,
749
+ showToday = true,
750
+ showClear,
751
+ inline = false,
752
+ allowInput = true,
753
+ closeOnSelect = true,
754
+ placeholder
755
+ } = props;
756
+ const reactId = react.useId();
757
+ const id = idProp ?? reactId;
758
+ const panelId = `${id}-cal`;
759
+ 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);
763
+ const [gridFocus, setGridFocus] = react.useState(false);
764
+ const [text, setText] = react.useState(() => value ? formatDate(value, format, locale) : "");
765
+ const boxRef = react.useRef(null);
766
+ const inputRef = react.useRef(null);
767
+ react.useEffect(() => {
768
+ setText(value ? formatDate(value, format, locale) : "");
769
+ }, [value, format, locale]);
770
+ const isDisabledDate = (d) => {
771
+ if (Array.isArray(disabledDates))
772
+ return disabledDates.some((x) => isSameDay(startOfDay(x), d));
773
+ return Boolean(disabledDates?.(d));
774
+ };
775
+ const commit = (d) => {
776
+ if (!isControlled) setInner(d);
777
+ onChange?.(d);
778
+ };
779
+ const select = (d) => {
780
+ if (isDisabledDate(d)) return;
781
+ commit(startOfDay(d));
782
+ if (closeOnSelect) {
783
+ setOpen(false);
784
+ inputRef.current?.focus();
785
+ }
786
+ };
787
+ const onKeyDown = (e) => {
788
+ if (disabled || readOnly) return;
789
+ if (e.key === "ArrowDown") {
790
+ e.preventDefault();
791
+ setOpen(true);
792
+ setGridFocus(true);
793
+ } else if (e.key === "Escape" && open) {
794
+ e.preventDefault();
795
+ setOpen(false);
796
+ } else if (e.key === "Enter") {
797
+ const parsed = parseDate(text, format);
798
+ if (parsed && !isDisabledDate(startOfDay(parsed))) select(parsed);
799
+ }
800
+ };
801
+ const cal = /* @__PURE__ */ jsxRuntime.jsx(
802
+ Calendar,
803
+ {
804
+ value,
805
+ onSelect: select,
806
+ minDate,
807
+ maxDate,
808
+ isDisabled: isDisabledDate,
809
+ firstDayOfWeek,
810
+ locale,
811
+ showWeekNumbers,
812
+ showToday,
813
+ showClear,
814
+ onClear: () => {
815
+ commit(null);
816
+ setOpen(false);
817
+ },
818
+ autoFocus: open && gridFocus || inline
819
+ }
820
+ );
821
+ if (inline) {
822
+ return /* @__PURE__ */ jsxRuntime.jsx(
823
+ Field,
824
+ {
825
+ label,
826
+ description,
827
+ error,
828
+ success,
829
+ required,
830
+ disabled,
831
+ id,
832
+ className,
833
+ children: () => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "orynn-popover", style: { position: "static" }, children: cal })
834
+ }
835
+ );
836
+ }
837
+ const hasValue = value != null;
838
+ return /* @__PURE__ */ jsxRuntime.jsx(
839
+ Field,
840
+ {
841
+ label,
842
+ description,
843
+ error,
844
+ success,
845
+ required,
846
+ disabled,
847
+ hideLabel: floatingLabel,
848
+ id,
849
+ className,
850
+ children: ({ describedById, invalid, valid }) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
851
+ /* @__PURE__ */ jsxRuntime.jsx(
852
+ FieldBox,
853
+ {
854
+ ref: boxRef,
855
+ size,
856
+ variant,
857
+ radius,
858
+ disabled,
859
+ invalid,
860
+ valid,
861
+ open,
862
+ active: hasValue || text.length > 0,
863
+ floatingLabel: floatingLabel ? /* @__PURE__ */ jsxRuntime.jsx("label", { className: "orynn-box__label", htmlFor: id, children: label }) : void 0,
864
+ right: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
865
+ clearable && hasValue && !disabled && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
866
+ "button",
867
+ {
868
+ type: "button",
869
+ className: "orynn-box__iconbtn",
870
+ "aria-label": "Clear",
871
+ tabIndex: -1,
872
+ onClick: () => {
873
+ commit(null);
874
+ onClear?.();
875
+ },
876
+ children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, {})
877
+ }
878
+ ),
879
+ /* @__PURE__ */ jsxRuntime.jsx(
880
+ "button",
881
+ {
882
+ type: "button",
883
+ className: "orynn-box__iconbtn",
884
+ "aria-label": "Open calendar",
885
+ "aria-haspopup": "dialog",
886
+ "aria-expanded": open,
887
+ disabled,
888
+ onClick: () => {
889
+ if (disabled || readOnly) return;
890
+ setGridFocus(true);
891
+ setOpen((o) => !o);
892
+ },
893
+ children: /* @__PURE__ */ jsxRuntime.jsx(CalendarIcon, {})
894
+ }
895
+ )
896
+ ] }),
897
+ children: /* @__PURE__ */ jsxRuntime.jsx(
898
+ "input",
899
+ {
900
+ ref: (n) => {
901
+ inputRef.current = n;
902
+ if (typeof ref === "function") ref(n);
903
+ else if (ref)
904
+ ref.current = n;
905
+ },
906
+ id,
907
+ name,
908
+ className: "orynn-box__field",
909
+ type: "text",
910
+ inputMode: "numeric",
911
+ autoComplete: "off",
912
+ role: "combobox",
913
+ "aria-expanded": open,
914
+ "aria-controls": panelId,
915
+ "aria-haspopup": "dialog",
916
+ "aria-invalid": invalid || void 0,
917
+ "aria-describedby": describedById,
918
+ value: text,
919
+ disabled,
920
+ readOnly: !allowInput || readOnly,
921
+ required,
922
+ placeholder: floatingLabel ? void 0 : placeholder ?? format.toLowerCase(),
923
+ onChange: (e) => setText(e.target.value),
924
+ onKeyDown,
925
+ onFocus: () => setOpen(true),
926
+ onBlur: () => {
927
+ const parsed = parseDate(text, format);
928
+ if (parsed && !isDisabledDate(startOfDay(parsed))) {
929
+ commit(startOfDay(parsed));
930
+ setText(formatDate(startOfDay(parsed), format, locale));
931
+ } else {
932
+ setText(value ? formatDate(value, format, locale) : "");
933
+ }
934
+ }
935
+ }
936
+ )
937
+ }
938
+ ),
939
+ /* @__PURE__ */ jsxRuntime.jsx(
940
+ Popover,
941
+ {
942
+ open,
943
+ anchorRef: boxRef,
944
+ onClose: () => {
945
+ setOpen(false);
946
+ setGridFocus(false);
947
+ },
948
+ matchWidth: false,
949
+ id: panelId,
950
+ role: "dialog",
951
+ children: cal
952
+ }
953
+ )
954
+ ] })
955
+ }
956
+ );
957
+ }
958
+ );
959
+ var toArray = (v) => v == null ? [] : Array.isArray(v) ? v : [v];
960
+ function Highlight({ text, query }) {
961
+ if (!query) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: text });
962
+ const i = text.toLowerCase().indexOf(query.toLowerCase());
963
+ if (i < 0) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: text });
964
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
965
+ text.slice(0, i),
966
+ /* @__PURE__ */ jsxRuntime.jsx("mark", { children: text.slice(i, i + query.length) }),
967
+ text.slice(i + query.length)
968
+ ] });
969
+ }
51
970
  var Dropdown = /* @__PURE__ */ react.forwardRef(
52
971
  function Dropdown2(props, ref) {
53
972
  const {
54
973
  label,
55
974
  description,
56
975
  error,
976
+ success,
977
+ required,
978
+ disabled,
979
+ readOnly,
980
+ size,
981
+ variant,
982
+ radius,
983
+ floatingLabel,
984
+ clearable,
985
+ onClear,
986
+ loading,
987
+ id: idProp,
988
+ name,
57
989
  className,
58
990
  options,
59
- value,
991
+ value: valueProp,
992
+ defaultValue,
60
993
  onChange,
994
+ multiple = false,
995
+ searchable = false,
996
+ filterMode = "contains",
997
+ filterFn,
998
+ highlightMatch,
999
+ renderOption,
1000
+ renderValue,
61
1001
  placeholder,
62
- id,
1002
+ emptyMessage = "No results",
1003
+ closeOnSelect = !multiple,
1004
+ maxSelectedLabels,
1005
+ native = false,
1006
+ startContent
1007
+ } = props;
1008
+ const reactId = react.useId();
1009
+ const id = idProp ?? reactId;
1010
+ const listId = `${id}-listbox`;
1011
+ const isControlled = valueProp !== void 0;
1012
+ const [inner, setInner] = react.useState(() => toArray(defaultValue));
1013
+ const selectedValues = isControlled ? toArray(valueProp) : inner;
1014
+ const commit = (next) => {
1015
+ if (!isControlled) setInner(next);
1016
+ onChange?.(multiple ? next : next[0] ?? null);
1017
+ };
1018
+ const [open, setOpen] = react.useState(false);
1019
+ const [query, setQuery] = react.useState("");
1020
+ const [activeIndex, setActiveIndex] = react.useState(0);
1021
+ const boxRef = react.useRef(null);
1022
+ const inputRef = react.useRef(null);
1023
+ 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);
1026
+ 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]);
1032
+ const rows = react.useMemo(() => {
1033
+ const out = [];
1034
+ let seenGroup;
1035
+ let optionIndex = 0;
1036
+ for (const o of filtered) {
1037
+ if (o.group && o.group !== seenGroup) {
1038
+ seenGroup = o.group;
1039
+ out.push({ type: "group", label: o.group });
1040
+ }
1041
+ out.push({ type: "option", option: o, optionIndex });
1042
+ optionIndex += 1;
1043
+ }
1044
+ return out;
1045
+ }, [filtered]);
1046
+ const selectableOptions = filtered;
1047
+ react.useEffect(() => {
1048
+ setActiveIndex((i) => Math.min(Math.max(0, i), Math.max(0, selectableOptions.length - 1)));
1049
+ }, [selectableOptions.length]);
1050
+ react.useEffect(() => {
1051
+ if (!open) setQuery("");
1052
+ }, [open]);
1053
+ const firstRelevantIndex = () => {
1054
+ const sel = selectableOptions.findIndex((o) => selectedValues.includes(o.value));
1055
+ return sel >= 0 ? sel : 0;
1056
+ };
1057
+ react.useEffect(() => {
1058
+ if (!open) return;
1059
+ const el = listRef.current?.querySelector('[data-active="true"]');
1060
+ el?.scrollIntoView?.({ block: "nearest" });
1061
+ }, [activeIndex, open]);
1062
+ const openPanel = () => {
1063
+ if (disabled || readOnly || open) return;
1064
+ setActiveIndex(firstRelevantIndex());
1065
+ setOpen(true);
1066
+ };
1067
+ const pick = (opt) => {
1068
+ if (opt.disabled) return;
1069
+ if (multiple) {
1070
+ const has = selectedValues.includes(opt.value);
1071
+ commit(
1072
+ has ? selectedValues.filter((v) => v !== opt.value) : [...selectedValues, opt.value]
1073
+ );
1074
+ setQuery("");
1075
+ } else {
1076
+ commit([opt.value]);
1077
+ }
1078
+ if (closeOnSelect) {
1079
+ setOpen(false);
1080
+ inputRef.current?.focus();
1081
+ }
1082
+ };
1083
+ const clearAll = () => {
1084
+ commit([]);
1085
+ onClear?.();
1086
+ setQuery("");
1087
+ };
1088
+ const onKeyDown = (e) => {
1089
+ if (disabled || readOnly) return;
1090
+ switch (e.key) {
1091
+ case "ArrowDown":
1092
+ e.preventDefault();
1093
+ if (!open) {
1094
+ openPanel();
1095
+ setActiveIndex(firstRelevantIndex());
1096
+ } else {
1097
+ setActiveIndex((i) => Math.min(selectableOptions.length - 1, i + 1));
1098
+ }
1099
+ break;
1100
+ case "ArrowUp":
1101
+ e.preventDefault();
1102
+ if (!open) {
1103
+ openPanel();
1104
+ setActiveIndex(firstRelevantIndex());
1105
+ } else {
1106
+ setActiveIndex((i) => Math.max(0, i - 1));
1107
+ }
1108
+ break;
1109
+ case "Home":
1110
+ if (open) {
1111
+ e.preventDefault();
1112
+ setActiveIndex(0);
1113
+ }
1114
+ break;
1115
+ case "End":
1116
+ if (open) {
1117
+ e.preventDefault();
1118
+ setActiveIndex(selectableOptions.length - 1);
1119
+ }
1120
+ break;
1121
+ case "Enter":
1122
+ if (open && selectableOptions[activeIndex]) {
1123
+ e.preventDefault();
1124
+ pick(selectableOptions[activeIndex]);
1125
+ }
1126
+ break;
1127
+ case "Escape":
1128
+ if (open) {
1129
+ e.preventDefault();
1130
+ setOpen(false);
1131
+ }
1132
+ break;
1133
+ case " ":
1134
+ if (!searchable) {
1135
+ e.preventDefault();
1136
+ if (!open) openPanel();
1137
+ else if (selectableOptions[activeIndex]) pick(selectableOptions[activeIndex]);
1138
+ }
1139
+ break;
1140
+ case "Backspace":
1141
+ if (multiple && query === "" && selectedValues.length > 0) {
1142
+ commit(selectedValues.slice(0, -1));
1143
+ }
1144
+ break;
1145
+ case "Tab":
1146
+ setOpen(false);
1147
+ break;
1148
+ }
1149
+ };
1150
+ const hasValue = selectedValues.length > 0;
1151
+ const doHighlight = highlightMatch ?? searchable;
1152
+ if (native && !multiple && !searchable) {
1153
+ return /* @__PURE__ */ jsxRuntime.jsx(
1154
+ Field,
1155
+ {
1156
+ label,
1157
+ description,
1158
+ error,
1159
+ success,
1160
+ required,
1161
+ disabled,
1162
+ id,
1163
+ className,
1164
+ children: ({ describedById, invalid }) => /* @__PURE__ */ jsxRuntime.jsxs(
1165
+ "select",
1166
+ {
1167
+ id,
1168
+ name,
1169
+ className: "orynn-dropdown",
1170
+ value: selectedValues[0] ?? "",
1171
+ disabled,
1172
+ required,
1173
+ "aria-invalid": invalid || void 0,
1174
+ "aria-describedby": describedById,
1175
+ onChange: (e) => commit(e.target.value ? [e.target.value] : []),
1176
+ children: [
1177
+ 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))
1179
+ ]
1180
+ }
1181
+ )
1182
+ }
1183
+ );
1184
+ }
1185
+ return /* @__PURE__ */ jsxRuntime.jsx(
1186
+ Field,
1187
+ {
1188
+ label,
1189
+ description,
1190
+ error,
1191
+ success,
1192
+ required,
1193
+ disabled,
1194
+ hideLabel: floatingLabel,
1195
+ id,
1196
+ className,
1197
+ children: ({ describedById, invalid, valid }) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1198
+ /* @__PURE__ */ jsxRuntime.jsx(
1199
+ FieldBox,
1200
+ {
1201
+ ref: boxRef,
1202
+ size,
1203
+ variant,
1204
+ radius,
1205
+ disabled,
1206
+ invalid,
1207
+ valid,
1208
+ open,
1209
+ active: hasValue,
1210
+ onClick: () => {
1211
+ inputRef.current?.focus();
1212
+ openPanel();
1213
+ },
1214
+ floatingLabel: floatingLabel ? /* @__PURE__ */ jsxRuntime.jsx("label", { className: "orynn-box__label", htmlFor: id, children: label }) : void 0,
1215
+ right: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1216
+ loading && /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, {}),
1217
+ clearable && hasValue && !disabled && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
1218
+ "button",
1219
+ {
1220
+ type: "button",
1221
+ className: "orynn-box__iconbtn",
1222
+ "aria-label": "Clear",
1223
+ tabIndex: -1,
1224
+ onClick: (e) => {
1225
+ e.stopPropagation();
1226
+ clearAll();
1227
+ },
1228
+ children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, {})
1229
+ }
1230
+ ),
1231
+ valid && !loading && /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { className: "orynn-icon-success" }),
1232
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronDownIcon, { className: "orynn-select__chevron" })
1233
+ ] }),
1234
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "orynn-select__value", children: [
1235
+ startContent,
1236
+ multiple && selectedOptions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "orynn-select__chips", children: [
1237
+ (maxSelectedLabels ? selectedOptions.slice(0, maxSelectedLabels) : selectedOptions).map((o) => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "orynn-select__chip", children: [
1238
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: o.label }),
1239
+ /* @__PURE__ */ jsxRuntime.jsx(
1240
+ "button",
1241
+ {
1242
+ type: "button",
1243
+ "aria-label": `Remove ${o.label}`,
1244
+ tabIndex: -1,
1245
+ onClick: (e) => {
1246
+ e.stopPropagation();
1247
+ commit(selectedValues.filter((v) => v !== o.value));
1248
+ },
1249
+ children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, {})
1250
+ }
1251
+ )
1252
+ ] }, o.value)),
1253
+ maxSelectedLabels && selectedOptions.length > maxSelectedLabels && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-select__chip", children: /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
1254
+ "+",
1255
+ selectedOptions.length - maxSelectedLabels
1256
+ ] }) })
1257
+ ] }),
1258
+ !multiple && hasValue && query === "" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-select__single", children: renderValue ? renderValue(selectedOptions) : selectedOptions[0]?.label }),
1259
+ !hasValue && !searchable && query === "" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-select__value--placeholder orynn-select__single", children: floatingLabel ? "" : placeholder }),
1260
+ /* @__PURE__ */ jsxRuntime.jsx(
1261
+ "input",
1262
+ {
1263
+ ...ref ? { ref: setRefs(ref, inputRef) } : { ref: inputRef },
1264
+ id,
1265
+ name,
1266
+ className: "orynn-select__search",
1267
+ role: "combobox",
1268
+ "aria-expanded": open,
1269
+ "aria-controls": listId,
1270
+ "aria-autocomplete": searchable ? "list" : "none",
1271
+ "aria-activedescendant": open && selectableOptions[activeIndex] ? `${id}-opt-${selectableOptions[activeIndex].value}` : void 0,
1272
+ "aria-invalid": invalid || void 0,
1273
+ "aria-describedby": describedById,
1274
+ autoComplete: "off",
1275
+ readOnly: !searchable || readOnly,
1276
+ disabled,
1277
+ placeholder: !hasValue && (searchable || !floatingLabel) ? placeholder : void 0,
1278
+ value: query,
1279
+ size: 1,
1280
+ onChange: (e) => {
1281
+ setQuery(e.target.value);
1282
+ if (!open) setOpen(true);
1283
+ },
1284
+ onKeyDown,
1285
+ onFocus: openPanel
1286
+ }
1287
+ )
1288
+ ] })
1289
+ }
1290
+ ),
1291
+ /* @__PURE__ */ jsxRuntime.jsx(
1292
+ Popover,
1293
+ {
1294
+ open,
1295
+ anchorRef: boxRef,
1296
+ onClose: () => setOpen(false),
1297
+ popoverRef: listRef,
1298
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1299
+ "div",
1300
+ {
1301
+ className: "orynn-listbox",
1302
+ id: listId,
1303
+ role: "listbox",
1304
+ "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
1334
+ )
1335
+ )
1336
+ }
1337
+ )
1338
+ }
1339
+ )
1340
+ ] })
1341
+ }
1342
+ );
1343
+ }
1344
+ );
1345
+ function setRefs(...refs) {
1346
+ return (node) => {
1347
+ for (const r of refs) {
1348
+ if (typeof r === "function") r(node);
1349
+ else if (r && typeof r === "object") r.current = node;
1350
+ }
1351
+ };
1352
+ }
1353
+ var Input = /* @__PURE__ */ react.forwardRef(
1354
+ function Input2(props, ref) {
1355
+ const {
1356
+ label,
1357
+ description,
1358
+ error,
1359
+ success,
63
1360
  required,
64
1361
  disabled,
65
- onBlur,
66
- onFocus,
1362
+ readOnly,
1363
+ size,
1364
+ variant,
1365
+ radius,
1366
+ floatingLabel,
1367
+ startContent,
1368
+ endContent,
1369
+ clearable,
1370
+ onClear,
1371
+ loading,
1372
+ id: idProp,
1373
+ name,
1374
+ className,
1375
+ value: valueProp,
1376
+ defaultValue,
1377
+ onChange,
1378
+ onEnter,
1379
+ onKeyDown,
1380
+ type = "text",
1381
+ prefix,
1382
+ suffix,
1383
+ passwordToggle,
1384
+ showCount,
1385
+ maxLength,
1386
+ placeholder,
67
1387
  ...rest
68
1388
  } = props;
69
- const handleChange = react.useCallback(
70
- (event) => onChange?.(event.target.value, event),
71
- [onChange]
72
- );
73
- const controlled = value !== void 0;
1389
+ const reactId = react.useId();
1390
+ const id = idProp ?? reactId;
1391
+ const [value, setValue] = useControllableState({
1392
+ value: valueProp,
1393
+ defaultValue: defaultValue ?? "",
1394
+ onChange: void 0
1395
+ });
1396
+ const [reveal, setReveal] = react.useState(false);
1397
+ const showToggle = (passwordToggle ?? type === "password") && !disabled && !readOnly;
1398
+ const effectiveType = type === "password" && reveal ? "text" : type;
1399
+ const hasValue = value.length > 0;
1400
+ const showClear = clearable && hasValue && !disabled && !readOnly;
1401
+ const handleChange = (e) => {
1402
+ setValue(e.target.value);
1403
+ onChange?.(e.target.value, e);
1404
+ };
1405
+ const handleKeyDown = (e) => {
1406
+ if (e.key === "Enter") onEnter?.(value);
1407
+ onKeyDown?.(e);
1408
+ };
1409
+ const clear = () => {
1410
+ setValue("");
1411
+ onClear?.();
1412
+ };
74
1413
  return /* @__PURE__ */ jsxRuntime.jsx(
75
1414
  Field,
76
1415
  {
77
1416
  label,
78
1417
  description,
79
1418
  error,
1419
+ success,
80
1420
  required,
81
1421
  disabled,
1422
+ hideLabel: floatingLabel,
82
1423
  id,
83
1424
  className,
84
- children: ({ id: fieldId, describedById, invalid }) => /* @__PURE__ */ jsxRuntime.jsxs(
85
- "select",
1425
+ children: ({ describedById, invalid, valid }) => /* @__PURE__ */ jsxRuntime.jsx(
1426
+ FieldBox,
86
1427
  {
87
- ...rest,
88
- ref,
89
- id: fieldId,
90
- className: "orynn-dropdown",
91
- required,
1428
+ size,
1429
+ variant,
1430
+ radius,
92
1431
  disabled,
93
- "aria-invalid": invalid || void 0,
94
- "aria-describedby": describedById,
95
- ...controlled ? { value } : {},
96
- onChange: handleChange,
97
- onBlur,
98
- onFocus,
99
- children: [
100
- placeholder != null && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: required, children: placeholder }),
101
- options.map((option) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value))
102
- ]
1432
+ invalid,
1433
+ valid,
1434
+ active: hasValue,
1435
+ left: startContent != null || prefix != null ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1436
+ startContent,
1437
+ prefix != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-box__adornment--affix", children: prefix })
1438
+ ] }) : void 0,
1439
+ right: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1440
+ 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
+ ] }),
1445
+ showClear && /* @__PURE__ */ jsxRuntime.jsx(
1446
+ "button",
1447
+ {
1448
+ type: "button",
1449
+ className: "orynn-box__iconbtn",
1450
+ "aria-label": "Clear",
1451
+ tabIndex: -1,
1452
+ onClick: clear,
1453
+ children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, {})
1454
+ }
1455
+ ),
1456
+ showToggle && /* @__PURE__ */ jsxRuntime.jsx(
1457
+ "button",
1458
+ {
1459
+ type: "button",
1460
+ className: "orynn-box__iconbtn",
1461
+ "aria-label": reveal ? "Hide" : "Show",
1462
+ "aria-pressed": reveal,
1463
+ tabIndex: -1,
1464
+ onClick: () => setReveal((v) => !v),
1465
+ children: reveal ? /* @__PURE__ */ jsxRuntime.jsx(EyeOffIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(EyeIcon, {})
1466
+ }
1467
+ ),
1468
+ loading && /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, {}),
1469
+ endContent,
1470
+ valid && !loading && /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { className: "orynn-icon-success" })
1471
+ ] }),
1472
+ floatingLabel: floatingLabel ? /* @__PURE__ */ jsxRuntime.jsx("label", { className: "orynn-box__label", htmlFor: id, children: label }) : void 0,
1473
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1474
+ "input",
1475
+ {
1476
+ ...rest,
1477
+ ref,
1478
+ id,
1479
+ name,
1480
+ className: "orynn-box__field",
1481
+ type: effectiveType,
1482
+ value,
1483
+ disabled,
1484
+ readOnly,
1485
+ required,
1486
+ maxLength,
1487
+ placeholder: floatingLabel ? void 0 : placeholder,
1488
+ "aria-invalid": invalid || void 0,
1489
+ "aria-describedby": describedById,
1490
+ onChange: handleChange,
1491
+ onKeyDown: handleKeyDown
1492
+ }
1493
+ )
103
1494
  }
104
1495
  )
105
1496
  }
106
1497
  );
107
1498
  }
108
1499
  );
109
- var Input = /* @__PURE__ */ react.forwardRef(
110
- function Input2(props, ref) {
1500
+ var clamp = (n, min, max) => Math.min(max ?? Number.POSITIVE_INFINITY, Math.max(min ?? Number.NEGATIVE_INFINITY, n));
1501
+ var round = (n, p) => p == null ? n : Math.round(n * 10 ** p) / 10 ** p;
1502
+ var NumberField = /* @__PURE__ */ react.forwardRef(
1503
+ function NumberField2(props, ref) {
111
1504
  const {
112
1505
  label,
113
1506
  description,
114
1507
  error,
1508
+ success,
1509
+ required,
1510
+ disabled,
1511
+ readOnly,
1512
+ size,
1513
+ variant,
1514
+ radius,
1515
+ floatingLabel,
1516
+ startContent,
1517
+ endContent,
1518
+ id: idProp,
1519
+ name,
115
1520
  className,
116
- value,
1521
+ value: valueProp,
1522
+ defaultValue = null,
117
1523
  onChange,
118
- type = "text",
1524
+ min,
1525
+ max,
1526
+ step = 1,
1527
+ shiftStep,
1528
+ precision,
1529
+ buttons = "stacked",
1530
+ grouping,
1531
+ locale,
1532
+ currency,
1533
+ prefix,
1534
+ suffix,
1535
+ clampOnBlur = true,
1536
+ allowMouseWheel,
1537
+ placeholder,
1538
+ ...rest
1539
+ } = props;
1540
+ const reactId = react.useId();
1541
+ const id = idProp ?? reactId;
1542
+ const [value, setValue] = useControllableState({
1543
+ value: valueProp,
1544
+ defaultValue,
1545
+ onChange: void 0
1546
+ });
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
+ );
1556
+ const seps = react.useMemo(() => {
1557
+ const parts = new Intl.NumberFormat(locale).formatToParts(11111.1);
1558
+ return {
1559
+ group: parts.find((p) => p.type === "group")?.value ?? ",",
1560
+ decimal: parts.find((p) => p.type === "decimal")?.value ?? "."
1561
+ };
1562
+ }, [locale]);
1563
+ const display = react.useCallback(
1564
+ (n) => n == null || Number.isNaN(n) ? "" : `${prefix ?? ""}${fmt.format(n)}${suffix ?? ""}`,
1565
+ [fmt, prefix, suffix]
1566
+ );
1567
+ const parse = react.useCallback(
1568
+ (raw) => {
1569
+ let s = raw;
1570
+ if (prefix) s = s.split(prefix).join("");
1571
+ if (suffix) s = s.split(suffix).join("");
1572
+ s = s.split(seps.group).join("").split(seps.decimal).join(".");
1573
+ s = s.replace(/[^\d.\-]/g, "");
1574
+ if (s === "" || s === "-" || s === ".") return null;
1575
+ const first = s.indexOf(".");
1576
+ 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;
1579
+ },
1580
+ [prefix, suffix, seps]
1581
+ );
1582
+ const [text, setText] = react.useState(() => display(value));
1583
+ const [editing, setEditing] = react.useState(false);
1584
+ react.useEffect(() => {
1585
+ if (!editing) setText(display(value));
1586
+ }, [value, editing, display]);
1587
+ const commit = (n) => {
1588
+ setValue(n);
1589
+ onChange?.(n);
1590
+ };
1591
+ const setNumber = (n) => {
1592
+ commit(n);
1593
+ setText(display(n));
1594
+ };
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));
1599
+ };
1600
+ const innerRef = react.useRef(null);
1601
+ react.useEffect(() => {
1602
+ const el = innerRef.current;
1603
+ if (!el || !allowMouseWheel) return;
1604
+ const onWheel = (e) => {
1605
+ if (document.activeElement !== el) return;
1606
+ e.preventDefault();
1607
+ bump(e.deltaY < 0 ? 1 : -1, e.shiftKey);
1608
+ };
1609
+ el.addEventListener("wheel", onWheel, { passive: false });
1610
+ return () => el.removeEventListener("wheel", onWheel);
1611
+ });
1612
+ const onKeyDown = (e) => {
1613
+ if (disabled || readOnly) return;
1614
+ if (e.key === "ArrowUp") {
1615
+ e.preventDefault();
1616
+ bump(1, e.shiftKey);
1617
+ } else if (e.key === "ArrowDown") {
1618
+ e.preventDefault();
1619
+ bump(-1, e.shiftKey);
1620
+ } else if (e.key === "PageUp") {
1621
+ e.preventDefault();
1622
+ bump(1, true);
1623
+ } else if (e.key === "PageDown") {
1624
+ e.preventDefault();
1625
+ bump(-1, true);
1626
+ } else if (e.key === "Home" && min != null) {
1627
+ e.preventDefault();
1628
+ setNumber(min);
1629
+ } else if (e.key === "End" && max != null) {
1630
+ e.preventDefault();
1631
+ setNumber(max);
1632
+ }
1633
+ };
1634
+ const holdTimer = react.useRef();
1635
+ const startHold = (dir) => {
1636
+ bump(dir);
1637
+ let count = 0;
1638
+ holdTimer.current = setInterval(() => {
1639
+ count += 1;
1640
+ bump(dir, count > 6);
1641
+ }, 110);
1642
+ };
1643
+ const stopHold = () => clearInterval(holdTimer.current);
1644
+ react.useEffect(() => () => clearInterval(holdTimer.current), []);
1645
+ const StepButtons = buttons && /* @__PURE__ */ jsxRuntime.jsxs(
1646
+ "div",
1647
+ {
1648
+ className: `orynn-number__buttons${buttons === "horizontal" ? " orynn-number__buttons--horizontal" : ""}`,
1649
+ children: [
1650
+ /* @__PURE__ */ jsxRuntime.jsx(
1651
+ "button",
1652
+ {
1653
+ type: "button",
1654
+ className: "orynn-number__btn",
1655
+ "aria-label": "Increment",
1656
+ tabIndex: -1,
1657
+ disabled: disabled || readOnly || max != null && (value ?? 0) >= max,
1658
+ onPointerDown: () => startHold(1),
1659
+ onPointerUp: stopHold,
1660
+ onPointerLeave: stopHold,
1661
+ children: /* @__PURE__ */ jsxRuntime.jsx(PlusIcon, {})
1662
+ }
1663
+ ),
1664
+ /* @__PURE__ */ jsxRuntime.jsx(
1665
+ "button",
1666
+ {
1667
+ type: "button",
1668
+ className: "orynn-number__btn",
1669
+ "aria-label": "Decrement",
1670
+ tabIndex: -1,
1671
+ disabled: disabled || readOnly || min != null && (value ?? 0) <= min,
1672
+ onPointerDown: () => startHold(-1),
1673
+ onPointerUp: stopHold,
1674
+ onPointerLeave: stopHold,
1675
+ children: /* @__PURE__ */ jsxRuntime.jsx(MinusIcon, {})
1676
+ }
1677
+ )
1678
+ ]
1679
+ }
1680
+ );
1681
+ const hasValue = value != null;
1682
+ return /* @__PURE__ */ jsxRuntime.jsx(
1683
+ Field,
1684
+ {
1685
+ label,
1686
+ description,
1687
+ error,
1688
+ success,
1689
+ required,
1690
+ disabled,
1691
+ hideLabel: floatingLabel,
1692
+ id,
1693
+ className,
1694
+ children: ({ describedById, invalid, valid }) => /* @__PURE__ */ jsxRuntime.jsx(
1695
+ FieldBox,
1696
+ {
1697
+ size,
1698
+ variant,
1699
+ radius,
1700
+ disabled,
1701
+ invalid,
1702
+ valid,
1703
+ active: hasValue,
1704
+ left: startContent,
1705
+ right: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1706
+ endContent,
1707
+ StepButtons
1708
+ ] }),
1709
+ floatingLabel: floatingLabel ? /* @__PURE__ */ jsxRuntime.jsx("label", { className: "orynn-box__label", htmlFor: id, children: label }) : void 0,
1710
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1711
+ "input",
1712
+ {
1713
+ ...rest,
1714
+ ref: (n) => {
1715
+ innerRef.current = n;
1716
+ if (typeof ref === "function") ref(n);
1717
+ else if (ref) ref.current = n;
1718
+ },
1719
+ id,
1720
+ name,
1721
+ className: "orynn-box__field",
1722
+ type: "text",
1723
+ inputMode: "decimal",
1724
+ role: "spinbutton",
1725
+ "aria-valuenow": value ?? void 0,
1726
+ "aria-valuemin": min,
1727
+ "aria-valuemax": max,
1728
+ value: text,
1729
+ disabled,
1730
+ readOnly,
1731
+ required,
1732
+ placeholder: floatingLabel ? void 0 : placeholder,
1733
+ "aria-invalid": invalid || void 0,
1734
+ "aria-describedby": describedById,
1735
+ onFocus: () => setEditing(true),
1736
+ onChange: (e) => {
1737
+ setText(e.target.value);
1738
+ commit(parse(e.target.value));
1739
+ },
1740
+ onKeyDown,
1741
+ onBlur: () => {
1742
+ setEditing(false);
1743
+ const parsed = parse(text);
1744
+ const final = parsed == null ? null : round(clampOnBlur ? clamp(parsed, min, max) : parsed, precision);
1745
+ if (final !== value) commit(final);
1746
+ setText(display(final));
1747
+ }
1748
+ }
1749
+ )
1750
+ }
1751
+ )
1752
+ }
1753
+ );
1754
+ }
1755
+ );
1756
+ var RadioGroupContext = react.createContext(null);
1757
+ var sizePx2 = { sm: "0.95rem", md: "1.125rem", lg: "1.35rem" };
1758
+ var Radio = /* @__PURE__ */ react.forwardRef(
1759
+ function Radio2(props, ref) {
1760
+ const group = react.useContext(RadioGroupContext);
1761
+ const {
1762
+ value,
1763
+ label,
1764
+ description,
1765
+ disabled: disabledProp,
1766
+ size: sizeProp,
1767
+ card: cardProp,
1768
+ labelPlacement = "end",
1769
+ className,
1770
+ id: idProp,
1771
+ ...rest
1772
+ } = props;
1773
+ const reactId = react.useId();
1774
+ const id = idProp ?? reactId;
1775
+ const checked = group ? group.value === value : void 0;
1776
+ const disabled = disabledProp || group?.disabled || false;
1777
+ const size = sizeProp ?? group?.size ?? "md";
1778
+ const card = cardProp ?? group?.variant === "card";
1779
+ const onChange = (e) => {
1780
+ if (e.target.checked) group?.select(value);
1781
+ };
1782
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1783
+ "label",
1784
+ {
1785
+ className: cx(
1786
+ "orynn-choice",
1787
+ card && "orynn-choice--card",
1788
+ labelPlacement === "start" && "orynn-choice--start",
1789
+ className
1790
+ ),
1791
+ "data-disabled": disabled || void 0,
1792
+ style: { "--orynn-check-size": sizePx2[size] },
1793
+ children: [
1794
+ /* @__PURE__ */ jsxRuntime.jsx(
1795
+ "input",
1796
+ {
1797
+ ...rest,
1798
+ ref,
1799
+ type: "radio",
1800
+ className: "orynn-choice__native",
1801
+ id,
1802
+ name: group?.name,
1803
+ value,
1804
+ checked: group ? checked : void 0,
1805
+ disabled,
1806
+ onChange
1807
+ }
1808
+ ),
1809
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-choice__control orynn-choice__control--radio", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-choice__mark orynn-choice__mark--dot" }) }),
1810
+ (label != null || description != null) && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "orynn-choice__text", children: [
1811
+ label != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-choice__label", children: label }),
1812
+ description != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "orynn-choice__desc", children: description })
1813
+ ] })
1814
+ ]
1815
+ }
1816
+ );
1817
+ }
1818
+ );
1819
+ function RadioGroup(props) {
1820
+ const {
1821
+ label,
1822
+ description,
1823
+ error,
1824
+ success,
1825
+ required,
1826
+ disabled,
1827
+ name: nameProp,
1828
+ size,
1829
+ variant = "default",
1830
+ value: valueProp,
1831
+ defaultValue = null,
1832
+ onChange,
1833
+ orientation = "vertical",
1834
+ options,
1835
+ children,
1836
+ className,
1837
+ id: idProp
1838
+ } = props;
1839
+ const reactId = react.useId();
1840
+ const id = idProp ?? reactId;
1841
+ const name = nameProp ?? id;
1842
+ const [value, setValue] = useControllableState({
1843
+ value: valueProp,
1844
+ defaultValue,
1845
+ onChange: void 0
1846
+ });
1847
+ const select = (v) => {
1848
+ setValue(v);
1849
+ onChange?.(v);
1850
+ };
1851
+ return /* @__PURE__ */ jsxRuntime.jsx(
1852
+ Field,
1853
+ {
1854
+ label,
1855
+ description,
1856
+ error,
1857
+ success,
1858
+ required,
1859
+ disabled,
119
1860
  id,
1861
+ className,
1862
+ children: () => /* @__PURE__ */ jsxRuntime.jsx(RadioGroupContext.Provider, { value: { name, value, select, disabled, size, variant }, children: /* @__PURE__ */ jsxRuntime.jsx(
1863
+ "div",
1864
+ {
1865
+ role: "radiogroup",
1866
+ "aria-labelledby": label != null ? `${id}-label` : void 0,
1867
+ "aria-required": required || void 0,
1868
+ className: "orynn-choice-group",
1869
+ "data-orientation": orientation,
1870
+ children: options ? options.map((o) => /* @__PURE__ */ jsxRuntime.jsx(
1871
+ Radio,
1872
+ {
1873
+ value: o.value,
1874
+ label: o.label,
1875
+ description: o.description,
1876
+ disabled: o.disabled
1877
+ },
1878
+ o.value
1879
+ )) : children
1880
+ }
1881
+ ) })
1882
+ }
1883
+ );
1884
+ }
1885
+ var Textarea = /* @__PURE__ */ react.forwardRef(
1886
+ function Textarea2(props, ref) {
1887
+ const {
1888
+ label,
1889
+ description,
1890
+ error,
1891
+ success,
120
1892
  required,
121
1893
  disabled,
122
1894
  readOnly,
123
- onBlur,
124
- onFocus,
1895
+ size,
1896
+ variant,
1897
+ radius,
1898
+ floatingLabel,
1899
+ clearable,
1900
+ onClear,
1901
+ id: idProp,
1902
+ name,
1903
+ className,
1904
+ value: valueProp,
1905
+ defaultValue,
1906
+ onChange,
1907
+ autoResize,
1908
+ minRows = 3,
1909
+ maxRows = 10,
1910
+ resize,
1911
+ showCount,
1912
+ maxLength,
1913
+ placeholder,
125
1914
  ...rest
126
1915
  } = props;
127
- const handleChange = react.useCallback(
128
- (event) => onChange?.(event.target.value, event),
129
- [onChange]
130
- );
131
- const controlled = value !== void 0;
1916
+ const reactId = react.useId();
1917
+ const id = idProp ?? reactId;
1918
+ const [value, setValue] = useControllableState({
1919
+ value: valueProp,
1920
+ defaultValue: defaultValue ?? "",
1921
+ onChange: void 0
1922
+ });
1923
+ const innerRef = react.useRef(null);
1924
+ react.useImperativeHandle(ref, () => innerRef.current);
1925
+ const resizeToFit = react.useCallback(() => {
1926
+ const el = innerRef.current;
1927
+ if (!el || !autoResize) return;
1928
+ el.style.height = "auto";
1929
+ const cs = getComputedStyle(el);
1930
+ 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;
1934
+ el.style.height = `${Math.min(Math.max(el.scrollHeight, min), max)}px`;
1935
+ el.style.overflowY = el.scrollHeight > max ? "auto" : "hidden";
1936
+ }, [autoResize, minRows, maxRows]);
1937
+ react.useLayoutEffect(resizeToFit, [value, resizeToFit]);
1938
+ const handleChange = (e) => {
1939
+ setValue(e.target.value);
1940
+ onChange?.(e.target.value, e);
1941
+ };
1942
+ const hasValue = value.length > 0;
132
1943
  return /* @__PURE__ */ jsxRuntime.jsx(
133
1944
  Field,
134
1945
  {
135
1946
  label,
136
1947
  description,
137
1948
  error,
1949
+ success,
138
1950
  required,
139
1951
  disabled,
1952
+ hideLabel: floatingLabel,
140
1953
  id,
141
1954
  className,
142
- children: ({ id: fieldId, describedById, invalid }) => /* @__PURE__ */ jsxRuntime.jsx(
143
- "input",
1955
+ children: ({ describedById, invalid, valid }) => /* @__PURE__ */ jsxRuntime.jsxs(
1956
+ FieldBox,
144
1957
  {
145
- ...rest,
146
- ref,
147
- id: fieldId,
148
- className: "orynn-input",
149
- type,
150
- required,
1958
+ textarea: true,
1959
+ size,
1960
+ variant,
1961
+ radius,
151
1962
  disabled,
152
- readOnly,
153
- "aria-invalid": invalid || void 0,
154
- "aria-describedby": describedById,
155
- ...controlled ? { value } : {},
156
- onChange: handleChange,
157
- onBlur,
158
- onFocus
1963
+ invalid,
1964
+ valid,
1965
+ active: hasValue,
1966
+ 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,
1981
+ children: [
1982
+ /* @__PURE__ */ jsxRuntime.jsx(
1983
+ "textarea",
1984
+ {
1985
+ ...rest,
1986
+ ref: innerRef,
1987
+ id,
1988
+ name,
1989
+ className: "orynn-textarea",
1990
+ "data-resize": resize ?? (autoResize ? "none" : "vertical"),
1991
+ rows: minRows,
1992
+ value,
1993
+ disabled,
1994
+ readOnly,
1995
+ required,
1996
+ maxLength,
1997
+ placeholder: floatingLabel ? void 0 : placeholder,
1998
+ "aria-invalid": invalid || void 0,
1999
+ "aria-describedby": describedById,
2000
+ onChange: handleChange
2001
+ }
2002
+ ),
2003
+ showCount && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "orynn-box__footer", children: [
2004
+ value.length,
2005
+ maxLength ? `/${maxLength}` : ""
2006
+ ] })
2007
+ ]
159
2008
  }
160
2009
  )
161
2010
  }
@@ -389,32 +2238,6 @@ function computeDirty(values, initial) {
389
2238
  }
390
2239
  return dirty;
391
2240
  }
392
- function useLatestRef(value) {
393
- const ref = react.useRef(value);
394
- ref.current = value;
395
- return ref;
396
- }
397
-
398
- // src/core/state/use-controllable-state.ts
399
- function useControllableState({
400
- value,
401
- defaultValue,
402
- onChange
403
- }) {
404
- const isControlled = value !== void 0;
405
- const [uncontrolled, setUncontrolled] = react.useState(defaultValue);
406
- const current = isControlled ? value : uncontrolled;
407
- const currentRef = useLatestRef(current);
408
- const onChangeRef = useLatestRef(onChange);
409
- const isControlledRef = useLatestRef(isControlled);
410
- const setValue = react.useCallback((next) => {
411
- const resolved = typeof next === "function" ? next(currentRef.current) : next;
412
- if (Object.is(resolved, currentRef.current)) return;
413
- if (!isControlledRef.current) setUncontrolled(resolved);
414
- onChangeRef.current?.(resolved);
415
- }, []);
416
- return [current, setValue];
417
- }
418
2241
 
419
2242
  // src/core/state/use-fieldset-state.ts
420
2243
  var NO_ERRORS = Object.freeze([]);
@@ -803,14 +2626,87 @@ function Fieldset(props) {
803
2626
  );
804
2627
  }
805
2628
 
2629
+ // src/theme/theme.ts
2630
+ var RADIUS_KEYWORDS = /* @__PURE__ */ new Set(["none", "sm", "md", "lg", "pill"]);
2631
+ var len = (v) => typeof v === "number" ? `${v}px` : v;
2632
+ function contrastFor(color) {
2633
+ const m = /^#?([\da-f]{3}|[\da-f]{6})$/i.exec(color.trim());
2634
+ if (!m?.[1]) return "#ffffff";
2635
+ const hex = m[1].length === 3 ? m[1].replace(/./g, "$&$&") : m[1];
2636
+ const n = Number.parseInt(hex, 16);
2637
+ const toLin = (v) => {
2638
+ const c = v / 255;
2639
+ return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
2640
+ };
2641
+ const lum = 0.2126 * toLin(n >> 16 & 255) + 0.7152 * toLin(n >> 8 & 255) + 0.0722 * toLin(n & 255);
2642
+ return lum > 0.42 ? "#0b0d12" : "#ffffff";
2643
+ }
2644
+ function normalizeTheme(input) {
2645
+ return typeof input === "string" ? { preset: input } : input ?? {};
2646
+ }
2647
+ function resolveThemeVars(config) {
2648
+ const vars = {};
2649
+ if (config.accent) {
2650
+ vars["--orynn-color-accent"] = config.accent;
2651
+ vars["--orynn-color-accent-hover"] = `color-mix(in srgb, ${config.accent} 86%, #000)`;
2652
+ vars["--orynn-color-accent-contrast"] = config.accentContrast ?? contrastFor(config.accent);
2653
+ }
2654
+ if (config.ring) vars["--orynn-color-focus-ring"] = config.ring;
2655
+ if (config.font) vars["--orynn-font-family"] = config.font;
2656
+ if (config.fontSize != null) vars["--orynn-font-size"] = len(config.fontSize);
2657
+ if (config.controlWidth != null) vars["--orynn-control-width"] = len(config.controlWidth);
2658
+ if (config.radius != null) {
2659
+ const r = config.radius;
2660
+ if (r === "md") vars["--orynn-radius"] = "10px";
2661
+ else if (typeof r === "string" && RADIUS_KEYWORDS.has(r))
2662
+ vars["--orynn-radius"] = `var(--orynn-radius-${r})`;
2663
+ else vars["--orynn-radius"] = len(r);
2664
+ }
2665
+ Object.assign(vars, config.vars ?? {});
2666
+ return vars;
2667
+ }
2668
+ var OrynnContext = react.createContext({
2669
+ preset: "default",
2670
+ density: "comfortable",
2671
+ config: {}
2672
+ });
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]
2681
+ );
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
+ }
2688
+ function useOrynnTheme() {
2689
+ return react.useContext(OrynnContext);
2690
+ }
2691
+
806
2692
  // src/types/validation.ts
807
2693
  var FORM_ERROR_KEY = "$form";
808
2694
 
2695
+ exports.Checkbox = Checkbox;
2696
+ exports.CheckboxGroup = CheckboxGroup;
2697
+ exports.DatePicker = DatePicker;
809
2698
  exports.Dropdown = Dropdown;
810
2699
  exports.FORM_ERROR_KEY = FORM_ERROR_KEY;
811
2700
  exports.Field = Field;
2701
+ exports.FieldBox = FieldBox;
812
2702
  exports.Fieldset = Fieldset;
813
2703
  exports.Input = Input;
2704
+ exports.NumberField = NumberField;
2705
+ exports.OrynnProvider = OrynnProvider;
2706
+ exports.Radio = Radio;
2707
+ exports.RadioGroup = RadioGroup;
2708
+ exports.Select = Dropdown;
2709
+ exports.Textarea = Textarea;
814
2710
  exports.createRegistry = createRegistry;
815
2711
  exports.createRuleResolver = createRuleResolver;
816
2712
  exports.defaultRegistry = defaultRegistry;
@@ -818,6 +2714,8 @@ exports.defaultResolver = defaultResolver;
818
2714
  exports.normalizeConfig = normalizeConfig;
819
2715
  exports.registerField = registerField;
820
2716
  exports.registerRule = registerRule;
2717
+ exports.resolveThemeVars = resolveThemeVars;
821
2718
  exports.useFieldsetState = useFieldsetState;
2719
+ exports.useOrynnTheme = useOrynnTheme;
822
2720
  //# sourceMappingURL=index.cjs.map
823
2721
  //# sourceMappingURL=index.cjs.map