@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.
- package/dist/app/resource.d.ts +44 -1
- package/dist/app.js +1 -1
- package/dist/chunks/auth-shell-DQJ4UmUv.js +1208 -0
- package/dist/chunks/data-table-toolbar-B-5J2Ozl.js +736 -0
- package/dist/core/accordion.d.ts +16 -0
- package/dist/core/accordion.js +108 -0
- package/dist/core/alert.d.ts +20 -0
- package/dist/core/alert.js +63 -0
- package/dist/core/anchor.d.ts +35 -0
- package/dist/core/anchor.js +39 -0
- package/dist/core/breadcrumb.d.ts +30 -0
- package/dist/core/breadcrumb.js +67 -0
- package/dist/core/button.d.ts +8 -1
- package/dist/core/button.js +23 -18
- package/dist/core/calendar.d.ts +29 -0
- package/dist/core/calendar.js +180 -0
- package/dist/core/card.d.ts +5 -1
- package/dist/core/card.js +33 -29
- package/dist/core/checkbox.d.ts +19 -0
- package/dist/core/checkbox.js +55 -0
- package/dist/core/combobox.d.ts +32 -0
- package/dist/core/combobox.js +193 -0
- package/dist/core/date-picker.d.ts +62 -0
- package/dist/core/date-picker.js +314 -0
- package/dist/core/dialog.d.ts +15 -0
- package/dist/core/dialog.js +77 -60
- package/dist/core/drawer.d.ts +34 -0
- package/dist/core/drawer.js +129 -0
- package/dist/core/dropdown.d.ts +8 -4
- package/dist/core/dropdown.js +136 -51
- package/dist/core/file-upload.d.ts +28 -0
- package/dist/core/file-upload.js +111 -0
- package/dist/core/index.d.ts +23 -1
- package/dist/core/list-nav.d.ts +27 -0
- package/dist/core/list-nav.js +51 -0
- package/dist/core/modal.d.ts +14 -5
- package/dist/core/modal.js +163 -156
- package/dist/core/overlay.d.ts +93 -0
- package/dist/core/overlay.js +189 -0
- package/dist/core/popover.d.ts +29 -0
- package/dist/core/popover.js +124 -0
- package/dist/core/progress.d.ts +21 -0
- package/dist/core/progress.js +59 -0
- package/dist/core/radio.d.ts +20 -0
- package/dist/core/radio.js +115 -0
- package/dist/core/select.d.ts +6 -0
- package/dist/core/select.js +187 -98
- package/dist/core/skeleton.d.ts +20 -0
- package/dist/core/skeleton.js +41 -0
- package/dist/core/spinner.d.ts +15 -0
- package/dist/core/spinner.js +41 -0
- package/dist/core/tabs.js +82 -53
- package/dist/core/time-picker.d.ts +68 -0
- package/dist/core/time-picker.js +305 -0
- package/dist/core/timeline.d.ts +22 -0
- package/dist/core/timeline.js +57 -0
- package/dist/core/toast.d.ts +36 -0
- package/dist/core/toast.js +123 -0
- package/dist/core/tooltip.d.ts +23 -0
- package/dist/core/tooltip.js +96 -0
- package/dist/core/tree-view.d.ts +34 -0
- package/dist/core/tree-view.js +133 -0
- package/dist/core.js +160 -62
- package/dist/data-table/data-table.d.ts +22 -1
- package/dist/data-table.js +1 -1
- package/dist/forms/form.d.ts +8 -0
- package/dist/forms.js +213 -200
- package/dist/i18n/index.d.ts +89 -0
- package/dist/i18n.js +105 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +263 -155
- package/dist/prui-utilities.css +1 -1
- package/dist/prui.css +111 -4
- package/dist/theme/base.css +110 -3
- package/dist/theme/index.d.ts +27 -0
- package/dist/theme.js +86 -58
- package/package.json +9 -2
- package/dist/chunks/auth-shell-BG5vx9eG.js +0 -1054
- package/dist/chunks/data-table-toolbar-BUy2EWPy.js +0 -581
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import * as s from "react";
|
|
2
|
+
import { createPortal as E } from "react-dom";
|
|
3
|
+
function P({
|
|
4
|
+
children: t,
|
|
5
|
+
container: e
|
|
6
|
+
}) {
|
|
7
|
+
const [r, c] = s.useState(null);
|
|
8
|
+
return s.useEffect(() => {
|
|
9
|
+
c(e ?? (typeof document < "u" ? document.body : null));
|
|
10
|
+
}, [e]), r == null ? null : E(t, r);
|
|
11
|
+
}
|
|
12
|
+
function x() {
|
|
13
|
+
const [t, e] = s.useState(() => typeof window > "u" || !window.matchMedia ? !1 : window.matchMedia("(prefers-reduced-motion: reduce)").matches);
|
|
14
|
+
return s.useEffect(() => {
|
|
15
|
+
if (typeof window > "u" || !window.matchMedia) return;
|
|
16
|
+
const r = window.matchMedia("(prefers-reduced-motion: reduce)"), c = () => e(r.matches);
|
|
17
|
+
return r.addEventListener("change", c), () => r.removeEventListener("change", c);
|
|
18
|
+
}, []), t;
|
|
19
|
+
}
|
|
20
|
+
const v = [
|
|
21
|
+
"a[href]",
|
|
22
|
+
"button:not([disabled])",
|
|
23
|
+
"input:not([disabled])",
|
|
24
|
+
"select:not([disabled])",
|
|
25
|
+
"textarea:not([disabled])",
|
|
26
|
+
"iframe",
|
|
27
|
+
"object",
|
|
28
|
+
"embed",
|
|
29
|
+
"[tabindex]:not([tabindex='-1'])",
|
|
30
|
+
"[contenteditable]:not([contenteditable='false'])"
|
|
31
|
+
].join(",");
|
|
32
|
+
function y(t) {
|
|
33
|
+
return t ? Array.from(t.querySelectorAll(v)).filter(
|
|
34
|
+
(e) => !e.closest("[inert], [aria-hidden='true']") && !e.hasAttribute("hidden")
|
|
35
|
+
) : [];
|
|
36
|
+
}
|
|
37
|
+
const f = [];
|
|
38
|
+
function g(t) {
|
|
39
|
+
const e = Symbol("prui-overlay");
|
|
40
|
+
return f.push({ id: e, element: t }), e;
|
|
41
|
+
}
|
|
42
|
+
function T(t) {
|
|
43
|
+
const e = f.findIndex((r) => r.id === t);
|
|
44
|
+
e >= 0 && f.splice(e, 1);
|
|
45
|
+
}
|
|
46
|
+
function k(t) {
|
|
47
|
+
const e = f[f.length - 1];
|
|
48
|
+
return e != null && e.id === t;
|
|
49
|
+
}
|
|
50
|
+
function I() {
|
|
51
|
+
return f.length;
|
|
52
|
+
}
|
|
53
|
+
function w(t) {
|
|
54
|
+
const e = s.useRef(null), r = s.useRef(null), [c, o] = s.useReducer((i) => i + 1, 0);
|
|
55
|
+
s.useEffect(() => {
|
|
56
|
+
if (!t) return;
|
|
57
|
+
const i = g(null);
|
|
58
|
+
return e.current = i, r.current = f[f.length - 1] ?? null, o(), () => {
|
|
59
|
+
T(i), e.current === i && (e.current = null, r.current = null);
|
|
60
|
+
};
|
|
61
|
+
}, [t]);
|
|
62
|
+
const n = s.useCallback((i) => {
|
|
63
|
+
r.current && (r.current.element = i);
|
|
64
|
+
}, []), u = s.useCallback(() => e.current != null ? k(e.current) : !1, []);
|
|
65
|
+
return { setElement: n, isTop: u };
|
|
66
|
+
}
|
|
67
|
+
function R(t, e, r) {
|
|
68
|
+
const c = s.useRef(r);
|
|
69
|
+
c.current = r, s.useEffect(() => {
|
|
70
|
+
if (!t) return;
|
|
71
|
+
const o = (n) => {
|
|
72
|
+
if (n.key !== "Escape" || !e()) return;
|
|
73
|
+
c.current() === !1 && n.stopPropagation();
|
|
74
|
+
};
|
|
75
|
+
return document.addEventListener("keydown", o, !0), () => document.removeEventListener("keydown", o, !0);
|
|
76
|
+
}, [t, e]);
|
|
77
|
+
}
|
|
78
|
+
let a = 0, h = "", p = "";
|
|
79
|
+
function L() {
|
|
80
|
+
typeof document > "u" || (a++, !(a > 1) && (h = document.body.style.overflow, p = document.body.style.paddingRight, document.body.style.overflow = "hidden", document.body.classList.add("modal-open")));
|
|
81
|
+
}
|
|
82
|
+
function A() {
|
|
83
|
+
typeof document > "u" || (a = Math.max(0, a - 1), !(a > 0) && (document.body.style.overflow = h, document.body.style.paddingRight = p, document.body.classList.remove("modal-open")));
|
|
84
|
+
}
|
|
85
|
+
function S(t) {
|
|
86
|
+
s.useEffect(() => {
|
|
87
|
+
if (t)
|
|
88
|
+
return L(), A;
|
|
89
|
+
}, [t]);
|
|
90
|
+
}
|
|
91
|
+
const b = typeof HTMLElement < "u" && "inert" in HTMLElement.prototype;
|
|
92
|
+
function C(t, e) {
|
|
93
|
+
const r = t.ownerDocument, c = [];
|
|
94
|
+
for (const o of Array.from(r.body.childNodes)) {
|
|
95
|
+
if (!(o instanceof HTMLElement)) continue;
|
|
96
|
+
const n = o.tagName;
|
|
97
|
+
n === "SCRIPT" || n === "STYLE" || n === "LINK" || n === "NOSCRIPT" || n === "META" || n === "TITLE" || n === "TEMPLATE" || o === t || o.contains(t) || t.contains(o) || e.has(o) || o.getAttribute("aria-hidden") !== "true" && (c.push({ el: o, ariaHidden: o.getAttribute("aria-hidden") }), o.setAttribute("aria-hidden", "true"), b && o.setAttribute("inert", ""));
|
|
98
|
+
}
|
|
99
|
+
return () => {
|
|
100
|
+
for (const { el: o, ariaHidden: n } of c)
|
|
101
|
+
n === null ? o.removeAttribute("aria-hidden") : o.setAttribute("aria-hidden", n), b && o.removeAttribute("inert");
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function M(t, e) {
|
|
105
|
+
s.useEffect(() => {
|
|
106
|
+
if (!t || !e || typeof document > "u") return;
|
|
107
|
+
const r = /* @__PURE__ */ new Set();
|
|
108
|
+
for (const c of f)
|
|
109
|
+
c.element && r.add(c.element);
|
|
110
|
+
return C(e, r);
|
|
111
|
+
}, [t, e]);
|
|
112
|
+
}
|
|
113
|
+
function O({ active: t, container: e, initialFocus: r = "first", restoreFocus: c = !0 }) {
|
|
114
|
+
const o = s.useRef(null);
|
|
115
|
+
s.useEffect(() => {
|
|
116
|
+
if (!t) return;
|
|
117
|
+
o.current = document.activeElement ?? null;
|
|
118
|
+
const u = (r === !1 ? null : r === "container" ? e : r === "first" ? y(e)[0] ?? e : r.current ?? null) ?? e;
|
|
119
|
+
u && u.isConnected && !u.contains(document.activeElement) && (!u.hasAttribute("tabindex") && u.tagName !== "INPUT" && u.tagName !== "BUTTON" && u.tagName !== "TEXTAREA" && u.tagName !== "SELECT" && u.tagName !== "A" && !u.isContentEditable && u.setAttribute("tabindex", "-1"), u.focus());
|
|
120
|
+
}, [t, e, r]), s.useEffect(() => {
|
|
121
|
+
if (t) return;
|
|
122
|
+
const n = o.current;
|
|
123
|
+
!n || !c || (o.current = null, n.isConnected && n.focus());
|
|
124
|
+
}, [t, c]), s.useEffect(
|
|
125
|
+
() => () => {
|
|
126
|
+
const n = o.current;
|
|
127
|
+
n && c && n.isConnected && n.focus();
|
|
128
|
+
},
|
|
129
|
+
[c]
|
|
130
|
+
), s.useEffect(() => {
|
|
131
|
+
if (!t) return;
|
|
132
|
+
const n = (u) => {
|
|
133
|
+
if (u.key !== "Tab") return;
|
|
134
|
+
const i = y(e);
|
|
135
|
+
if (i.length === 0) {
|
|
136
|
+
e && !e.contains(document.activeElement) && (u.preventDefault(), e.focus());
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const l = i[0], m = i[i.length - 1], d = document.activeElement;
|
|
140
|
+
if (!d || !(e != null && e.contains(d))) {
|
|
141
|
+
u.preventDefault(), l.focus();
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
u.shiftKey && d === l ? (u.preventDefault(), m.focus()) : !u.shiftKey && d === m && (u.preventDefault(), l.focus());
|
|
145
|
+
};
|
|
146
|
+
return document.addEventListener("keydown", n, !0), () => document.removeEventListener("keydown", n, !0);
|
|
147
|
+
}, [t, e]);
|
|
148
|
+
}
|
|
149
|
+
function B({
|
|
150
|
+
open: t,
|
|
151
|
+
onEscape: e,
|
|
152
|
+
modal: r = !0,
|
|
153
|
+
initialFocus: c = "first",
|
|
154
|
+
restoreFocus: o = !0
|
|
155
|
+
}) {
|
|
156
|
+
const [n, u] = s.useState(null), { setElement: i, isTop: l } = w(t);
|
|
157
|
+
return R(t, l, () => e == null ? void 0 : e()), S(t && r), M(t && r && n != null, n), O({ active: t && r, container: n, initialFocus: c, restoreFocus: o }), { ref: s.useCallback(
|
|
158
|
+
(d) => {
|
|
159
|
+
u(d), i(d);
|
|
160
|
+
},
|
|
161
|
+
[i]
|
|
162
|
+
), element: n, top: l };
|
|
163
|
+
}
|
|
164
|
+
const D = {
|
|
165
|
+
name: "useOverlay",
|
|
166
|
+
props: [
|
|
167
|
+
{ name: "open", type: "boolean", default: null, control: "boolean" },
|
|
168
|
+
{ name: "onEscape", type: "() => boolean | void", default: null, control: "none", description: "Return false to block close (Modal shakes)." },
|
|
169
|
+
{ name: "modal", type: "boolean", default: "true", control: "boolean", description: "Trap focus, lock scroll, inert the background." },
|
|
170
|
+
{ name: "initialFocus", type: 'ref | "first" | "container" | false', default: '"first"', control: "none" },
|
|
171
|
+
{ name: "restoreFocus", type: "boolean", default: "true", control: "boolean" }
|
|
172
|
+
]
|
|
173
|
+
};
|
|
174
|
+
export {
|
|
175
|
+
P as Portal,
|
|
176
|
+
y as getFocusable,
|
|
177
|
+
k as isTopOverlay,
|
|
178
|
+
I as overlayCount,
|
|
179
|
+
D as overlayPropsMeta,
|
|
180
|
+
g as pushOverlay,
|
|
181
|
+
T as removeOverlay,
|
|
182
|
+
R as useEscapeKey,
|
|
183
|
+
O as useFocusTrap,
|
|
184
|
+
M as useInertBackground,
|
|
185
|
+
B as useOverlay,
|
|
186
|
+
w as useOverlayStack,
|
|
187
|
+
x as useReducedMotion,
|
|
188
|
+
S as useScrollLock
|
|
189
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { AnchorSide, AnchorAlign } from './anchor';
|
|
2
|
+
import { PropsMeta } from './props-meta';
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
/**
|
|
5
|
+
* Popover: a click-triggered anchored surface for rich content (filters,
|
|
6
|
+
* pickers, little forms). Non-modal by default: Escape (topmost only) and
|
|
7
|
+
* outside-pointer close it, focus starts inside the panel, and it returns
|
|
8
|
+
* to the trigger on close. `modal` opts into the full trap + inert
|
|
9
|
+
* background.
|
|
10
|
+
*/
|
|
11
|
+
export interface PopoverProps {
|
|
12
|
+
/** The element that toggles the popover (cloned and wired up). */
|
|
13
|
+
trigger?: React.ReactNode;
|
|
14
|
+
children?: React.ReactNode;
|
|
15
|
+
open?: boolean;
|
|
16
|
+
defaultOpen?: boolean;
|
|
17
|
+
onOpenChange?: (open: boolean) => void;
|
|
18
|
+
side?: AnchorSide;
|
|
19
|
+
align?: AnchorAlign;
|
|
20
|
+
/** Focus-trap + inert background instead of a dismissible surface. */
|
|
21
|
+
modal?: boolean;
|
|
22
|
+
/** Accessible label for the popover panel. */
|
|
23
|
+
ariaLabel?: string;
|
|
24
|
+
/** Min panel width in px. Default 200. */
|
|
25
|
+
minWidth?: number;
|
|
26
|
+
className?: string;
|
|
27
|
+
}
|
|
28
|
+
export declare const Popover: React.ForwardRefExoticComponent<PopoverProps & React.RefAttributes<HTMLDivElement>>;
|
|
29
|
+
export declare const popoverPropsMeta: PropsMeta;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { jsxs as T, Fragment as q, jsx as m } from "react/jsx-runtime";
|
|
2
|
+
import * as l from "react";
|
|
3
|
+
import { cn as C } from "./cn.js";
|
|
4
|
+
import { useOverlayStack as D, useEscapeKey as F, Portal as M } from "./overlay.js";
|
|
5
|
+
import { useAnchoredPosition as U } from "./anchor.js";
|
|
6
|
+
import { usePruiI18n as V } from "../i18n.js";
|
|
7
|
+
const B = l.forwardRef(function({
|
|
8
|
+
trigger: u,
|
|
9
|
+
children: E,
|
|
10
|
+
open: b,
|
|
11
|
+
defaultOpen: P = !1,
|
|
12
|
+
onOpenChange: f,
|
|
13
|
+
side: v = "bottom",
|
|
14
|
+
align: R = "start",
|
|
15
|
+
modal: y = !1,
|
|
16
|
+
ariaLabel: w,
|
|
17
|
+
minWidth: k = 200,
|
|
18
|
+
className: A
|
|
19
|
+
}, c) {
|
|
20
|
+
const [N, S] = l.useState(P), x = b !== void 0, n = x ? b : N, s = l.useRef(null), d = l.useRef(null), [g, j] = l.useState(null), { t: L } = V(), i = (e) => {
|
|
21
|
+
var o, t;
|
|
22
|
+
if (x || S(e), f == null || f(e), !e) {
|
|
23
|
+
const r = document.activeElement;
|
|
24
|
+
r && ((o = d.current) != null && o.contains(r)) && ((t = s.current) == null || t.focus());
|
|
25
|
+
}
|
|
26
|
+
}, { setElement: z, isTop: I } = D(n);
|
|
27
|
+
F(n, I, () => i(!1));
|
|
28
|
+
const { ref: K, position: a } = U({ active: n, anchorRef: s, side: v, align: R });
|
|
29
|
+
l.useEffect(() => {
|
|
30
|
+
if (!n || y) return;
|
|
31
|
+
const e = (o) => {
|
|
32
|
+
var r, h;
|
|
33
|
+
const t = o.target;
|
|
34
|
+
(r = s.current) != null && r.contains(t) || (h = d.current) != null && h.contains(t) || i(!1);
|
|
35
|
+
};
|
|
36
|
+
return document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e);
|
|
37
|
+
}), l.useEffect(() => {
|
|
38
|
+
if (!n) return;
|
|
39
|
+
const e = g;
|
|
40
|
+
if (!e) return;
|
|
41
|
+
const o = e.querySelectorAll(
|
|
42
|
+
"a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex='-1'])"
|
|
43
|
+
);
|
|
44
|
+
(Array.from(o).find((r) => !r.hasAttribute("hidden")) ?? e).focus();
|
|
45
|
+
}, [n, g]);
|
|
46
|
+
let p;
|
|
47
|
+
if (l.isValidElement(u)) {
|
|
48
|
+
const e = u;
|
|
49
|
+
p = l.cloneElement(e, {
|
|
50
|
+
ref: (o) => {
|
|
51
|
+
s.current = o;
|
|
52
|
+
const t = e.props.ref;
|
|
53
|
+
typeof t == "function" ? t(o) : t && typeof t == "object" && (t.current = o);
|
|
54
|
+
},
|
|
55
|
+
"aria-haspopup": "dialog",
|
|
56
|
+
"aria-expanded": n,
|
|
57
|
+
onClick: (o) => {
|
|
58
|
+
var t, r;
|
|
59
|
+
(r = (t = e.props).onClick) == null || r.call(t, o), o.defaultPrevented || i(!n);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
} else
|
|
63
|
+
p = /* @__PURE__ */ m(
|
|
64
|
+
"button",
|
|
65
|
+
{
|
|
66
|
+
type: "button",
|
|
67
|
+
ref: (e) => {
|
|
68
|
+
s.current = e;
|
|
69
|
+
},
|
|
70
|
+
"aria-haspopup": "dialog",
|
|
71
|
+
"aria-expanded": n,
|
|
72
|
+
onClick: () => i(!n),
|
|
73
|
+
children: u
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
return /* @__PURE__ */ T(q, { children: [
|
|
77
|
+
p,
|
|
78
|
+
n ? /* @__PURE__ */ m(M, { children: /* @__PURE__ */ m(
|
|
79
|
+
"div",
|
|
80
|
+
{
|
|
81
|
+
ref: (e) => {
|
|
82
|
+
d.current = e, j(e), K(e), z(e), typeof c == "function" ? c(e) : c && (c.current = e);
|
|
83
|
+
},
|
|
84
|
+
role: "dialog",
|
|
85
|
+
"aria-label": w ?? L.dismiss,
|
|
86
|
+
tabIndex: -1,
|
|
87
|
+
"data-state": n ? "open" : "closed",
|
|
88
|
+
"data-side": (a == null ? void 0 : a.side) ?? v,
|
|
89
|
+
className: C(
|
|
90
|
+
"prui-popover fixed z-[var(--prui-z-overlay)] rounded-[var(--prui-radius)] border border-[var(--prui-line)]",
|
|
91
|
+
"bg-[var(--prui-surface)] p-4 text-sm text-[var(--prui-fg)] shadow-[var(--prui-shadow-lg)] outline-none",
|
|
92
|
+
A
|
|
93
|
+
),
|
|
94
|
+
style: {
|
|
95
|
+
top: (a == null ? void 0 : a.top) ?? -9999,
|
|
96
|
+
left: (a == null ? void 0 : a.left) ?? -9999,
|
|
97
|
+
minWidth: k
|
|
98
|
+
},
|
|
99
|
+
onKeyDown: (e) => {
|
|
100
|
+
!y && e.key === "Tab" && i(!1);
|
|
101
|
+
},
|
|
102
|
+
children: E
|
|
103
|
+
}
|
|
104
|
+
) }) : null
|
|
105
|
+
] });
|
|
106
|
+
});
|
|
107
|
+
B.displayName = "Popover";
|
|
108
|
+
const X = {
|
|
109
|
+
name: "Popover",
|
|
110
|
+
props: [
|
|
111
|
+
{ name: "trigger", type: "ReactNode", default: null, control: "none" },
|
|
112
|
+
{ name: "open", type: "boolean", default: "undefined", control: "boolean" },
|
|
113
|
+
{ name: "defaultOpen", type: "boolean", default: "false", control: "boolean" },
|
|
114
|
+
{ name: "onOpenChange", type: "(open: boolean) => void", default: null, control: "none" },
|
|
115
|
+
{ name: "side", type: "'top' | 'bottom' | 'left' | 'right'", default: "'bottom'", control: "select", options: ["top", "bottom", "left", "right"] },
|
|
116
|
+
{ name: "align", type: "'start' | 'center' | 'end'", default: "'start'", control: "select", options: ["start", "center", "end"] },
|
|
117
|
+
{ name: "modal", type: "boolean", default: "false", control: "boolean" },
|
|
118
|
+
{ name: "ariaLabel", type: "string", default: "undefined", control: "text" }
|
|
119
|
+
]
|
|
120
|
+
};
|
|
121
|
+
export {
|
|
122
|
+
B as Popover,
|
|
123
|
+
X as popoverPropsMeta
|
|
124
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { PropsMeta } from './props-meta';
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Progress: a linear progress indicator. role="progressbar" with the full
|
|
5
|
+
* aria value triple; an indeterminate mode communicates activity without a
|
|
6
|
+
* known percentage.
|
|
7
|
+
*/
|
|
8
|
+
export interface ProgressProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
9
|
+
/** Current value; omit for indeterminate. */
|
|
10
|
+
value?: number;
|
|
11
|
+
/** Minimum (default 0). */
|
|
12
|
+
min?: number;
|
|
13
|
+
/** Maximum (default 100). */
|
|
14
|
+
max?: number;
|
|
15
|
+
/** Render the numeric percentage label. */
|
|
16
|
+
showValue?: boolean;
|
|
17
|
+
/** Color variant. */
|
|
18
|
+
variant?: "brand" | "success" | "warning" | "danger";
|
|
19
|
+
}
|
|
20
|
+
export declare const Progress: React.ForwardRefExoticComponent<ProgressProps & React.RefAttributes<HTMLDivElement>>;
|
|
21
|
+
export declare const progressPropsMeta: PropsMeta;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { jsxs as o, jsx as d } from "react/jsx-runtime";
|
|
2
|
+
import * as v from "react";
|
|
3
|
+
import { cn as l } from "./cn.js";
|
|
4
|
+
const b = {
|
|
5
|
+
brand: "bg-[var(--prui-brand)]",
|
|
6
|
+
success: "bg-[var(--prui-ok)]",
|
|
7
|
+
warning: "bg-[var(--prui-warn)]",
|
|
8
|
+
danger: "bg-[var(--prui-danger)]"
|
|
9
|
+
}, g = v.forwardRef(
|
|
10
|
+
({ className: u, value: s, min: a = 0, max: e = 100, showValue: m = !1, variant: p = "brand", ...i }, c) => {
|
|
11
|
+
const r = s === void 0, n = r ? void 0 : Math.min(Math.max(s, a), e), t = n === void 0 ? void 0 : (n - a) / (e - a) * 100;
|
|
12
|
+
return /* @__PURE__ */ o("div", { className: l("flex items-center gap-2", u), children: [
|
|
13
|
+
/* @__PURE__ */ d(
|
|
14
|
+
"div",
|
|
15
|
+
{
|
|
16
|
+
ref: c,
|
|
17
|
+
role: "progressbar",
|
|
18
|
+
"aria-valuenow": r ? void 0 : n,
|
|
19
|
+
"aria-valuemin": a,
|
|
20
|
+
"aria-valuemax": e,
|
|
21
|
+
"aria-label": i["aria-label"] ?? "Progress",
|
|
22
|
+
"data-state": r ? "indeterminate" : "determinate",
|
|
23
|
+
className: "prui-progress h-2 w-full overflow-hidden rounded-[var(--prui-radius-full)] bg-[var(--prui-raise)]",
|
|
24
|
+
...i,
|
|
25
|
+
children: /* @__PURE__ */ d(
|
|
26
|
+
"div",
|
|
27
|
+
{
|
|
28
|
+
className: l(
|
|
29
|
+
"h-full rounded-[var(--prui-radius-full)] transition-[width] duration-[var(--prui-duration-base)] ease-[var(--prui-ease-out)]",
|
|
30
|
+
b[p],
|
|
31
|
+
r && "prui-progress-indeterminate w-1/3"
|
|
32
|
+
),
|
|
33
|
+
style: r ? void 0 : { width: `${t}%` }
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
),
|
|
38
|
+
m && t !== void 0 ? /* @__PURE__ */ o("span", { className: "text-xs tabular-nums text-[var(--prui-dim)]", children: [
|
|
39
|
+
Math.round(t),
|
|
40
|
+
"%"
|
|
41
|
+
] }) : null
|
|
42
|
+
] });
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
g.displayName = "Progress";
|
|
46
|
+
const w = {
|
|
47
|
+
name: "Progress",
|
|
48
|
+
props: [
|
|
49
|
+
{ name: "value", type: "number", default: "undefined (indeterminate)", control: "number" },
|
|
50
|
+
{ name: "min", type: "number", default: "0", control: "number" },
|
|
51
|
+
{ name: "max", type: "number", default: "100", control: "number" },
|
|
52
|
+
{ name: "showValue", type: "boolean", default: "false", control: "boolean" },
|
|
53
|
+
{ name: "variant", type: "'brand' | 'success' | 'warning' | 'danger'", default: "'brand'", control: "select", options: ["brand", "success", "warning", "danger"] }
|
|
54
|
+
]
|
|
55
|
+
};
|
|
56
|
+
export {
|
|
57
|
+
g as Progress,
|
|
58
|
+
w as progressPropsMeta
|
|
59
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { PropsMeta } from './props-meta';
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
export interface RadioGroupProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> {
|
|
4
|
+
value?: string;
|
|
5
|
+
defaultValue?: string;
|
|
6
|
+
onChange?: (value: string) => void;
|
|
7
|
+
/** Group label exposed to assistive tech. */
|
|
8
|
+
label?: string;
|
|
9
|
+
name?: string;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
orientation?: "vertical" | "horizontal";
|
|
12
|
+
}
|
|
13
|
+
export declare const RadioGroup: React.ForwardRefExoticComponent<RadioGroupProps & React.RefAttributes<HTMLDivElement>>;
|
|
14
|
+
export interface RadioProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "value" | "onChange" | "checked"> {
|
|
15
|
+
value: string;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
label?: React.ReactNode;
|
|
18
|
+
}
|
|
19
|
+
export declare const Radio: React.ForwardRefExoticComponent<RadioProps & React.RefAttributes<HTMLButtonElement>>;
|
|
20
|
+
export declare const radioGroupPropsMeta: PropsMeta;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { jsx as v, jsxs as M } from "react/jsx-runtime";
|
|
2
|
+
import * as n from "react";
|
|
3
|
+
import { Circle as P } from "lucide-react";
|
|
4
|
+
import { cn as G } from "./cn.js";
|
|
5
|
+
const I = n.createContext(null);
|
|
6
|
+
function S() {
|
|
7
|
+
const m = n.useContext(I);
|
|
8
|
+
if (!m) throw new Error("Radio must be used inside <RadioGroup>");
|
|
9
|
+
return m;
|
|
10
|
+
}
|
|
11
|
+
const U = n.forwardRef(function({ value: d, defaultValue: a, onChange: i, label: R, name: h, disabled: f, orientation: b = "vertical", className: s, onKeyDown: c, children: y, ...p }, k) {
|
|
12
|
+
const l = n.useId(), x = h ?? `prui-radio-${l}`, [N, o] = n.useState(a ?? ""), j = d !== void 0 ? d : N, [V, A] = n.useState([]), w = n.useRef(/* @__PURE__ */ new Map()), E = (e) => {
|
|
13
|
+
d === void 0 && o(e), i == null || i(e);
|
|
14
|
+
}, z = n.useCallback((e, t) => {
|
|
15
|
+
t ? (w.current.set(e, t), A((u) => u.includes(e) ? u : [...u, e])) : (w.current.delete(e), A((u) => u.filter((g) => g !== e)));
|
|
16
|
+
}, []), D = (e) => {
|
|
17
|
+
if (c == null || c(e), e.defaultPrevented) return;
|
|
18
|
+
const t = V.map((r) => w.current.get(r)).filter((r) => !!r && !r.disabled);
|
|
19
|
+
if (t.length === 0) return;
|
|
20
|
+
const u = t.findIndex((r) => r.tabIndex === 0), g = e.key === "ArrowDown" || e.key === "ArrowRight" ? 1 : e.key === "ArrowUp" || e.key === "ArrowLeft" ? -1 : 0;
|
|
21
|
+
if (g !== 0) {
|
|
22
|
+
e.preventDefault();
|
|
23
|
+
const r = t[(u + g + t.length) % t.length];
|
|
24
|
+
r.focus(), r.click();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (e.key === "Home") {
|
|
28
|
+
e.preventDefault(), t[0].focus(), t[0].click();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (e.key === "End") {
|
|
32
|
+
e.preventDefault();
|
|
33
|
+
const r = t[t.length - 1];
|
|
34
|
+
r.focus(), r.click();
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
return /* @__PURE__ */ v(I.Provider, { value: { value: j, name: x, setValue: E, register: z, firstValue: V[0] ?? null }, children: /* @__PURE__ */ v(
|
|
38
|
+
"div",
|
|
39
|
+
{
|
|
40
|
+
ref: k,
|
|
41
|
+
role: "radiogroup",
|
|
42
|
+
"aria-label": R,
|
|
43
|
+
"aria-disabled": f,
|
|
44
|
+
"data-orientation": b,
|
|
45
|
+
onKeyDown: D,
|
|
46
|
+
className: G(
|
|
47
|
+
"prui-radio-group flex gap-2",
|
|
48
|
+
b === "vertical" ? "flex-col" : "flex-row flex-wrap items-center",
|
|
49
|
+
s
|
|
50
|
+
),
|
|
51
|
+
...p,
|
|
52
|
+
children: y
|
|
53
|
+
}
|
|
54
|
+
) });
|
|
55
|
+
});
|
|
56
|
+
U.displayName = "RadioGroup";
|
|
57
|
+
const H = n.forwardRef(function({ className: d, value: a, disabled: i, label: R, children: h, onClick: f, ...b }, s) {
|
|
58
|
+
const { value: c, setValue: y, register: p, firstValue: k } = S(), l = c === a, x = n.useRef(null);
|
|
59
|
+
return n.useEffect(() => (p(a, x.current), () => p(a, null)), [a, p]), /* @__PURE__ */ M(
|
|
60
|
+
"button",
|
|
61
|
+
{
|
|
62
|
+
ref: (o) => {
|
|
63
|
+
x.current = o, typeof s == "function" ? s(o) : s && (s.current = o);
|
|
64
|
+
},
|
|
65
|
+
type: "button",
|
|
66
|
+
role: "radio",
|
|
67
|
+
"aria-checked": l,
|
|
68
|
+
"aria-disabled": i,
|
|
69
|
+
"data-state": l ? "checked" : "unchecked",
|
|
70
|
+
tabIndex: l || !c && a === k ? 0 : -1,
|
|
71
|
+
disabled: i,
|
|
72
|
+
onClick: (o) => {
|
|
73
|
+
f == null || f(o), !o.defaultPrevented && !i && y(a);
|
|
74
|
+
},
|
|
75
|
+
className: G(
|
|
76
|
+
"prui-radio inline-flex items-center gap-2 text-sm cursor-pointer outline-none",
|
|
77
|
+
"focus-visible:ring-2 focus-visible:ring-[var(--prui-brand)] focus-visible:ring-offset-1 rounded-[var(--prui-radius-1)]",
|
|
78
|
+
i ? "opacity-50 cursor-not-allowed" : "text-[var(--prui-fg)]",
|
|
79
|
+
d
|
|
80
|
+
),
|
|
81
|
+
...b,
|
|
82
|
+
children: [
|
|
83
|
+
/* @__PURE__ */ v(
|
|
84
|
+
"span",
|
|
85
|
+
{
|
|
86
|
+
"aria-hidden": !0,
|
|
87
|
+
className: G(
|
|
88
|
+
"inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-[var(--prui-radius-full)] border",
|
|
89
|
+
"transition-colors duration-[var(--prui-duration-fast)]",
|
|
90
|
+
l ? "border-[var(--prui-brand)]" : "border-[var(--prui-line)] bg-[var(--prui-background)]"
|
|
91
|
+
),
|
|
92
|
+
children: l ? /* @__PURE__ */ v(P, { className: "h-2 w-2 fill-[var(--prui-brand)] text-[var(--prui-brand)]" }) : null
|
|
93
|
+
}
|
|
94
|
+
),
|
|
95
|
+
R ?? h
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
});
|
|
100
|
+
H.displayName = "Radio";
|
|
101
|
+
const B = {
|
|
102
|
+
name: "RadioGroup",
|
|
103
|
+
props: [
|
|
104
|
+
{ name: "value", type: "string", default: "undefined", control: "text" },
|
|
105
|
+
{ name: "defaultValue", type: "string", default: "undefined", control: "text" },
|
|
106
|
+
{ name: "onChange", type: "(value: string) => void", default: null, control: "none" },
|
|
107
|
+
{ name: "label", type: "string (group label)", default: null, control: "text" },
|
|
108
|
+
{ name: "orientation", type: "'vertical' | 'horizontal'", default: "'vertical'", control: "select", options: ["vertical", "horizontal"] }
|
|
109
|
+
]
|
|
110
|
+
};
|
|
111
|
+
export {
|
|
112
|
+
H as Radio,
|
|
113
|
+
U as RadioGroup,
|
|
114
|
+
B as radioGroupPropsMeta
|
|
115
|
+
};
|
package/dist/core/select.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { PropsMeta } from './props-meta';
|
|
2
2
|
import * as React from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Select: a native-feeling single-select listbox. Full keyboard support:
|
|
5
|
+
* Enter/Space/ArrowDown open, ArrowUp/Down/Home/End move, Enter/Space pick,
|
|
6
|
+
* Escape (topmost overlay only) closes and restores focus, Tab closes,
|
|
7
|
+
* printable-character typeahead jumps. Non-modal: no scroll lock.
|
|
8
|
+
*/
|
|
3
9
|
export interface SelectOption {
|
|
4
10
|
label: string;
|
|
5
11
|
value: string;
|