@skiddph/prui 0.7.0 → 0.9.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.
Files changed (79) hide show
  1. package/dist/app/resource.d.ts +44 -1
  2. package/dist/app.js +1 -1
  3. package/dist/chunks/auth-shell-DQJ4UmUv.js +1208 -0
  4. package/dist/chunks/data-table-toolbar-B-5J2Ozl.js +736 -0
  5. package/dist/core/accordion.d.ts +16 -0
  6. package/dist/core/accordion.js +108 -0
  7. package/dist/core/alert.d.ts +20 -0
  8. package/dist/core/alert.js +63 -0
  9. package/dist/core/anchor.d.ts +35 -0
  10. package/dist/core/anchor.js +39 -0
  11. package/dist/core/breadcrumb.d.ts +30 -0
  12. package/dist/core/breadcrumb.js +67 -0
  13. package/dist/core/button.d.ts +8 -1
  14. package/dist/core/button.js +23 -18
  15. package/dist/core/calendar.d.ts +29 -0
  16. package/dist/core/calendar.js +180 -0
  17. package/dist/core/card.d.ts +5 -1
  18. package/dist/core/card.js +33 -29
  19. package/dist/core/checkbox.d.ts +19 -0
  20. package/dist/core/checkbox.js +55 -0
  21. package/dist/core/combobox.d.ts +32 -0
  22. package/dist/core/combobox.js +193 -0
  23. package/dist/core/date-picker.d.ts +62 -0
  24. package/dist/core/date-picker.js +314 -0
  25. package/dist/core/dialog.d.ts +15 -0
  26. package/dist/core/dialog.js +77 -60
  27. package/dist/core/drawer.d.ts +34 -0
  28. package/dist/core/drawer.js +129 -0
  29. package/dist/core/dropdown.d.ts +8 -4
  30. package/dist/core/dropdown.js +136 -51
  31. package/dist/core/file-upload.d.ts +28 -0
  32. package/dist/core/file-upload.js +111 -0
  33. package/dist/core/index.d.ts +23 -1
  34. package/dist/core/list-nav.d.ts +27 -0
  35. package/dist/core/list-nav.js +51 -0
  36. package/dist/core/modal.d.ts +14 -5
  37. package/dist/core/modal.js +163 -156
  38. package/dist/core/overlay.d.ts +93 -0
  39. package/dist/core/overlay.js +189 -0
  40. package/dist/core/popover.d.ts +29 -0
  41. package/dist/core/popover.js +124 -0
  42. package/dist/core/progress.d.ts +21 -0
  43. package/dist/core/progress.js +59 -0
  44. package/dist/core/radio.d.ts +20 -0
  45. package/dist/core/radio.js +115 -0
  46. package/dist/core/select.d.ts +6 -0
  47. package/dist/core/select.js +187 -98
  48. package/dist/core/skeleton.d.ts +20 -0
  49. package/dist/core/skeleton.js +41 -0
  50. package/dist/core/spinner.d.ts +15 -0
  51. package/dist/core/spinner.js +41 -0
  52. package/dist/core/tabs.js +82 -53
  53. package/dist/core/time-picker.d.ts +68 -0
  54. package/dist/core/time-picker.js +305 -0
  55. package/dist/core/timeline.d.ts +22 -0
  56. package/dist/core/timeline.js +57 -0
  57. package/dist/core/toast.d.ts +36 -0
  58. package/dist/core/toast.js +123 -0
  59. package/dist/core/tooltip.d.ts +23 -0
  60. package/dist/core/tooltip.js +96 -0
  61. package/dist/core/tree-view.d.ts +34 -0
  62. package/dist/core/tree-view.js +133 -0
  63. package/dist/core.js +160 -62
  64. package/dist/data-table/data-table.d.ts +22 -1
  65. package/dist/data-table.js +1 -1
  66. package/dist/forms/form.d.ts +8 -0
  67. package/dist/forms.js +213 -200
  68. package/dist/i18n/index.d.ts +89 -0
  69. package/dist/i18n.js +105 -0
  70. package/dist/index.d.ts +1 -0
  71. package/dist/index.js +263 -155
  72. package/dist/prui-utilities.css +1 -1
  73. package/dist/prui.css +111 -4
  74. package/dist/theme/base.css +110 -3
  75. package/dist/theme/index.d.ts +27 -0
  76. package/dist/theme.js +86 -58
  77. package/package.json +9 -2
  78. package/dist/chunks/auth-shell-BG5vx9eG.js +0 -1054
  79. package/dist/chunks/data-table-toolbar-BUy2EWPy.js +0 -581
@@ -0,0 +1,129 @@
1
+ import { jsx as t, jsxs as Y } from "react/jsx-runtime";
2
+ import * as a from "react";
3
+ import { X as k } from "lucide-react";
4
+ import { cn as P } from "./cn.js";
5
+ import { useReducedMotion as j, useOverlay as A, Portal as E } from "./overlay.js";
6
+ import { usePruiI18n as F } from "../i18n.js";
7
+ const L = {
8
+ left: "left-0 top-0 h-full prui-drawer-panel",
9
+ right: "right-0 top-0 h-full prui-drawer-panel",
10
+ top: "left-0 top-0 w-full prui-drawer-panel",
11
+ bottom: "left-0 bottom-0 w-full prui-drawer-panel"
12
+ }, $ = {
13
+ left: "translateX(0)",
14
+ right: "translateX(0)",
15
+ top: "translateY(0)",
16
+ bottom: "translateY(0)"
17
+ }, q = {
18
+ left: "translateX(-100%)",
19
+ right: "translateX(100%)",
20
+ top: "translateY(-100%)",
21
+ bottom: "translateY(100%)"
22
+ }, h = a.forwardRef(function({ open: n, defaultOpen: i = !1, onOpenChange: s, side: r = "right", size: o, children: v, ariaLabel: w, hideClose: y, className: g, style: x }, S) {
23
+ const [N, D] = a.useState(i), m = n !== void 0, l = m ? n : N, [M, u] = a.useState(l), [p, d] = a.useState(!1), f = j(), { t: R } = F(), c = (e) => {
24
+ m || D(e), s == null || s(e);
25
+ };
26
+ a.useEffect(() => {
27
+ if (l) {
28
+ if (u(!0), f) {
29
+ d(!0);
30
+ return;
31
+ }
32
+ const C = requestAnimationFrame(() => d(!0));
33
+ return () => cancelAnimationFrame(C);
34
+ }
35
+ if (d(!1), f) {
36
+ u(!1);
37
+ return;
38
+ }
39
+ const e = setTimeout(() => u(!1), 300);
40
+ return () => clearTimeout(e);
41
+ }, [l, f]);
42
+ const { ref: T } = A({
43
+ open: l,
44
+ onEscape: () => c(!1)
45
+ });
46
+ if (!M) return null;
47
+ const X = {
48
+ transform: p ? $[r] : q[r],
49
+ transition: "transform var(--prui-duration-slow) var(--prui-ease-out)",
50
+ ...r === "left" || r === "right" ? { width: typeof o == "number" ? `${o}px` : o ?? "400px" } : { maxHeight: typeof o == "number" ? `${o}px` : o ?? "80vh" },
51
+ ...x
52
+ };
53
+ return /* @__PURE__ */ t(E, { children: /* @__PURE__ */ t("div", { ref: T, children: /* @__PURE__ */ t(
54
+ "div",
55
+ {
56
+ className: "prui-drawer-overlay fixed inset-0 z-[var(--prui-z-modal)]",
57
+ style: {
58
+ backgroundColor: "var(--prui-scrim)",
59
+ opacity: p ? 1 : 0,
60
+ transition: "opacity var(--prui-duration-slow) var(--prui-ease-out)"
61
+ },
62
+ onMouseDown: (e) => {
63
+ e.target === e.currentTarget && c(!1);
64
+ },
65
+ children: /* @__PURE__ */ Y(
66
+ "div",
67
+ {
68
+ ref: S,
69
+ role: "dialog",
70
+ "aria-modal": "true",
71
+ "aria-label": w,
72
+ "data-state": p ? "open" : "closed",
73
+ className: P(
74
+ "absolute flex flex-col overflow-y-auto border border-[var(--prui-line)] bg-[var(--prui-surface)]",
75
+ "text-[var(--prui-fg)] shadow-[var(--prui-shadow-modal)] outline-none",
76
+ L[r],
77
+ g
78
+ ),
79
+ style: X,
80
+ children: [
81
+ r === "bottom" ? /* @__PURE__ */ t("div", { "aria-hidden": !0, className: "mx-auto mt-2 mb-1 h-1 w-10 shrink-0 rounded-[var(--prui-radius-full)] bg-[var(--prui-line)]" }) : null,
82
+ v,
83
+ y ? null : /* @__PURE__ */ t(
84
+ "button",
85
+ {
86
+ type: "button",
87
+ "aria-label": R.close,
88
+ onClick: () => c(!1),
89
+ className: "absolute right-3 top-3 rounded-[var(--prui-radius-1)] p-1 text-[var(--prui-dim)] transition-colors hover:bg-[var(--prui-raise)] hover:text-[var(--prui-fg)] cursor-pointer",
90
+ children: /* @__PURE__ */ t(k, { className: "h-4 w-4", "aria-hidden": !0 })
91
+ }
92
+ )
93
+ ]
94
+ }
95
+ )
96
+ }
97
+ ) }) });
98
+ });
99
+ h.displayName = "Drawer";
100
+ const H = a.forwardRef(function(n, i) {
101
+ return /* @__PURE__ */ t(h, { ref: i, side: "bottom", ...n });
102
+ });
103
+ H.displayName = "Sheet";
104
+ const J = {
105
+ name: "Drawer",
106
+ props: [
107
+ { name: "open", type: "boolean", default: "undefined", control: "boolean" },
108
+ { name: "defaultOpen", type: "boolean", default: "false", control: "boolean" },
109
+ { name: "onOpenChange", type: "(open: boolean) => void", default: null, control: "none" },
110
+ { name: "side", type: "'left' | 'right' | 'top' | 'bottom'", default: "'right'", control: "select", options: ["left", "right", "top", "bottom"] },
111
+ { name: "size", type: "number | string (CSS length)", default: "400px / 80vh", control: "text" },
112
+ { name: "ariaLabel", type: "string", default: null, control: "text" },
113
+ { name: "hideClose", type: "boolean", default: "false", control: "boolean" }
114
+ ]
115
+ }, K = {
116
+ name: "Sheet",
117
+ props: [
118
+ { name: "open", type: "boolean", default: "undefined", control: "boolean" },
119
+ { name: "onOpenChange", type: "(open: boolean) => void", default: null, control: "none" },
120
+ { name: "size", type: "number | string (max height)", default: "80vh", control: "text" },
121
+ { name: "ariaLabel", type: "string", default: null, control: "text" }
122
+ ]
123
+ };
124
+ export {
125
+ h as Drawer,
126
+ H as Sheet,
127
+ J as drawerPropsMeta,
128
+ K as sheetPropsMeta
129
+ };
@@ -1,5 +1,12 @@
1
1
  import { PropsMeta } from './props-meta';
2
2
  import * as React from "react";
3
+ /**
4
+ * Dropdown: an anchored menu over a trigger. Full WAI-ARIA menu keyboard
5
+ * support: Enter/Space/ArrowDown open, ArrowUp/Down + Home/End move,
6
+ * printable-character typeahead selects, Escape (topmost overlay only)
7
+ * closes and restores focus to the trigger, Tab closes. Non-modal: no scroll
8
+ * lock and no background inertness.
9
+ */
3
10
  export interface DropdownItem {
4
11
  label: string;
5
12
  onSelect?: () => void;
@@ -17,8 +24,5 @@ export interface DropdownProps {
17
24
  align?: "start" | "end";
18
25
  className?: string;
19
26
  }
20
- export declare const Dropdown: {
21
- ({ items, trigger, children, open: openProp, defaultOpen, onOpenChange, align, className, }: DropdownProps): React.JSX.Element;
22
- displayName: string;
23
- };
27
+ export declare const Dropdown: React.ForwardRefExoticComponent<DropdownProps & React.RefAttributes<HTMLDivElement>>;
24
28
  export declare const dropdownPropsMeta: PropsMeta;
@@ -1,89 +1,174 @@
1
- import { jsxs as p, jsx as a } from "react/jsx-runtime";
2
- import * as l from "react";
3
- import { cn as i } from "./cn.js";
4
- const k = ({
5
- items: f,
1
+ import { jsxs as I, jsx as p } from "react/jsx-runtime";
2
+ import * as o from "react";
3
+ import { cn as x } from "./cn.js";
4
+ import { useOverlayStack as P, useEscapeKey as _ } from "./overlay.js";
5
+ import { typeaheadIndex as $, endIndex as q, homeIndex as B, moveIndex as R } from "./list-nav.js";
6
+ const F = "[role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']", H = o.forwardRef(function({
7
+ items: N,
6
8
  trigger: m,
7
- children: v,
8
- open: u,
9
- defaultOpen: b = !1,
10
- onOpenChange: d,
11
- align: y = "start",
12
- className: w
13
- }) => {
14
- const [x, g] = l.useState(b), c = u !== void 0, n = c ? u : x, s = l.useRef(null), o = (e) => {
15
- c || g(e), d == null || d(e);
16
- };
17
- return l.useEffect(() => {
18
- if (!n) return;
19
- const e = (r) => {
20
- s.current && !s.current.contains(r.target) && o(!1);
21
- }, t = (r) => {
22
- r.key === "Escape" && o(!1);
9
+ children: A,
10
+ open: h,
11
+ defaultOpen: K = !1,
12
+ onOpenChange: b,
13
+ align: C = "start",
14
+ className: S
15
+ }, i) {
16
+ const [j, M] = o.useState(K), D = h !== void 0, r = D ? h : j, v = o.useRef(null), c = o.useRef(null), y = o.useRef(null), [d, w] = o.useState(-1), E = o.useRef({ buffer: "", at: 0 }), s = (e) => {
17
+ var n, t;
18
+ if (D || M(e), b == null || b(e), !e) {
19
+ w(-1), E.current = { buffer: "", at: 0 };
20
+ const a = document.activeElement;
21
+ a && ((n = c.current) != null && n.contains(a)) && ((t = y.current) == null || t.focus());
22
+ }
23
+ }, { setElement: T, isTop: L } = P(r);
24
+ _(r, L, () => {
25
+ s(!1);
26
+ }), o.useEffect(() => {
27
+ if (!r) return;
28
+ const e = (n) => {
29
+ v.current && !v.current.contains(n.target) && s(!1);
23
30
  };
24
- return document.addEventListener("mousedown", e), document.addEventListener("keydown", t), () => {
25
- document.removeEventListener("mousedown", e), document.removeEventListener("keydown", t);
31
+ return document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e);
32
+ });
33
+ const f = o.useCallback(() => {
34
+ const e = c.current;
35
+ return e ? Array.from(e.querySelectorAll(F)) : [];
36
+ }, []), u = (e) => {
37
+ var t;
38
+ w(e), (t = f()[e]) == null || t.focus();
39
+ };
40
+ o.useEffect(() => {
41
+ var t;
42
+ if (!r) return;
43
+ const e = f(), n = e.findIndex((a) => !a.disabled);
44
+ n >= 0 ? (w(n), (t = e[n]) == null || t.focus()) : c.current && c.current.focus();
45
+ }, [r, f]);
46
+ const U = (e) => {
47
+ const n = f(), t = (a) => {
48
+ var l;
49
+ return !((l = n[a]) != null && l.disabled);
26
50
  };
27
- }), /* @__PURE__ */ p("div", { ref: s, className: i("prui-dropdown relative inline-block", w), "data-state": n ? "open" : "closed", children: [
28
- /* @__PURE__ */ a(
29
- "div",
51
+ switch (e.key) {
52
+ case "ArrowDown":
53
+ e.preventDefault(), u(R(d, 1, n.length, { enabled: t }));
54
+ break;
55
+ case "ArrowUp":
56
+ e.preventDefault(), u(R(d, -1, n.length, { enabled: t }));
57
+ break;
58
+ case "Home":
59
+ e.preventDefault(), u(B(n.length, t));
60
+ break;
61
+ case "End":
62
+ e.preventDefault(), u(q(n.length, t));
63
+ break;
64
+ case "Tab":
65
+ s(!1);
66
+ break;
67
+ case "Enter":
68
+ case " ":
69
+ break;
70
+ default:
71
+ if (e.key.length === 1) {
72
+ const a = () => n.map((z) => z.textContent ?? ""), { index: l } = $(a, E.current, e.key, d);
73
+ l >= 0 && l !== d && (e.preventDefault(), u(l));
74
+ }
75
+ }
76
+ }, g = () => {
77
+ s(!0);
78
+ };
79
+ let k;
80
+ if (o.isValidElement(m)) {
81
+ const e = m;
82
+ k = o.cloneElement(e, {
83
+ ref: (n) => {
84
+ y.current = n;
85
+ const t = e.props.ref;
86
+ typeof t == "function" ? t(n) : t && typeof t == "object" && (t.current = n);
87
+ },
88
+ "aria-haspopup": "menu",
89
+ "aria-expanded": r,
90
+ onClick: (n) => {
91
+ const t = e.props.onClick;
92
+ t == null || t(n), n.defaultPrevented || s(!r);
93
+ },
94
+ onKeyDown: (n) => {
95
+ const t = e.props.onKeyDown;
96
+ t == null || t(n), !n.defaultPrevented && (n.key === "ArrowDown" || n.key === "Enter" || n.key === " ") && (r || (n.preventDefault(), g()));
97
+ }
98
+ });
99
+ } else
100
+ k = /* @__PURE__ */ p(
101
+ "button",
30
102
  {
31
- role: "button",
32
- tabIndex: 0,
103
+ type: "button",
104
+ ref: (e) => {
105
+ y.current = e;
106
+ },
33
107
  "aria-haspopup": "menu",
34
- "aria-expanded": n,
35
- onClick: () => o(!n),
108
+ "aria-expanded": r,
109
+ onClick: () => s(!r),
36
110
  onKeyDown: (e) => {
37
- (e.key === "Enter" || e.key === " ") && (e.preventDefault(), o(!n));
111
+ (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") && !r && (e.preventDefault(), g());
38
112
  },
39
113
  children: m
40
114
  }
41
- ),
42
- n ? /* @__PURE__ */ a(
115
+ );
116
+ return /* @__PURE__ */ I("div", { ref: (e) => {
117
+ v.current = e, typeof i == "function" ? i(e) : i && (i.current = e);
118
+ }, className: x("prui-dropdown relative inline-block", S), "data-state": r ? "open" : "closed", children: [
119
+ k,
120
+ r ? /* @__PURE__ */ p(
43
121
  "div",
44
122
  {
123
+ ref: (e) => {
124
+ c.current = e, T((e == null ? void 0 : e.parentElement) ?? null);
125
+ },
45
126
  role: "menu",
46
- className: i(
47
- "prui-dropdown-menu absolute z-50 mt-1 min-w-40 rounded-[var(--prui-radius)] border border-[var(--prui-line)]",
48
- "bg-[var(--prui-surface)] p-1 shadow-lg",
49
- y === "end" ? "right-0" : "left-0"
127
+ tabIndex: -1,
128
+ onKeyDown: U,
129
+ className: x(
130
+ "prui-dropdown-menu absolute z-[var(--prui-z-overlay)] mt-1 min-w-40 rounded-[var(--prui-radius)] border border-[var(--prui-line)]",
131
+ "bg-[var(--prui-surface)] p-1 shadow-[var(--prui-shadow-md)] outline-none",
132
+ C === "end" ? "right-0" : "left-0"
50
133
  ),
51
- children: v ?? (f ?? []).map((e, t) => /* @__PURE__ */ p(l.Fragment, { children: [
52
- e.separatorBefore ? /* @__PURE__ */ a("div", { className: "my-1 h-px bg-[var(--prui-line)]" }) : null,
53
- /* @__PURE__ */ a(
134
+ children: A ?? (N ?? []).map((e, n) => /* @__PURE__ */ I(o.Fragment, { children: [
135
+ e.separatorBefore ? /* @__PURE__ */ p("div", { className: "my-1 h-px bg-[var(--prui-line)]" }) : null,
136
+ /* @__PURE__ */ p(
54
137
  "button",
55
138
  {
56
139
  type: "button",
57
140
  role: "menuitem",
141
+ tabIndex: -1,
58
142
  disabled: e.disabled,
59
143
  onClick: () => {
60
- var r;
61
- e.disabled || (o(!1), (r = e.onSelect) == null || r.call(e));
144
+ var t;
145
+ e.disabled || (s(!1), (t = e.onSelect) == null || t.call(e));
62
146
  },
63
- className: i(
147
+ className: x(
64
148
  "flex w-full items-center rounded-[calc(var(--prui-radius)-1px)] px-2.5 py-1.5 text-left text-sm",
65
- "hover:bg-[var(--prui-raise)] cursor-pointer disabled:opacity-50 disabled:pointer-events-none",
149
+ "hover:bg-[var(--prui-raise)] focus-visible:bg-[var(--prui-raise)] outline-none cursor-pointer disabled:opacity-50 disabled:pointer-events-none",
66
150
  e.danger ? "text-[var(--prui-danger)]" : "text-[var(--prui-fg)]"
67
151
  ),
68
152
  children: e.label
69
153
  }
70
154
  )
71
- ] }, `${e.label}-${t}`))
155
+ ] }, `${e.label}-${n}`))
72
156
  }
73
157
  ) : null
74
158
  ] });
75
- };
76
- k.displayName = "Dropdown";
77
- const E = {
159
+ });
160
+ H.displayName = "Dropdown";
161
+ const Q = {
78
162
  name: "Dropdown",
79
163
  props: [
80
164
  { name: "items", type: "DropdownItem[]", default: "[]", control: "object" },
81
165
  { name: "trigger", type: "ReactNode", default: null, control: "none" },
82
166
  { name: "align", type: "'start' | 'end'", default: "'start'", control: "select", options: ["start", "end"] },
83
- { name: "onOpenChange", type: "(open: boolean) => void", default: null, control: "none" }
167
+ { name: "onOpenChange", type: "(open: boolean) => void", default: null, control: "none" },
168
+ { name: "open", type: "boolean", default: "undefined", control: "boolean" }
84
169
  ]
85
170
  };
86
171
  export {
87
- k as Dropdown,
88
- E as dropdownPropsMeta
172
+ H as Dropdown,
173
+ Q as dropdownPropsMeta
89
174
  };
@@ -0,0 +1,28 @@
1
+ import { PropsMeta } from './props-meta';
2
+ import * as React from "react";
3
+ /**
4
+ * FileUpload: an accessible dropzone + file picker. The dropzone is a real
5
+ * button wrapping a visually-hidden input (keyboard + screen reader
6
+ * native); drag events highlight the zone. Files render as a removable
7
+ * list. Accept/multiple mirror the native input attributes.
8
+ */
9
+ export interface FileUploadProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onError"> {
10
+ /** Accepted file types (native input accept syntax). */
11
+ accept?: string;
12
+ /** Allow multiple files. Default true. */
13
+ multiple?: boolean;
14
+ /** Validate/reject files; return an error message string or null. */
15
+ validate?: (file: File) => string | null;
16
+ onFiles?: (files: File[]) => void;
17
+ /** Control the visible file list externally. */
18
+ files?: File[];
19
+ /** Max file size in bytes per file. */
20
+ maxSize?: number;
21
+ /** Primary dropzone copy. */
22
+ hint?: React.ReactNode;
23
+ disabled?: boolean;
24
+ /** Hide the selected-files list (caller renders their own). */
25
+ hideList?: boolean;
26
+ }
27
+ export declare const FileUpload: React.ForwardRefExoticComponent<FileUploadProps & React.RefAttributes<HTMLInputElement>>;
28
+ export declare const fileUploadPropsMeta: PropsMeta;
@@ -0,0 +1,111 @@
1
+ import { jsxs as h, jsx as t } from "react/jsx-runtime";
2
+ import * as i from "react";
3
+ import { UploadCloud as M, File as S, X as T } from "lucide-react";
4
+ import { cn as k } from "./cn.js";
5
+ import { usePruiI18n as z } from "../i18n.js";
6
+ function C(n) {
7
+ return n < 1024 ? `${n} B` : n < 1024 * 1024 ? `${(n / 1024).toFixed(1)} KB` : `${(n / 1024 / 1024).toFixed(1)} MB`;
8
+ }
9
+ const K = i.forwardRef(function({ accept: F, multiple: v = !0, validate: p, onFiles: o, files: b, maxSize: f, hint: R, disabled: l, hideList: B, className: j, ...A }, s) {
10
+ const { t: y } = z(), [E, N] = i.useState([]), [$, m] = i.useState(!1), [g, I] = i.useState([]), x = b !== void 0, u = x ? b : E, w = i.useRef(null), D = (e) => {
11
+ if (l) return;
12
+ const r = [], a = [];
13
+ for (const d of Array.from(e)) {
14
+ const U = f != null && d.size > f ? `${d.name} is larger than ${C(f)}.` : (p == null ? void 0 : p(d)) ?? null;
15
+ U ? r.push(U) : a.push(d);
16
+ }
17
+ I(r);
18
+ const c = v ? [...u, ...a] : a.slice(-1);
19
+ x || N(c), o == null || o(c);
20
+ }, L = (e) => {
21
+ const r = u.filter((a, c) => c !== e);
22
+ x || N(r), o == null || o(r);
23
+ };
24
+ return /* @__PURE__ */ h("div", { className: k("prui-file-upload flex flex-col gap-2", j), ...A, children: [
25
+ /* @__PURE__ */ h(
26
+ "button",
27
+ {
28
+ type: "button",
29
+ disabled: l,
30
+ "aria-describedby": g.length ? "prui-file-upload-errors" : void 0,
31
+ onClick: () => {
32
+ var e;
33
+ return (e = w.current) == null ? void 0 : e.click();
34
+ },
35
+ onDragOver: (e) => {
36
+ e.preventDefault(), l || m(!0);
37
+ },
38
+ onDragLeave: () => m(!1),
39
+ onDrop: (e) => {
40
+ var r, a;
41
+ e.preventDefault(), m(!1), (a = (r = e.dataTransfer) == null ? void 0 : r.files) != null && a.length && D(e.dataTransfer.files);
42
+ },
43
+ "data-dragging": $ || void 0,
44
+ "data-disabled": l || void 0,
45
+ className: k(
46
+ "flex cursor-pointer flex-col items-center justify-center gap-2 rounded-[var(--prui-radius)] border border-dashed p-8 text-sm",
47
+ "transition-colors outline-none focus-visible:ring-2 focus-visible:ring-[var(--prui-brand)]",
48
+ $ ? "border-[var(--prui-brand)] bg-[var(--prui-brand)]/10 text-[var(--prui-brand)]" : "border-[var(--prui-line)] bg-[var(--prui-background)] text-[var(--prui-dim)] hover:border-[var(--prui-dim)]",
49
+ l && "opacity-50 cursor-not-allowed"
50
+ ),
51
+ children: [
52
+ /* @__PURE__ */ t(M, { className: "h-6 w-6", "aria-hidden": !0 }),
53
+ /* @__PURE__ */ t("span", { children: R ?? y.dropFiles })
54
+ ]
55
+ }
56
+ ),
57
+ /* @__PURE__ */ t(
58
+ "input",
59
+ {
60
+ ref: (e) => {
61
+ w.current = e, typeof s == "function" ? s(e) : s && (s.current = e);
62
+ },
63
+ type: "file",
64
+ accept: F,
65
+ multiple: v,
66
+ disabled: l,
67
+ className: "sr-only",
68
+ tabIndex: -1,
69
+ "aria-hidden": !0,
70
+ onChange: (e) => {
71
+ var r;
72
+ (r = e.target.files) != null && r.length && D(e.target.files), e.target.value = "";
73
+ }
74
+ }
75
+ ),
76
+ g.length > 0 ? /* @__PURE__ */ t("div", { id: "prui-file-upload-errors", role: "alert", className: "flex flex-col gap-1 text-xs text-[var(--prui-danger)]", children: g.map((e, r) => /* @__PURE__ */ t("span", { children: e }, r)) }) : null,
77
+ !B && u.length > 0 ? /* @__PURE__ */ t("ul", { className: "flex flex-col gap-1", "data-testid": "file-upload-list", children: u.map((e, r) => /* @__PURE__ */ h("li", { className: "flex items-center gap-2 rounded-[var(--prui-radius-1)] bg-[var(--prui-raise)] px-3 py-2 text-sm text-[var(--prui-fg)]", children: [
78
+ /* @__PURE__ */ t(S, { className: "h-4 w-4 shrink-0 text-[var(--prui-dim)]", "aria-hidden": !0 }),
79
+ /* @__PURE__ */ t("span", { className: "min-w-0 flex-1 truncate", children: e.name }),
80
+ /* @__PURE__ */ t("span", { className: "text-xs text-[var(--prui-dim)]", children: C(e.size) }),
81
+ /* @__PURE__ */ t(
82
+ "button",
83
+ {
84
+ type: "button",
85
+ "aria-label": `${y.removeFile}: ${e.name}`,
86
+ onClick: () => L(r),
87
+ className: "rounded-[var(--prui-radius-1)] p-1 text-[var(--prui-dim)] transition-colors hover:text-[var(--prui-danger)] cursor-pointer",
88
+ children: /* @__PURE__ */ t(T, { className: "h-3.5 w-3.5", "aria-hidden": !0 })
89
+ }
90
+ )
91
+ ] }, `${e.name}-${r}`)) }) : null
92
+ ] });
93
+ });
94
+ K.displayName = "FileUpload";
95
+ const q = {
96
+ name: "FileUpload",
97
+ props: [
98
+ { name: "accept", type: "string (input accept)", default: "undefined", control: "text" },
99
+ { name: "multiple", type: "boolean", default: "true", control: "boolean" },
100
+ { name: "onFiles", type: "(files: File[]) => void", default: null, control: "none" },
101
+ { name: "files", type: "File[]", default: "internal state", control: "none" },
102
+ { name: "maxSize", type: "number (bytes)", default: "undefined", control: "number" },
103
+ { name: "validate", type: "(file: File) => string | null", default: null, control: "none" },
104
+ { name: "hint", type: "ReactNode", default: "'Drag files here or click to browse'", control: "text" },
105
+ { name: "hideList", type: "boolean", default: "false", control: "boolean" }
106
+ ]
107
+ };
108
+ export {
109
+ K as FileUpload,
110
+ q as fileUploadPropsMeta
111
+ };
@@ -2,6 +2,8 @@
2
2
  export { cn } from './cn';
3
3
  export { resolveSurface, withSurface, type SurfaceProps, type SurfaceRadius, type SurfaceTexture, type SurfaceElevation } from './surface';
4
4
  export type { PropsMeta, PropMeta, PropControl } from './props-meta';
5
+ export { Portal, useReducedMotion, useFocusTrap, useEscapeKey, useScrollLock, useInertBackground, useOverlay, useOverlayStack, pushOverlay, removeOverlay, isTopOverlay, overlayCount, getFocusable, overlayPropsMeta, } from './overlay';
6
+ export { moveIndex, homeIndex, endIndex, typeaheadIndex } from './list-nav';
5
7
  export { Button, buttonPropsMeta, type ButtonProps, type ButtonVariant, type ButtonSize, } from './button';
6
8
  export { Input, inputPropsMeta, Textarea, textareaPropsMeta, type InputProps, type TextareaProps, } from './input';
7
9
  export { Label, labelPropsMeta, Separator, separatorPropsMeta, type LabelProps, type SeparatorProps } from './label';
@@ -9,7 +11,27 @@ export { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, selectPr
9
11
  export { Switch, switchPropsMeta, type SwitchProps } from './switch';
10
12
  export { Tabs, TabsList, TabsTrigger, TabsContent, tabsPropsMeta, type TabsProps, type TabsListProps, type TabsTriggerProps, type TabsContentProps, } from './tabs';
11
13
  export { Badge, badgePropsMeta, Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter, cardPropsMeta, Avatar, avatarPropsMeta, type BadgeProps, type BadgeVariant, type AvatarProps, } from './card';
12
- export { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, dialogPropsMeta, type DialogProps, type DialogContentProps, } from './dialog';
14
+ export { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogBody, DialogFooter, dialogPropsMeta, dialogBodyPropsMeta, type DialogProps, type DialogContentProps, type DialogBodyProps, } from './dialog';
13
15
  export { Dropdown, dropdownPropsMeta, type DropdownProps, type DropdownItem, } from './dropdown';
14
16
  export { ScrollArea, scrollAreaPropsMeta, type ScrollAreaProps } from './scroll-area';
15
17
  export { Modal, confirmModal, modalPropsMeta, confirmModalPropsMeta, type ModalProps, type ModalSize, type ConfirmModalOptions, type ConfirmModalType, } from './modal';
18
+ export { computePosition, useAnchoredPosition, type AnchorSide, type AnchorAlign, type AnchoredPosition } from './anchor';
19
+ export { Spinner, spinnerPropsMeta, type SpinnerProps, type SpinnerSize } from './spinner';
20
+ export { Skeleton, skeletonPropsMeta, type SkeletonProps } from './skeleton';
21
+ export { Alert, alertPropsMeta, type AlertProps, type AlertVariant } from './alert';
22
+ export { Progress, progressPropsMeta, type ProgressProps } from './progress';
23
+ export { Toaster, toast, dismissAllToasts, toasterPropsMeta, toastPropsMeta, type ToasterProps, type ToastOptions, type ToastVariant, type ToastHandle } from './toast';
24
+ export { Tooltip, tooltipPropsMeta, type TooltipProps } from './tooltip';
25
+ export { Popover, popoverPropsMeta, type PopoverProps } from './popover';
26
+ export { Drawer, Sheet, drawerPropsMeta, sheetPropsMeta, type DrawerProps, type DrawerSide } from './drawer';
27
+ export { Checkbox, checkboxPropsMeta, type CheckboxProps } from './checkbox';
28
+ export { RadioGroup, Radio, radioGroupPropsMeta, type RadioGroupProps, type RadioProps } from './radio';
29
+ export { Combobox, comboboxPropsMeta, type ComboboxProps, type ComboboxOption } from './combobox';
30
+ export { Accordion, AccordionItem, accordionPropsMeta, type AccordionProps, type AccordionItemProps } from './accordion';
31
+ export { Breadcrumb, BreadcrumbItem, BreadcrumbEllipsis, breadcrumbPropsMeta, type BreadcrumbProps, type BreadcrumbItemProps } from './breadcrumb';
32
+ export { TreeView, treeViewPropsMeta, type TreeViewProps, type TreeViewItem } from './tree-view';
33
+ export { Timeline, timelinePropsMeta, type TimelineProps, type TimelineItemData, type TimelineVariant } from './timeline';
34
+ export { Calendar, calendarPropsMeta, toDateKey, fromDateKey, type CalendarProps } from './calendar';
35
+ export { DatePicker, DateRangePicker, datePickerPropsMeta, dateRangePickerPropsMeta, type DatePickerProps, type DateRangePickerProps, type DateRange, type MaxRange } from './date-picker';
36
+ export { TimePicker, TimeRangePicker, TimePanel, timePickerPropsMeta, timeRangePickerPropsMeta, type TimePickerProps, type TimeRangePickerProps, type TimeRange } from './time-picker';
37
+ export { FileUpload, fileUploadPropsMeta, type FileUploadProps } from './file-upload';
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Shared list/menus keyboard navigation helpers.
3
+ *
4
+ * Pure index math plus a typeahead buffer, reused by Dropdown, Select,
5
+ * CommandPalette, the nav rail flyout, Combobox and any roving-tabIndex
6
+ * list. No DOM assumptions: callers own focus and rendering.
7
+ */
8
+ /** Next index for ArrowUp/ArrowDown (and Left/Right in horizontal lists). */
9
+ export declare function moveIndex(current: number, delta: 1 | -1, length: number, opts?: {
10
+ loop?: boolean;
11
+ enabled?: (index: number) => boolean;
12
+ }): number;
13
+ /** Home / End clamps. */
14
+ export declare function homeIndex(length: number, enabled?: (i: number) => boolean): number;
15
+ export declare function endIndex(length: number, enabled?: (i: number) => boolean): number;
16
+ /**
17
+ * Typeahead: given the item labels, find the next item whose label starts
18
+ * with the accumulated prefix (searching forward from the active item,
19
+ * wrapping). Case-insensitive; resets after `resetMs` of inactivity.
20
+ */
21
+ export declare function typeaheadIndex(labels: () => string[], state: {
22
+ buffer: string;
23
+ at: number;
24
+ }, key: string, activeIndex: number, resetMs?: number): {
25
+ index: number;
26
+ buffer: string;
27
+ };
@@ -0,0 +1,51 @@
1
+ function m(n, r, t, f = {}) {
2
+ if (t <= 0) return -1;
3
+ const { loop: o = !0, enabled: u } = f;
4
+ if (n < 0)
5
+ return r > 0 ? h(0, 1, t, u) : h(t - 1, -1, t, u);
6
+ let i = n;
7
+ for (let e = 0; e < t; e++) {
8
+ if (i = i + r, i >= t || i < 0) {
9
+ if (!o) return n;
10
+ i = i < 0 ? t - 1 : 0;
11
+ }
12
+ if (!u || u(i)) return i;
13
+ }
14
+ return n;
15
+ }
16
+ function h(n, r, t, f) {
17
+ for (let o = n; o >= 0 && o < t; o += r)
18
+ if (!f || f(o)) return o;
19
+ return -1;
20
+ }
21
+ function p(n, r) {
22
+ return h(0, 1, n, r);
23
+ }
24
+ function w(n, r) {
25
+ return h(n - 1, -1, n, r);
26
+ }
27
+ function a(n, r, t, f, o = 500) {
28
+ var x, b;
29
+ if (t.length !== 1) return { index: f, buffer: r.buffer };
30
+ const u = Date.now(), e = u - r.at < o ? r.buffer + t.toLowerCase() : t.toLowerCase();
31
+ r.buffer = e, r.at = u;
32
+ const c = n().map((s) => s.trim().toLowerCase());
33
+ for (let s = 1; s <= c.length; s++) {
34
+ const d = (f + s) % c.length;
35
+ if ((x = c[d]) != null && x.startsWith(e)) return { index: d, buffer: e };
36
+ }
37
+ if (e.length > 1) {
38
+ const s = e[e.length - 1];
39
+ for (let d = 1; d <= c.length; d++) {
40
+ const l = (f + d) % c.length;
41
+ if ((b = c[l]) != null && b.startsWith(s)) return { index: l, buffer: e };
42
+ }
43
+ }
44
+ return { index: f, buffer: e };
45
+ }
46
+ export {
47
+ w as endIndex,
48
+ p as homeIndex,
49
+ m as moveIndex,
50
+ a as typeaheadIndex
51
+ };