@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,68 @@
|
|
|
1
|
+
import { PropsMeta } from './props-meta';
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
/**
|
|
4
|
+
* TimePicker / TimeRangePicker: time selection without a date library.
|
|
5
|
+
* Values are plain "HH:mm" strings (or "HH:mm:ss" with seconds). The
|
|
6
|
+
* picker is a popover with column grids (hours / minutes / seconds) using
|
|
7
|
+
* listbox semantics; arrows move within a column, Enter/Space pick,
|
|
8
|
+
* Escape (topmost) closes, and outside clicks dismiss. TimePanel is the
|
|
9
|
+
* inline grid pair (no popover) that DatePicker composes when
|
|
10
|
+
* timepicker is on.
|
|
11
|
+
*/
|
|
12
|
+
export interface TimePickerProps {
|
|
13
|
+
/** "HH:mm" or "HH:mm:ss" when seconds is set. */
|
|
14
|
+
value?: string;
|
|
15
|
+
defaultValue?: string;
|
|
16
|
+
onChange?: (time: string) => void;
|
|
17
|
+
/** Step between minute options, in minutes. Default 1. */
|
|
18
|
+
minuteStep?: number;
|
|
19
|
+
/** Include a seconds column ("HH:mm:ss" values). */
|
|
20
|
+
seconds?: boolean;
|
|
21
|
+
/** Earliest selectable time, inclusive. */
|
|
22
|
+
min?: string;
|
|
23
|
+
/** Latest selectable time, inclusive. */
|
|
24
|
+
max?: string;
|
|
25
|
+
disabled?: boolean;
|
|
26
|
+
placeholder?: string;
|
|
27
|
+
ariaLabel?: string;
|
|
28
|
+
className?: string;
|
|
29
|
+
}
|
|
30
|
+
export type TimeRange = {
|
|
31
|
+
from?: string;
|
|
32
|
+
to?: string;
|
|
33
|
+
};
|
|
34
|
+
export interface TimeRangePickerProps {
|
|
35
|
+
value?: TimeRange;
|
|
36
|
+
defaultValue?: TimeRange;
|
|
37
|
+
onChange?: (range: TimeRange) => void;
|
|
38
|
+
minuteStep?: number;
|
|
39
|
+
seconds?: boolean;
|
|
40
|
+
min?: string;
|
|
41
|
+
max?: string;
|
|
42
|
+
disabled?: boolean;
|
|
43
|
+
placeholder?: string;
|
|
44
|
+
ariaLabel?: string;
|
|
45
|
+
className?: string;
|
|
46
|
+
}
|
|
47
|
+
interface TimePanelProps {
|
|
48
|
+
value?: string;
|
|
49
|
+
onSelect: (time: string) => void;
|
|
50
|
+
minuteStep?: number;
|
|
51
|
+
seconds?: boolean;
|
|
52
|
+
min?: string;
|
|
53
|
+
max?: string;
|
|
54
|
+
/** Hide the column headers (used by the range picker's side-by-side pair). */
|
|
55
|
+
compact?: boolean;
|
|
56
|
+
className?: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* TimePanel: the inline hour/minute(/second) grids. TimePicker renders it
|
|
60
|
+
* in a popover; DatePicker composes it directly; it is exported for
|
|
61
|
+
* custom compositions.
|
|
62
|
+
*/
|
|
63
|
+
export declare function TimePanel({ value, onSelect, minuteStep, seconds, min, max, compact, className }: TimePanelProps): React.JSX.Element;
|
|
64
|
+
export declare const TimePicker: React.ForwardRefExoticComponent<TimePickerProps & React.RefAttributes<HTMLInputElement>>;
|
|
65
|
+
export declare const TimeRangePicker: React.ForwardRefExoticComponent<TimeRangePickerProps & React.RefAttributes<HTMLInputElement>>;
|
|
66
|
+
export declare const timePickerPropsMeta: PropsMeta;
|
|
67
|
+
export declare const timeRangePickerPropsMeta: PropsMeta;
|
|
68
|
+
export {};
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { jsxs as N, Fragment as U, jsx as u } from "react/jsx-runtime";
|
|
2
|
+
import * as R from "react";
|
|
3
|
+
import { Clock as G } from "lucide-react";
|
|
4
|
+
import { cn as I } from "./cn.js";
|
|
5
|
+
import { Portal as L, useOverlayStack as J, useEscapeKey as Q } from "./overlay.js";
|
|
6
|
+
import { useAnchoredPosition as W } from "./anchor.js";
|
|
7
|
+
import { moveIndex as F } from "./list-nav.js";
|
|
8
|
+
import { usePruiI18n as V } from "../i18n.js";
|
|
9
|
+
const P = (s) => String(s).padStart(2, "0");
|
|
10
|
+
function _(s) {
|
|
11
|
+
if (!s || !/^\d{2}:\d{2}(:\d{2})?$/.test(s)) return null;
|
|
12
|
+
const [o, c, d] = s.split(":").map(Number);
|
|
13
|
+
return { h: o ?? 0, m: c ?? 0, s: d ?? 0 };
|
|
14
|
+
}
|
|
15
|
+
function X(s, o, c = 0, d) {
|
|
16
|
+
return d ? `${P(s)}:${P(o)}:${P(c)}` : `${P(s)}:${P(o)}`;
|
|
17
|
+
}
|
|
18
|
+
function z(s) {
|
|
19
|
+
const o = _(s);
|
|
20
|
+
return o ? o.h * 3600 + o.m * 60 + o.s : -1;
|
|
21
|
+
}
|
|
22
|
+
function Y(s) {
|
|
23
|
+
const o = [];
|
|
24
|
+
for (let c = 0; c < 60; c += Math.max(1, s)) o.push(c);
|
|
25
|
+
return o;
|
|
26
|
+
}
|
|
27
|
+
function C({ value: s, onSelect: o, minuteStep: c = 1, seconds: d = !1, min: h, max: y, compact: k, className: w }) {
|
|
28
|
+
const { t: b } = V(), t = _(s), x = h ? z(h) : -1 / 0, A = y ? z(y) : 1 / 0, p = [
|
|
29
|
+
{ label: b.hours, items: Array.from({ length: 24 }, (e, a) => a), selected: (t == null ? void 0 : t.h) ?? null, unit: "h" },
|
|
30
|
+
{ label: b.minutes, items: Y(c), selected: (t == null ? void 0 : t.m) ?? null, unit: "m" }
|
|
31
|
+
];
|
|
32
|
+
d && p.push({ label: b.seconds, items: Array.from({ length: 60 }, (e, a) => a), selected: (t == null ? void 0 : t.s) ?? null, unit: "s" });
|
|
33
|
+
const H = (e, a) => {
|
|
34
|
+
const n = e === "h" ? a : (t == null ? void 0 : t.h) ?? 0, m = e === "m" ? a : (t == null ? void 0 : t.m) ?? 0, i = e === "s" ? a : (t == null ? void 0 : t.s) ?? 0, f = n * 3600 + m * 60 + i;
|
|
35
|
+
return f < x || f > A;
|
|
36
|
+
}, E = (e, a) => {
|
|
37
|
+
const n = e === "h" ? a : (t == null ? void 0 : t.h) ?? 0, m = e === "m" ? a : (t == null ? void 0 : t.m) ?? 0, i = e === "s" ? a : (t == null ? void 0 : t.s) ?? 0;
|
|
38
|
+
o(X(n, m, i, d));
|
|
39
|
+
}, S = (e, a) => {
|
|
40
|
+
var T, v, g, l, D, $, M, K;
|
|
41
|
+
const n = Array.from(
|
|
42
|
+
e.currentTarget.querySelectorAll("[role='option']:not([aria-disabled='true'])")
|
|
43
|
+
), m = n.findIndex((r) => r.getAttribute("aria-selected") === "true"), i = n.indexOf(document.activeElement), f = i >= 0 ? i : m;
|
|
44
|
+
switch (e.key) {
|
|
45
|
+
case "ArrowDown":
|
|
46
|
+
e.preventDefault(), (T = n[F(f, 1, n.length)]) == null || T.focus();
|
|
47
|
+
break;
|
|
48
|
+
case "ArrowUp":
|
|
49
|
+
e.preventDefault(), (v = n[F(f, -1, n.length)]) == null || v.focus();
|
|
50
|
+
break;
|
|
51
|
+
case "Home":
|
|
52
|
+
e.preventDefault(), (g = n[0]) == null || g.focus();
|
|
53
|
+
break;
|
|
54
|
+
case "End":
|
|
55
|
+
e.preventDefault(), (l = n[n.length - 1]) == null || l.focus();
|
|
56
|
+
break;
|
|
57
|
+
case "ArrowLeft":
|
|
58
|
+
if (a > 0) {
|
|
59
|
+
e.preventDefault();
|
|
60
|
+
const r = (D = e.currentTarget.previousElementSibling) == null ? void 0 : D.querySelectorAll("[role='option']:not([aria-disabled='true'])");
|
|
61
|
+
($ = r == null ? void 0 : r[Math.min(f < 0 ? 0 : f, r.length - 1)]) == null || $.focus();
|
|
62
|
+
}
|
|
63
|
+
break;
|
|
64
|
+
case "ArrowRight":
|
|
65
|
+
if (a < p.length - 1) {
|
|
66
|
+
e.preventDefault();
|
|
67
|
+
const r = (M = e.currentTarget.nextElementSibling) == null ? void 0 : M.querySelectorAll("[role='option']:not([aria-disabled='true'])");
|
|
68
|
+
(K = r == null ? void 0 : r[Math.min(f < 0 ? 0 : f, r.length - 1)]) == null || K.focus();
|
|
69
|
+
}
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
return /* @__PURE__ */ u("div", { className: I("prui-time-panel flex gap-1", w), role: "group", "aria-label": b.time, children: p.map((e, a) => /* @__PURE__ */ N(
|
|
74
|
+
"div",
|
|
75
|
+
{
|
|
76
|
+
role: "listbox",
|
|
77
|
+
"aria-label": e.label,
|
|
78
|
+
tabIndex: -1,
|
|
79
|
+
onKeyDown: (n) => S(n, a),
|
|
80
|
+
className: I("flex w-16 flex-col rounded-[var(--prui-radius-1)] outline-none", !k && "bg-[var(--prui-raise)]/40"),
|
|
81
|
+
children: [
|
|
82
|
+
k ? null : /* @__PURE__ */ u("div", { className: "px-2 pb-1 pt-2 text-center text-[10px] font-semibold uppercase tracking-wide text-[var(--prui-dim)]", children: e.label }),
|
|
83
|
+
/* @__PURE__ */ u("div", { className: "flex max-h-56 flex-col overflow-y-auto p-1", children: e.items.map((n) => {
|
|
84
|
+
const m = e.selected === n, i = H(e.unit, n);
|
|
85
|
+
return /* @__PURE__ */ u(
|
|
86
|
+
"button",
|
|
87
|
+
{
|
|
88
|
+
type: "button",
|
|
89
|
+
role: "option",
|
|
90
|
+
"aria-selected": m,
|
|
91
|
+
"aria-disabled": i || void 0,
|
|
92
|
+
tabIndex: m ? 0 : -1,
|
|
93
|
+
"data-testid": `time-${e.unit}`,
|
|
94
|
+
onClick: () => !i && E(e.unit, n),
|
|
95
|
+
className: I(
|
|
96
|
+
"rounded-[calc(var(--prui-radius-1))] py-1 text-center text-sm tabular-nums cursor-pointer outline-none",
|
|
97
|
+
"focus-visible:bg-[var(--prui-raise)]",
|
|
98
|
+
m ? "bg-[var(--prui-brand)] text-[var(--prui-brand-fg,#fff)] font-medium" : "text-[var(--prui-fg)] hover:bg-[var(--prui-raise)]",
|
|
99
|
+
i && "opacity-40 pointer-events-none"
|
|
100
|
+
),
|
|
101
|
+
children: P(n)
|
|
102
|
+
},
|
|
103
|
+
n
|
|
104
|
+
);
|
|
105
|
+
}) })
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
e.unit
|
|
109
|
+
)) });
|
|
110
|
+
}
|
|
111
|
+
function B(s, o, c) {
|
|
112
|
+
const { setElement: d, isTop: h } = J(o);
|
|
113
|
+
Q(o, h, c);
|
|
114
|
+
const { ref: y, position: k } = W({ active: o, anchorRef: s, side: "bottom", align: "start", offsetHeight: 280 });
|
|
115
|
+
return R.useEffect(() => {
|
|
116
|
+
if (!o) return;
|
|
117
|
+
const w = (b) => {
|
|
118
|
+
var x;
|
|
119
|
+
const t = b.target;
|
|
120
|
+
(x = s.current) != null && x.contains(t) || (!(t instanceof Element) || !t.closest("[data-prui-time-panel-wrap]")) && c();
|
|
121
|
+
};
|
|
122
|
+
return document.addEventListener("mousedown", w), () => document.removeEventListener("mousedown", w);
|
|
123
|
+
}), { setElement: d, floatingRef: y, position: k };
|
|
124
|
+
}
|
|
125
|
+
const Z = R.forwardRef(function({ value: o, defaultValue: c, onChange: d, minuteStep: h, seconds: y, min: k, max: w, disabled: b, placeholder: t = "HH:mm", ariaLabel: x, className: A }, p) {
|
|
126
|
+
const [H, E] = R.useState(c ?? ""), [S, e] = R.useState(!1), a = R.useRef(null), n = o !== void 0, m = n ? o : H, i = a, { setElement: f, floatingRef: T, position: v } = B(i, S, () => {
|
|
127
|
+
var l;
|
|
128
|
+
e(!1), (l = a.current) == null || l.focus();
|
|
129
|
+
}), g = (l) => {
|
|
130
|
+
n || E(l), d == null || d(l);
|
|
131
|
+
};
|
|
132
|
+
return /* @__PURE__ */ N(U, { children: [
|
|
133
|
+
/* @__PURE__ */ u(
|
|
134
|
+
"input",
|
|
135
|
+
{
|
|
136
|
+
ref: (l) => {
|
|
137
|
+
a.current = l, typeof p == "function" ? p(l) : p && (p.current = l);
|
|
138
|
+
},
|
|
139
|
+
type: "text",
|
|
140
|
+
readOnly: !0,
|
|
141
|
+
role: "combobox",
|
|
142
|
+
"aria-expanded": S,
|
|
143
|
+
"aria-haspopup": "dialog",
|
|
144
|
+
"aria-label": x,
|
|
145
|
+
placeholder: t,
|
|
146
|
+
disabled: b,
|
|
147
|
+
value: m,
|
|
148
|
+
onClick: () => e(!0),
|
|
149
|
+
onFocus: () => {
|
|
150
|
+
!b && !S && e(!0);
|
|
151
|
+
},
|
|
152
|
+
className: I(
|
|
153
|
+
"prui-time-picker h-9 w-full cursor-pointer rounded-[var(--prui-radius)] border border-[var(--prui-line)]",
|
|
154
|
+
"bg-[var(--prui-background)] px-3 pr-8 text-sm tabular-nums text-[var(--prui-fg)] placeholder:text-[var(--prui-dim)]",
|
|
155
|
+
"outline-none transition-colors focus:border-[var(--prui-brand)] focus:ring-2 focus:ring-[var(--prui-brand)]/30",
|
|
156
|
+
"disabled:opacity-50 disabled:cursor-not-allowed",
|
|
157
|
+
A
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
),
|
|
161
|
+
S ? /* @__PURE__ */ u(L, { children: /* @__PURE__ */ u(
|
|
162
|
+
"div",
|
|
163
|
+
{
|
|
164
|
+
ref: (l) => {
|
|
165
|
+
T(l), f(l);
|
|
166
|
+
},
|
|
167
|
+
role: "dialog",
|
|
168
|
+
"aria-label": x ?? "Choose time",
|
|
169
|
+
"data-prui-time-panel-wrap": !0,
|
|
170
|
+
className: "fixed z-[var(--prui-z-overlay)] rounded-[var(--prui-radius)] border border-[var(--prui-line)] bg-[var(--prui-surface)] p-2 shadow-[var(--prui-shadow-lg)]",
|
|
171
|
+
style: { top: (v == null ? void 0 : v.top) ?? -9999, left: (v == null ? void 0 : v.left) ?? -9999 },
|
|
172
|
+
children: /* @__PURE__ */ u(C, { value: m, onSelect: g, minuteStep: h, seconds: y, min: k, max: w })
|
|
173
|
+
}
|
|
174
|
+
) }) : null
|
|
175
|
+
] });
|
|
176
|
+
});
|
|
177
|
+
Z.displayName = "TimePicker";
|
|
178
|
+
const ee = R.forwardRef(function({ value: o, defaultValue: c, onChange: d, minuteStep: h, seconds: y, min: k, max: w, disabled: b, placeholder: t = "HH:mm – HH:mm", ariaLabel: x, className: A }, p) {
|
|
179
|
+
const { t: H } = V(), [E, S] = R.useState(c ?? {}), [e, a] = R.useState(!1), n = R.useRef(null), m = o !== void 0, i = m ? o : E, f = n, { setElement: T, floatingRef: v, position: g } = B(f, e, () => {
|
|
180
|
+
var r;
|
|
181
|
+
a(!1), (r = n.current) == null || r.focus();
|
|
182
|
+
}), l = (r) => {
|
|
183
|
+
m || S(r), d == null || d(r);
|
|
184
|
+
}, D = (r, q) => {
|
|
185
|
+
const O = r === "from" ? q : i.from, j = r === "to" ? q : i.to;
|
|
186
|
+
if (O && j && z(j) < z(O)) {
|
|
187
|
+
l(r === "to" ? { from: q, to: void 0 } : { from: j, to: O });
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
l({ from: O, to: j });
|
|
191
|
+
}, $ = i.from ? i.to ? `${i.from} – ${i.to}` : `${i.from} – …` : "", M = i.to ?? w, K = i.from ?? k;
|
|
192
|
+
return /* @__PURE__ */ N(U, { children: [
|
|
193
|
+
/* @__PURE__ */ u(
|
|
194
|
+
"input",
|
|
195
|
+
{
|
|
196
|
+
ref: (r) => {
|
|
197
|
+
n.current = r, typeof p == "function" ? p(r) : p && (p.current = r);
|
|
198
|
+
},
|
|
199
|
+
type: "text",
|
|
200
|
+
readOnly: !0,
|
|
201
|
+
role: "combobox",
|
|
202
|
+
"aria-expanded": e,
|
|
203
|
+
"aria-haspopup": "dialog",
|
|
204
|
+
"aria-label": x,
|
|
205
|
+
placeholder: t,
|
|
206
|
+
disabled: b,
|
|
207
|
+
value: $,
|
|
208
|
+
onClick: () => a(!0),
|
|
209
|
+
onFocus: () => {
|
|
210
|
+
!b && !e && a(!0);
|
|
211
|
+
},
|
|
212
|
+
className: I(
|
|
213
|
+
"prui-time-range-picker h-9 w-full cursor-pointer rounded-[var(--prui-radius)] border border-[var(--prui-line)]",
|
|
214
|
+
"bg-[var(--prui-background)] px-3 pr-8 text-sm tabular-nums text-[var(--prui-fg)] placeholder:text-[var(--prui-dim)]",
|
|
215
|
+
"outline-none transition-colors focus:border-[var(--prui-brand)] focus:ring-2 focus:ring-[var(--prui-brand)]/30",
|
|
216
|
+
"disabled:opacity-50 disabled:cursor-not-allowed",
|
|
217
|
+
A
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
),
|
|
221
|
+
/* @__PURE__ */ u(G, { className: "pointer-events-none -ml-7 h-4 w-4 text-[var(--prui-dim)]", "aria-hidden": !0 }),
|
|
222
|
+
e ? /* @__PURE__ */ u(L, { children: /* @__PURE__ */ u(
|
|
223
|
+
"div",
|
|
224
|
+
{
|
|
225
|
+
ref: (r) => {
|
|
226
|
+
v(r), T(r);
|
|
227
|
+
},
|
|
228
|
+
role: "dialog",
|
|
229
|
+
"aria-label": x ?? "Choose time range",
|
|
230
|
+
"data-prui-time-panel-wrap": !0,
|
|
231
|
+
className: "fixed z-[var(--prui-z-overlay)] rounded-[var(--prui-radius)] border border-[var(--prui-line)] bg-[var(--prui-surface)] p-3 shadow-[var(--prui-shadow-lg)]",
|
|
232
|
+
style: { top: (g == null ? void 0 : g.top) ?? -9999, left: (g == null ? void 0 : g.left) ?? -9999 },
|
|
233
|
+
children: /* @__PURE__ */ N("div", { className: "flex flex-col gap-3", children: [
|
|
234
|
+
/* @__PURE__ */ N("div", { className: "flex gap-4", children: [
|
|
235
|
+
/* @__PURE__ */ N("div", { children: [
|
|
236
|
+
/* @__PURE__ */ u("div", { className: "mb-1 text-xs font-semibold text-[var(--prui-dim)]", children: H.from }),
|
|
237
|
+
/* @__PURE__ */ u(C, { compact: !0, value: i.from, onSelect: (r) => D("from", r), minuteStep: h, seconds: y, min: k, max: M })
|
|
238
|
+
] }),
|
|
239
|
+
/* @__PURE__ */ N("div", { children: [
|
|
240
|
+
/* @__PURE__ */ u("div", { className: "mb-1 text-xs font-semibold text-[var(--prui-dim)]", children: H.to }),
|
|
241
|
+
/* @__PURE__ */ u(C, { compact: !0, value: i.to, onSelect: (r) => D("to", r), minuteStep: h, seconds: y, min: K, max: w })
|
|
242
|
+
] })
|
|
243
|
+
] }),
|
|
244
|
+
/* @__PURE__ */ N("div", { className: "flex items-center justify-between border-t border-[var(--prui-line)] pt-2 text-xs text-[var(--prui-dim)]", children: [
|
|
245
|
+
/* @__PURE__ */ u("span", { "data-testid": "timerange-label", children: $ || H.time }),
|
|
246
|
+
/* @__PURE__ */ N("div", { className: "flex gap-2", children: [
|
|
247
|
+
/* @__PURE__ */ u(
|
|
248
|
+
"button",
|
|
249
|
+
{
|
|
250
|
+
type: "button",
|
|
251
|
+
onClick: () => l({}),
|
|
252
|
+
className: "cursor-pointer rounded-[var(--prui-radius-1)] px-2 py-1 transition-colors hover:bg-[var(--prui-raise)]",
|
|
253
|
+
children: H.clear
|
|
254
|
+
}
|
|
255
|
+
),
|
|
256
|
+
/* @__PURE__ */ u(
|
|
257
|
+
"button",
|
|
258
|
+
{
|
|
259
|
+
type: "button",
|
|
260
|
+
onClick: () => {
|
|
261
|
+
var r;
|
|
262
|
+
a(!1), (r = n.current) == null || r.focus();
|
|
263
|
+
},
|
|
264
|
+
className: "cursor-pointer rounded-[var(--prui-radius-1)] bg-[var(--prui-brand)]/15 px-2 py-1 text-[var(--prui-brand)] transition-colors hover:bg-[var(--prui-brand)]/25",
|
|
265
|
+
children: H.apply
|
|
266
|
+
}
|
|
267
|
+
)
|
|
268
|
+
] })
|
|
269
|
+
] })
|
|
270
|
+
] })
|
|
271
|
+
}
|
|
272
|
+
) }) : null
|
|
273
|
+
] });
|
|
274
|
+
});
|
|
275
|
+
ee.displayName = "TimeRangePicker";
|
|
276
|
+
const le = {
|
|
277
|
+
name: "TimePicker",
|
|
278
|
+
props: [
|
|
279
|
+
{ name: "value", type: "string 'HH:mm' | 'HH:mm:ss'", default: "undefined", control: "text" },
|
|
280
|
+
{ name: "defaultValue", type: "string", default: "undefined", control: "text" },
|
|
281
|
+
{ name: "onChange", type: "(time: string) => void", default: null, control: "none" },
|
|
282
|
+
{ name: "minuteStep", type: "number (minutes)", default: "1", control: "number" },
|
|
283
|
+
{ name: "seconds", type: "boolean", default: "false", control: "boolean", description: "Adds a seconds column ('HH:mm:ss')." },
|
|
284
|
+
{ name: "min", type: "string 'HH:mm'", default: "undefined", control: "text" },
|
|
285
|
+
{ name: "max", type: "string 'HH:mm'", default: "undefined", control: "text" }
|
|
286
|
+
]
|
|
287
|
+
}, ue = {
|
|
288
|
+
name: "TimeRangePicker",
|
|
289
|
+
props: [
|
|
290
|
+
{ name: "value", type: "{ from?: string, to?: string }", default: "{}", control: "object" },
|
|
291
|
+
{ name: "defaultValue", type: "{ from?: string, to?: string }", default: "{}", control: "object" },
|
|
292
|
+
{ name: "onChange", type: "(range: TimeRange) => void", default: null, control: "none" },
|
|
293
|
+
{ name: "minuteStep", type: "number (minutes)", default: "1", control: "number" },
|
|
294
|
+
{ name: "seconds", type: "boolean", default: "false", control: "boolean" },
|
|
295
|
+
{ name: "min", type: "string 'HH:mm'", default: "undefined", control: "text" },
|
|
296
|
+
{ name: "max", type: "string 'HH:mm'", default: "undefined", control: "text" }
|
|
297
|
+
]
|
|
298
|
+
};
|
|
299
|
+
export {
|
|
300
|
+
C as TimePanel,
|
|
301
|
+
Z as TimePicker,
|
|
302
|
+
ee as TimeRangePicker,
|
|
303
|
+
le as timePickerPropsMeta,
|
|
304
|
+
ue as timeRangePickerPropsMeta
|
|
305
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { PropsMeta } from './props-meta';
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Timeline: a vertical chronological list with markers, timestamps, and
|
|
5
|
+
* optional icons/colors per item. Presentational (role="list" semantics);
|
|
6
|
+
* content is fully caller-owned.
|
|
7
|
+
*/
|
|
8
|
+
export type TimelineVariant = "brand" | "success" | "warning" | "danger" | "neutral";
|
|
9
|
+
export interface TimelineItemData {
|
|
10
|
+
title: React.ReactNode;
|
|
11
|
+
time?: React.ReactNode;
|
|
12
|
+
description?: React.ReactNode;
|
|
13
|
+
icon?: React.ReactNode;
|
|
14
|
+
variant?: TimelineVariant;
|
|
15
|
+
}
|
|
16
|
+
export interface TimelineProps extends React.HTMLAttributes<HTMLOListElement> {
|
|
17
|
+
items: TimelineItemData[];
|
|
18
|
+
/** Marker alignment relative to the content column. Default left. */
|
|
19
|
+
align?: "left" | "alternate" | "right";
|
|
20
|
+
}
|
|
21
|
+
export declare const Timeline: React.ForwardRefExoticComponent<TimelineProps & React.RefAttributes<HTMLOListElement>>;
|
|
22
|
+
export declare const timelinePropsMeta: PropsMeta;
|
|
@@ -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;
|