@skiddph/prui 0.7.0 → 0.8.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 +183 -0
- package/dist/core/date-picker.d.ts +41 -0
- package/dist/core/date-picker.js +202 -0
- package/dist/core/dialog.d.ts +8 -0
- package/dist/core/dialog.js +59 -61
- 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 +21 -0
- 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 +178 -97
- 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/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 +152 -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 +83 -0
- package/dist/i18n.js +99 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +256 -156
- 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,57 @@
|
|
|
1
|
+
import { jsx as e, jsxs as i } from "react/jsx-runtime";
|
|
2
|
+
import * as u from "react";
|
|
3
|
+
import { cn as a } from "./cn.js";
|
|
4
|
+
const c = {
|
|
5
|
+
brand: "border-[var(--prui-brand)] bg-[var(--prui-brand)]/20 text-[var(--prui-brand)]",
|
|
6
|
+
success: "border-[var(--prui-ok)] bg-[var(--prui-ok)]/20 text-[var(--prui-ok)]",
|
|
7
|
+
warning: "border-[var(--prui-warn)] bg-[var(--prui-warn)]/20 text-[var(--prui-warn)]",
|
|
8
|
+
danger: "border-[var(--prui-danger)] bg-[var(--prui-danger)]/20 text-[var(--prui-danger)]",
|
|
9
|
+
neutral: "border-[var(--prui-line)] bg-[var(--prui-raise)] text-[var(--prui-dim)]"
|
|
10
|
+
}, m = u.forwardRef(function({ items: n, align: t = "left", className: p, ...d }, o) {
|
|
11
|
+
return /* @__PURE__ */ e("ol", { ref: o, role: "list", className: a("prui-timeline relative flex flex-col", p), ...d, children: n.map((r, l) => {
|
|
12
|
+
const s = r.variant ?? "neutral";
|
|
13
|
+
return /* @__PURE__ */ i("li", { className: "relative flex gap-3 pb-6 last:pb-0", "data-variant": s, children: [
|
|
14
|
+
/* @__PURE__ */ e(
|
|
15
|
+
"span",
|
|
16
|
+
{
|
|
17
|
+
"aria-hidden": !0,
|
|
18
|
+
className: a(
|
|
19
|
+
"absolute top-5 bottom-0 w-px bg-[var(--prui-line)]",
|
|
20
|
+
t === "right" ? "right-[11px]" : "left-[11px]",
|
|
21
|
+
l === n.length - 1 && "hidden"
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
),
|
|
25
|
+
/* @__PURE__ */ e(
|
|
26
|
+
"span",
|
|
27
|
+
{
|
|
28
|
+
"aria-hidden": !0,
|
|
29
|
+
className: a(
|
|
30
|
+
"relative z-[var(--prui-z-content)] flex h-6 w-6 shrink-0 items-center justify-center rounded-[var(--prui-radius-full)] border",
|
|
31
|
+
c[s]
|
|
32
|
+
),
|
|
33
|
+
children: r.icon ?? /* @__PURE__ */ e("span", { className: "h-1.5 w-1.5 rounded-[var(--prui-radius-full)] bg-current" })
|
|
34
|
+
}
|
|
35
|
+
),
|
|
36
|
+
/* @__PURE__ */ i("div", { className: a("min-w-0 flex-1", t === "right" && "text-right"), children: [
|
|
37
|
+
/* @__PURE__ */ i("div", { className: a("flex flex-wrap items-baseline gap-x-2", t === "right" && "flex-row-reverse"), children: [
|
|
38
|
+
/* @__PURE__ */ e("span", { className: "text-sm font-medium text-[var(--prui-fg)]", children: r.title }),
|
|
39
|
+
r.time ? /* @__PURE__ */ e("span", { className: "text-xs text-[var(--prui-dim)]", children: r.time }) : null
|
|
40
|
+
] }),
|
|
41
|
+
r.description ? /* @__PURE__ */ e("div", { className: "mt-0.5 text-sm text-[var(--prui-dim)]", children: r.description }) : null
|
|
42
|
+
] })
|
|
43
|
+
] }, l);
|
|
44
|
+
}) });
|
|
45
|
+
});
|
|
46
|
+
m.displayName = "Timeline";
|
|
47
|
+
const b = {
|
|
48
|
+
name: "Timeline",
|
|
49
|
+
props: [
|
|
50
|
+
{ name: "items", type: "TimelineItemData[]", default: null, control: "object" },
|
|
51
|
+
{ name: "align", type: "'left' | 'alternate' | 'right'", default: "'left'", control: "select", options: ["left", "alternate", "right"] }
|
|
52
|
+
]
|
|
53
|
+
};
|
|
54
|
+
export {
|
|
55
|
+
m as Timeline,
|
|
56
|
+
b as timelinePropsMeta
|
|
57
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { PropsMeta } from './props-meta';
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Toast: transient notifications.
|
|
5
|
+
*
|
|
6
|
+
* - <Toaster /> renders the portaled stack (mount once near the app root).
|
|
7
|
+
* - toast() queues imperatively from anywhere and returns a dismiss handle:
|
|
8
|
+
* const t = toast({ title: "Saved", variant: "success" }); t.dismiss().
|
|
9
|
+
*
|
|
10
|
+
* Announcements go through an aria-live region; the stack never steals
|
|
11
|
+
* focus. prefers-reduced-motion disables the slide/fade.
|
|
12
|
+
*/
|
|
13
|
+
export type ToastVariant = "neutral" | "info" | "success" | "warning" | "danger";
|
|
14
|
+
export interface ToastOptions {
|
|
15
|
+
title: React.ReactNode;
|
|
16
|
+
description?: React.ReactNode;
|
|
17
|
+
variant?: ToastVariant;
|
|
18
|
+
/** Auto-dismiss delay in ms; 0 disables. Default 5000. */
|
|
19
|
+
duration?: number;
|
|
20
|
+
onDismiss?: () => void;
|
|
21
|
+
}
|
|
22
|
+
export interface ToastHandle {
|
|
23
|
+
dismiss: () => void;
|
|
24
|
+
}
|
|
25
|
+
/** Queue a toast; returns a handle to dismiss it early. */
|
|
26
|
+
export declare function toast(options: ToastOptions): ToastHandle;
|
|
27
|
+
/** Dismiss every toast. */
|
|
28
|
+
export declare function dismissAllToasts(): void;
|
|
29
|
+
export interface ToasterProps {
|
|
30
|
+
/** Stack corner. Default bottom-right (bottom-start in RTL). */
|
|
31
|
+
position?: "top-right" | "top-center" | "bottom-right" | "bottom-center" | "bottom-left";
|
|
32
|
+
className?: string;
|
|
33
|
+
}
|
|
34
|
+
export declare function Toaster({ position, className }?: ToasterProps): React.ReactPortal | null;
|
|
35
|
+
export declare const toasterPropsMeta: PropsMeta;
|
|
36
|
+
export declare const toastPropsMeta: PropsMeta;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { jsx as a, jsxs as d } from "react/jsx-runtime";
|
|
2
|
+
import * as m from "react";
|
|
3
|
+
import { createPortal as x } from "react-dom";
|
|
4
|
+
import { XCircle as w, AlertTriangle as N, CheckCircle2 as T, Info as p, X as C } from "lucide-react";
|
|
5
|
+
import { cn as s } from "./cn.js";
|
|
6
|
+
import { usePruiI18n as k } from "../i18n.js";
|
|
7
|
+
import { useReducedMotion as I } from "./overlay.js";
|
|
8
|
+
const f = {
|
|
9
|
+
neutral: { icon: p, accent: "text-[var(--prui-brand)]" },
|
|
10
|
+
info: { icon: p, accent: "text-[var(--prui-brand)]" },
|
|
11
|
+
success: { icon: T, accent: "text-[var(--prui-ok)]" },
|
|
12
|
+
warning: { icon: N, accent: "text-[var(--prui-warn)]" },
|
|
13
|
+
danger: { icon: w, accent: "text-[var(--prui-danger)]" }
|
|
14
|
+
};
|
|
15
|
+
let P = 1, r = [];
|
|
16
|
+
const c = /* @__PURE__ */ new Set();
|
|
17
|
+
function u() {
|
|
18
|
+
for (const o of c) o(r);
|
|
19
|
+
}
|
|
20
|
+
function l(o) {
|
|
21
|
+
var e;
|
|
22
|
+
const t = r.find((i) => i.id === o);
|
|
23
|
+
r = r.filter((i) => i.id !== o), t && ((e = t.onDismiss) == null || e.call(t)), u();
|
|
24
|
+
}
|
|
25
|
+
function D(o) {
|
|
26
|
+
const t = P++;
|
|
27
|
+
r = [...r, { ...o, id: t }], u();
|
|
28
|
+
const e = o.duration ?? 5e3;
|
|
29
|
+
return e > 0 && typeof setTimeout == "function" && setTimeout(() => l(t), e), { dismiss: () => l(t) };
|
|
30
|
+
}
|
|
31
|
+
function X() {
|
|
32
|
+
var t;
|
|
33
|
+
const o = r;
|
|
34
|
+
r = [];
|
|
35
|
+
for (const e of o) (t = e.onDismiss) == null || t.call(e);
|
|
36
|
+
u();
|
|
37
|
+
}
|
|
38
|
+
function R() {
|
|
39
|
+
const [o, t] = m.useState(r);
|
|
40
|
+
return m.useEffect(() => {
|
|
41
|
+
const e = (i) => t([...i]);
|
|
42
|
+
return c.add(e), e(r), () => {
|
|
43
|
+
c.delete(e);
|
|
44
|
+
};
|
|
45
|
+
}, []), o;
|
|
46
|
+
}
|
|
47
|
+
function E({ position: o = "bottom-right", className: t } = {}) {
|
|
48
|
+
const e = R(), { t: i } = k(), b = I();
|
|
49
|
+
if (typeof document > "u") return null;
|
|
50
|
+
const v = {
|
|
51
|
+
"top-right": "top-4 right-4",
|
|
52
|
+
"top-center": "top-4 left-1/2 -translate-x-1/2",
|
|
53
|
+
"bottom-right": "bottom-4 right-4",
|
|
54
|
+
"bottom-center": "bottom-4 left-1/2 -translate-x-1/2",
|
|
55
|
+
"bottom-left": "bottom-4 left-4"
|
|
56
|
+
}[o];
|
|
57
|
+
return x(
|
|
58
|
+
/* @__PURE__ */ a(
|
|
59
|
+
"ol",
|
|
60
|
+
{
|
|
61
|
+
"aria-live": "polite",
|
|
62
|
+
"aria-label": i.notifications,
|
|
63
|
+
className: s("fixed z-[var(--prui-z-toast)] flex w-80 flex-col gap-2", v, t),
|
|
64
|
+
children: e.map((n) => {
|
|
65
|
+
const h = f[n.variant ?? "neutral"].icon, g = f[n.variant ?? "neutral"].accent;
|
|
66
|
+
return /* @__PURE__ */ d(
|
|
67
|
+
"li",
|
|
68
|
+
{
|
|
69
|
+
"data-testid": "toast",
|
|
70
|
+
"data-variant": n.variant ?? "neutral",
|
|
71
|
+
className: s(
|
|
72
|
+
"prui-toast flex items-start gap-3 rounded-[var(--prui-radius)] border border-[var(--prui-line)]",
|
|
73
|
+
"bg-[var(--prui-surface)] p-4 text-sm shadow-[var(--prui-shadow-md)]",
|
|
74
|
+
!b && "prui-anim-slide"
|
|
75
|
+
),
|
|
76
|
+
children: [
|
|
77
|
+
/* @__PURE__ */ a(h, { className: s("mt-0.5 h-4 w-4 shrink-0", g), "aria-hidden": !0 }),
|
|
78
|
+
/* @__PURE__ */ d("div", { className: "min-w-0 flex-1", children: [
|
|
79
|
+
/* @__PURE__ */ a("div", { className: "font-medium text-[var(--prui-fg)]", children: n.title }),
|
|
80
|
+
n.description ? /* @__PURE__ */ a("div", { className: "mt-0.5 text-[var(--prui-dim)]", children: n.description }) : null
|
|
81
|
+
] }),
|
|
82
|
+
/* @__PURE__ */ a(
|
|
83
|
+
"button",
|
|
84
|
+
{
|
|
85
|
+
type: "button",
|
|
86
|
+
"aria-label": i.dismiss,
|
|
87
|
+
onClick: () => l(n.id),
|
|
88
|
+
className: "-m-1 rounded-[var(--prui-radius-1)] p-1 cursor-pointer text-[var(--prui-dim)] transition-colors hover:text-[var(--prui-fg)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-[var(--prui-brand)]",
|
|
89
|
+
children: /* @__PURE__ */ a(C, { className: "h-4 w-4", "aria-hidden": !0 })
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
n.id
|
|
95
|
+
);
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
),
|
|
99
|
+
document.body
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
const L = {
|
|
103
|
+
name: "Toaster",
|
|
104
|
+
props: [
|
|
105
|
+
{ name: "position", type: "'top-right' | 'top-center' | 'bottom-right' | 'bottom-center' | 'bottom-left'", default: "'bottom-right'", control: "select", options: ["top-right", "top-center", "bottom-right", "bottom-center", "bottom-left"] },
|
|
106
|
+
{ name: "className", type: "string", default: "undefined", control: "text" }
|
|
107
|
+
]
|
|
108
|
+
}, q = {
|
|
109
|
+
name: "toast()",
|
|
110
|
+
props: [
|
|
111
|
+
{ name: "title", type: "ReactNode", default: null, control: "text" },
|
|
112
|
+
{ name: "description", type: "ReactNode", default: null, control: "text" },
|
|
113
|
+
{ name: "variant", type: "'neutral' | 'info' | 'success' | 'warning' | 'danger'", default: "'neutral'", control: "select", options: ["neutral", "info", "success", "warning", "danger"] },
|
|
114
|
+
{ name: "duration", type: "number (ms)", default: "5000", control: "number" }
|
|
115
|
+
]
|
|
116
|
+
};
|
|
117
|
+
export {
|
|
118
|
+
E as Toaster,
|
|
119
|
+
X as dismissAllToasts,
|
|
120
|
+
D as toast,
|
|
121
|
+
q as toastPropsMeta,
|
|
122
|
+
L as toasterPropsMeta
|
|
123
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { AnchorSide, AnchorAlign } from './anchor';
|
|
2
|
+
import { PropsMeta } from './props-meta';
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
/**
|
|
5
|
+
* Tooltip: a hover/focus-triggered label for controls. Non-modal (no trap,
|
|
6
|
+
* no scroll lock): Escape (topmost overlay only) hides it, focus stays on
|
|
7
|
+
* the trigger, and it repositions/flips against the viewport. Shows after
|
|
8
|
+
* a short delay, hides immediately; reduced motion skips the fade.
|
|
9
|
+
*/
|
|
10
|
+
export interface TooltipProps {
|
|
11
|
+
/** Tooltip content (plain text recommended). */
|
|
12
|
+
content: React.ReactNode;
|
|
13
|
+
children?: React.ReactNode;
|
|
14
|
+
/** Preferred side of the trigger. Default top. */
|
|
15
|
+
side?: AnchorSide;
|
|
16
|
+
align?: AnchorAlign;
|
|
17
|
+
/** Show delay in ms. Default 300. */
|
|
18
|
+
delay?: number;
|
|
19
|
+
/** Accessible label for the trigger wrapper when the child is not labeled. */
|
|
20
|
+
className?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare const Tooltip: React.ForwardRefExoticComponent<TooltipProps & React.RefAttributes<HTMLSpanElement>>;
|
|
23
|
+
export declare const tooltipPropsMeta: PropsMeta;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { jsx as d, jsxs as R, Fragment as N } from "react/jsx-runtime";
|
|
2
|
+
import * as s from "react";
|
|
3
|
+
import { cn as w } from "./cn.js";
|
|
4
|
+
import { useReducedMotion as F, useOverlayStack as j, useEscapeKey as B, Portal as L } from "./overlay.js";
|
|
5
|
+
import { useAnchoredPosition as P } from "./anchor.js";
|
|
6
|
+
const z = s.forwardRef(function({ content: x, children: p, side: v = "top", align: y = "center", delay: g = 300, className: h }, S) {
|
|
7
|
+
const [i, c] = s.useState(!1), f = s.useRef(null), n = s.useRef(null), b = F(), { setElement: E, isTop: T } = j(i);
|
|
8
|
+
B(i, T, () => c(!1));
|
|
9
|
+
const { ref: M, position: l } = P({ active: i, anchorRef: f, side: v, align: y, offsetHeight: 32 }), a = () => {
|
|
10
|
+
n.current != null && window.clearTimeout(n.current), n.current = window.setTimeout(() => c(!0), g);
|
|
11
|
+
}, u = () => {
|
|
12
|
+
n.current != null && window.clearTimeout(n.current), c(!1);
|
|
13
|
+
};
|
|
14
|
+
s.useEffect(
|
|
15
|
+
() => () => {
|
|
16
|
+
n.current != null && window.clearTimeout(n.current);
|
|
17
|
+
},
|
|
18
|
+
[]
|
|
19
|
+
);
|
|
20
|
+
let m;
|
|
21
|
+
if (s.isValidElement(p)) {
|
|
22
|
+
const t = p;
|
|
23
|
+
m = s.cloneElement(t, {
|
|
24
|
+
ref: (o) => {
|
|
25
|
+
f.current = o;
|
|
26
|
+
const e = t.props.ref;
|
|
27
|
+
typeof e == "function" ? e(o) : e && typeof e == "object" && (e.current = o);
|
|
28
|
+
},
|
|
29
|
+
onFocus: (o) => {
|
|
30
|
+
var e, r;
|
|
31
|
+
(r = (e = t.props).onFocus) == null || r.call(e, o), a();
|
|
32
|
+
},
|
|
33
|
+
onBlur: (o) => {
|
|
34
|
+
var e, r;
|
|
35
|
+
(r = (e = t.props).onBlur) == null || r.call(e, o), u();
|
|
36
|
+
},
|
|
37
|
+
onMouseEnter: (o) => {
|
|
38
|
+
var e, r;
|
|
39
|
+
(r = (e = t.props).onMouseEnter) == null || r.call(e, o), a();
|
|
40
|
+
},
|
|
41
|
+
onMouseLeave: (o) => {
|
|
42
|
+
var e, r;
|
|
43
|
+
(r = (e = t.props).onMouseLeave) == null || r.call(e, o), u();
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
} else
|
|
47
|
+
m = /* @__PURE__ */ d(
|
|
48
|
+
"span",
|
|
49
|
+
{
|
|
50
|
+
ref: f,
|
|
51
|
+
tabIndex: 0,
|
|
52
|
+
className: w("inline-flex", h),
|
|
53
|
+
onMouseEnter: a,
|
|
54
|
+
onMouseLeave: u,
|
|
55
|
+
onFocus: a,
|
|
56
|
+
onBlur: u,
|
|
57
|
+
children: p
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
return /* @__PURE__ */ R(N, { children: [
|
|
61
|
+
m,
|
|
62
|
+
i ? /* @__PURE__ */ d(L, { children: /* @__PURE__ */ d(
|
|
63
|
+
"div",
|
|
64
|
+
{
|
|
65
|
+
ref: (t) => {
|
|
66
|
+
M(t), E((t == null ? void 0 : t.parentElement) ?? null);
|
|
67
|
+
},
|
|
68
|
+
role: "tooltip",
|
|
69
|
+
"data-state": "open",
|
|
70
|
+
"data-side": (l == null ? void 0 : l.side) ?? v,
|
|
71
|
+
className: w(
|
|
72
|
+
"prui-tooltip fixed z-[var(--prui-z-tooltip)] max-w-60 rounded-[var(--prui-radius-1)]",
|
|
73
|
+
"border border-[var(--prui-line)] bg-[var(--prui-surface)] px-2 py-1 text-xs text-[var(--prui-fg)]",
|
|
74
|
+
"shadow-[var(--prui-shadow-md)] pointer-events-none",
|
|
75
|
+
!b && "prui-anim-fade"
|
|
76
|
+
),
|
|
77
|
+
style: l ? { top: l.top, left: l.left } : { top: -9999, left: -9999 },
|
|
78
|
+
children: x
|
|
79
|
+
}
|
|
80
|
+
) }) : null
|
|
81
|
+
] });
|
|
82
|
+
});
|
|
83
|
+
z.displayName = "Tooltip";
|
|
84
|
+
const K = {
|
|
85
|
+
name: "Tooltip",
|
|
86
|
+
props: [
|
|
87
|
+
{ name: "content", type: "ReactNode", default: null, control: "text" },
|
|
88
|
+
{ name: "side", type: "'top' | 'bottom' | 'left' | 'right'", default: "'top'", control: "select", options: ["top", "bottom", "left", "right"] },
|
|
89
|
+
{ name: "align", type: "'start' | 'center' | 'end'", default: "'center'", control: "select", options: ["start", "center", "end"] },
|
|
90
|
+
{ name: "delay", type: "number (ms)", default: "300", control: "number" }
|
|
91
|
+
]
|
|
92
|
+
};
|
|
93
|
+
export {
|
|
94
|
+
z as Tooltip,
|
|
95
|
+
K as tooltipPropsMeta
|
|
96
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { PropsMeta } from './props-meta';
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
/**
|
|
4
|
+
* TreeView: nested expandable nodes (files/folders, categories). Full tree
|
|
5
|
+
* keyboard pattern: ArrowUp/Down move between visible nodes, ArrowRight
|
|
6
|
+
* expands (or moves into), ArrowLeft collapses (or moves to parent),
|
|
7
|
+
* Home/End jump. aria-level/aria-expanded/role=tree/treeitem carry the
|
|
8
|
+
* structure to assistive tech.
|
|
9
|
+
*/
|
|
10
|
+
export interface TreeViewItem {
|
|
11
|
+
id: string;
|
|
12
|
+
label: React.ReactNode;
|
|
13
|
+
children?: TreeViewItem[];
|
|
14
|
+
/** Render as a leaf action (no expander). Default: has children. */
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
/** Extra data passed back on events. */
|
|
17
|
+
meta?: unknown;
|
|
18
|
+
}
|
|
19
|
+
export interface TreeViewProps {
|
|
20
|
+
items: TreeViewItem[];
|
|
21
|
+
/** Expanded node ids (controlled). */
|
|
22
|
+
expanded?: string[];
|
|
23
|
+
defaultExpanded?: string[];
|
|
24
|
+
onExpandedChange?: (ids: string[]) => void;
|
|
25
|
+
/** Selected node id (controlled). */
|
|
26
|
+
selected?: string;
|
|
27
|
+
defaultSelected?: string;
|
|
28
|
+
onSelect?: (id: string, item: TreeViewItem) => void;
|
|
29
|
+
/** Accessible label for the tree landmark. */
|
|
30
|
+
ariaLabel?: string;
|
|
31
|
+
className?: string;
|
|
32
|
+
}
|
|
33
|
+
export declare const TreeView: React.ForwardRefExoticComponent<TreeViewProps & React.RefAttributes<HTMLUListElement>>;
|
|
34
|
+
export declare const treeViewPropsMeta: PropsMeta;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { jsx as p, jsxs as M } from "react/jsx-runtime";
|
|
2
|
+
import * as c from "react";
|
|
3
|
+
import { ChevronRight as H } from "lucide-react";
|
|
4
|
+
import { cn as h } from "./cn.js";
|
|
5
|
+
const O = c.forwardRef(function({ items: D, expanded: b, defaultExpanded: C = [], onExpandedChange: o, selected: v, defaultSelected: I, onSelect: u, ariaLabel: N, className: R }, V) {
|
|
6
|
+
const [j, T] = c.useState(C), [A, E] = c.useState(I), x = b !== void 0, l = x ? b : j, g = v !== void 0, S = g ? v : A, f = c.useRef(/* @__PURE__ */ new Map()), U = (e) => {
|
|
7
|
+
x || T(e), o == null || o(e);
|
|
8
|
+
}, m = (e) => {
|
|
9
|
+
U(l.includes(e) ? l.filter((t) => t !== e) : [...l, e]);
|
|
10
|
+
}, w = (e) => {
|
|
11
|
+
e.item.disabled || (g || E(e.item.id), u == null || u(e.item.id, e.item));
|
|
12
|
+
}, r = [], y = (e, t, a, i) => {
|
|
13
|
+
var d;
|
|
14
|
+
for (const s of e) {
|
|
15
|
+
if (!i) continue;
|
|
16
|
+
const k = !!((d = s.children) != null && d.length), L = { item: s, level: t, hasChildren: k, parentIds: a };
|
|
17
|
+
r.push(L), k && l.includes(s.id) && y(s.children, t + 1, [...a, s.id], i && l.includes(s.id));
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
y(D, 1, [], !0);
|
|
21
|
+
const n = (e) => {
|
|
22
|
+
var t;
|
|
23
|
+
(t = f.current.get(e)) == null || t.focus();
|
|
24
|
+
}, K = (e, t) => {
|
|
25
|
+
const a = r.findIndex((i) => i.item.id === t.item.id);
|
|
26
|
+
switch (e.key) {
|
|
27
|
+
case "ArrowDown":
|
|
28
|
+
a < r.length - 1 && (e.preventDefault(), n(r[a + 1].item.id));
|
|
29
|
+
break;
|
|
30
|
+
case "ArrowUp":
|
|
31
|
+
a > 0 && (e.preventDefault(), n(r[a - 1].item.id));
|
|
32
|
+
break;
|
|
33
|
+
case "ArrowRight":
|
|
34
|
+
if (e.preventDefault(), t.hasChildren && !l.includes(t.item.id)) m(t.item.id);
|
|
35
|
+
else if (t.hasChildren) {
|
|
36
|
+
const i = r.find((d) => d.parentIds[d.parentIds.length - 1] === t.item.id);
|
|
37
|
+
i && n(i.item.id);
|
|
38
|
+
}
|
|
39
|
+
break;
|
|
40
|
+
case "ArrowLeft":
|
|
41
|
+
if (e.preventDefault(), t.hasChildren && l.includes(t.item.id)) m(t.item.id);
|
|
42
|
+
else {
|
|
43
|
+
const i = t.parentIds[t.parentIds.length - 1];
|
|
44
|
+
i && n(i);
|
|
45
|
+
}
|
|
46
|
+
break;
|
|
47
|
+
case "Home":
|
|
48
|
+
r.length && (e.preventDefault(), n(r[0].item.id));
|
|
49
|
+
break;
|
|
50
|
+
case "End":
|
|
51
|
+
r.length && (e.preventDefault(), n(r[r.length - 1].item.id));
|
|
52
|
+
break;
|
|
53
|
+
case "Enter":
|
|
54
|
+
case " ":
|
|
55
|
+
e.preventDefault(), w(t);
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
return /* @__PURE__ */ p(
|
|
60
|
+
"ul",
|
|
61
|
+
{
|
|
62
|
+
ref: V,
|
|
63
|
+
role: "tree",
|
|
64
|
+
"aria-label": N,
|
|
65
|
+
className: h("prui-tree-view flex flex-col gap-0.5 text-sm", R),
|
|
66
|
+
children: r.map((e) => {
|
|
67
|
+
const t = l.includes(e.item.id), a = S === e.item.id;
|
|
68
|
+
return /* @__PURE__ */ p(
|
|
69
|
+
"li",
|
|
70
|
+
{
|
|
71
|
+
role: "treeitem",
|
|
72
|
+
"aria-level": e.level,
|
|
73
|
+
"aria-expanded": e.hasChildren ? t : void 0,
|
|
74
|
+
"aria-selected": a,
|
|
75
|
+
"aria-disabled": e.item.disabled || void 0,
|
|
76
|
+
className: "rounded-[var(--prui-radius-1)]",
|
|
77
|
+
style: { paddingInlineStart: `${(e.level - 1) * 16}px` },
|
|
78
|
+
children: /* @__PURE__ */ M(
|
|
79
|
+
"button",
|
|
80
|
+
{
|
|
81
|
+
type: "button",
|
|
82
|
+
ref: (i) => {
|
|
83
|
+
i ? f.current.set(e.item.id, i) : f.current.delete(e.item.id);
|
|
84
|
+
},
|
|
85
|
+
tabIndex: a ? 0 : -1,
|
|
86
|
+
disabled: e.item.disabled,
|
|
87
|
+
"data-selected": a || void 0,
|
|
88
|
+
onClick: () => {
|
|
89
|
+
e.hasChildren && m(e.item.id), w(e);
|
|
90
|
+
},
|
|
91
|
+
onKeyDown: (i) => K(i, e),
|
|
92
|
+
className: h(
|
|
93
|
+
"flex w-full cursor-pointer items-center gap-1.5 rounded-[var(--prui-radius-1)] px-2 py-1 text-left outline-none",
|
|
94
|
+
"focus-visible:ring-2 focus-visible:ring-[var(--prui-brand)]",
|
|
95
|
+
a ? "bg-[var(--prui-brand)]/15 text-[var(--prui-brand)]" : "text-[var(--prui-fg)] hover:bg-[var(--prui-raise)]",
|
|
96
|
+
e.item.disabled && "opacity-50 cursor-not-allowed"
|
|
97
|
+
),
|
|
98
|
+
children: [
|
|
99
|
+
/* @__PURE__ */ p(
|
|
100
|
+
H,
|
|
101
|
+
{
|
|
102
|
+
className: h("h-3.5 w-3.5 shrink-0 text-[var(--prui-dim)] transition-transform", t && "rotate-90", !e.hasChildren && "invisible"),
|
|
103
|
+
"aria-hidden": !0
|
|
104
|
+
}
|
|
105
|
+
),
|
|
106
|
+
e.item.label
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
},
|
|
111
|
+
e.item.id
|
|
112
|
+
);
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
O.displayName = "TreeView";
|
|
118
|
+
const F = {
|
|
119
|
+
name: "TreeView",
|
|
120
|
+
props: [
|
|
121
|
+
{ name: "items", type: "TreeViewItem[]", default: null, control: "object" },
|
|
122
|
+
{ name: "expanded", type: "string[]", default: "undefined", control: "object" },
|
|
123
|
+
{ name: "defaultExpanded", type: "string[]", default: "[]", control: "object" },
|
|
124
|
+
{ name: "onExpandedChange", type: "(ids: string[]) => void", default: null, control: "none" },
|
|
125
|
+
{ name: "selected", type: "string", default: "undefined", control: "text" },
|
|
126
|
+
{ name: "onSelect", type: "(id, item) => void", default: null, control: "none" },
|
|
127
|
+
{ name: "ariaLabel", type: "string", default: null, control: "text" }
|
|
128
|
+
]
|
|
129
|
+
};
|
|
130
|
+
export {
|
|
131
|
+
O as TreeView,
|
|
132
|
+
F as treeViewPropsMeta
|
|
133
|
+
};
|