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