namps-ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,893 @@
1
+ "use client";
2
+ var __defProp = Object.defineProperty;
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, { get: all[name], enumerable: true });
6
+ };
7
+
8
+ // src/theme.tsx
9
+ import * as React from "react";
10
+
11
+ // src/icons.tsx
12
+ var icons_exports = {};
13
+ __export(icons_exports, {
14
+ AlertTriangle: () => AlertTriangle,
15
+ ArrowRight: () => ArrowRight,
16
+ Check: () => Check,
17
+ CheckCircle: () => CheckCircle,
18
+ ChevronDown: () => ChevronDown,
19
+ ChevronLeft: () => ChevronLeft,
20
+ ChevronRight: () => ChevronRight,
21
+ DotsVertical: () => DotsVertical,
22
+ Info: () => Info,
23
+ Moon: () => Moon,
24
+ Plus: () => Plus,
25
+ Search: () => Search,
26
+ Sort: () => Sort,
27
+ Sun: () => Sun,
28
+ X: () => X,
29
+ XCircle: () => XCircle
30
+ });
31
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
32
+ var base = (path) => function Icon({ className, ...props }) {
33
+ return /* @__PURE__ */ jsx(
34
+ "svg",
35
+ {
36
+ viewBox: "0 0 24 24",
37
+ fill: "none",
38
+ stroke: "currentColor",
39
+ strokeWidth: 1.8,
40
+ strokeLinecap: "round",
41
+ strokeLinejoin: "round",
42
+ className: className ?? "pui-icon",
43
+ "aria-hidden": "true",
44
+ ...props,
45
+ children: path
46
+ }
47
+ );
48
+ };
49
+ var ChevronDown = base(/* @__PURE__ */ jsx("path", { d: "M6 9l6 6 6-6" }));
50
+ var ChevronRight = base(/* @__PURE__ */ jsx("path", { d: "M9 6l6 6-6 6" }));
51
+ var ChevronLeft = base(/* @__PURE__ */ jsx("path", { d: "M15 6l-6 6 6 6" }));
52
+ var Check = base(/* @__PURE__ */ jsx("path", { d: "M5 12l4 4 10-10" }));
53
+ var X = base(/* @__PURE__ */ jsx("path", { d: "M6 6l12 12M18 6L6 18" }));
54
+ var Search = base(/* @__PURE__ */ jsxs(Fragment, { children: [
55
+ /* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "7" }),
56
+ /* @__PURE__ */ jsx("path", { d: "M21 21l-4.3-4.3" })
57
+ ] }));
58
+ var Plus = base(/* @__PURE__ */ jsx("path", { d: "M12 5v14M5 12h14" }));
59
+ var Sun = base(/* @__PURE__ */ jsxs(Fragment, { children: [
60
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "4" }),
61
+ /* @__PURE__ */ jsx("path", { d: "M12 2v2M12 20v2M4 12H2M22 12h-2M5.6 5.6l1.4 1.4M17 17l1.4 1.4M18.4 5.6L17 7M7 17l-1.4 1.4" })
62
+ ] }));
63
+ var Moon = base(/* @__PURE__ */ jsx("path", { d: "M21 12.8A9 9 0 1 1 11.2 3 7 7 0 0 0 21 12.8Z" }));
64
+ var Info = base(/* @__PURE__ */ jsxs(Fragment, { children: [
65
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9" }),
66
+ /* @__PURE__ */ jsx("path", { d: "M12 11v5M12 8h.01" })
67
+ ] }));
68
+ var AlertTriangle = base(/* @__PURE__ */ jsxs(Fragment, { children: [
69
+ /* @__PURE__ */ jsx("path", { d: "M10.3 3.3 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.3a2 2 0 0 0-3.4 0Z" }),
70
+ /* @__PURE__ */ jsx("path", { d: "M12 9v4M12 17h.01" })
71
+ ] }));
72
+ var CheckCircle = base(/* @__PURE__ */ jsxs(Fragment, { children: [
73
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9" }),
74
+ /* @__PURE__ */ jsx("path", { d: "M8 12l3 3 5-6" })
75
+ ] }));
76
+ var XCircle = base(/* @__PURE__ */ jsxs(Fragment, { children: [
77
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9" }),
78
+ /* @__PURE__ */ jsx("path", { d: "M15 9l-6 6M9 9l6 6" })
79
+ ] }));
80
+ var ArrowRight = base(/* @__PURE__ */ jsx("path", { d: "M5 12h14M13 6l6 6-6 6" }));
81
+ var Sort = base(/* @__PURE__ */ jsx("path", { d: "M8 9l4-4 4 4M8 15l4 4 4-4" }));
82
+ function DotsVertical({ className, ...props }) {
83
+ return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "currentColor", className: className ?? "pui-icon", "aria-hidden": "true", ...props, children: [
84
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "5", r: "1.6" }),
85
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "1.6" }),
86
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "19", r: "1.6" })
87
+ ] });
88
+ }
89
+
90
+ // src/utils.ts
91
+ function cx(...parts) {
92
+ return parts.filter(Boolean).join(" ");
93
+ }
94
+ function initials(name) {
95
+ const words = name.trim().split(/\s+/);
96
+ if (words.length === 0) return "?";
97
+ if (words.length === 1) return words[0].slice(0, 2).toUpperCase();
98
+ return (words[0][0] + words[words.length - 1][0]).toUpperCase();
99
+ }
100
+
101
+ // src/theme.tsx
102
+ import { jsx as jsx2 } from "react/jsx-runtime";
103
+ var ThemeContext = React.createContext(null);
104
+ function ThemeProvider({
105
+ children,
106
+ defaultTheme = "light",
107
+ storageKey = "pui-theme",
108
+ attributeTarget = "html"
109
+ }) {
110
+ const [theme, setThemeState] = React.useState(defaultTheme);
111
+ React.useEffect(() => {
112
+ if (storageKey) {
113
+ const stored = window.localStorage.getItem(storageKey);
114
+ if (stored === "light" || stored === "dark") setThemeState(stored);
115
+ }
116
+ }, [storageKey]);
117
+ React.useEffect(() => {
118
+ if (attributeTarget === "html") {
119
+ document.documentElement.setAttribute("data-theme", theme);
120
+ }
121
+ if (storageKey) window.localStorage.setItem(storageKey, theme);
122
+ }, [theme, attributeTarget, storageKey]);
123
+ const value = React.useMemo(
124
+ () => ({
125
+ theme,
126
+ setTheme: setThemeState,
127
+ toggle: () => setThemeState((t) => t === "light" ? "dark" : "light")
128
+ }),
129
+ [theme]
130
+ );
131
+ return /* @__PURE__ */ jsx2(ThemeContext.Provider, { value, children: attributeTarget === "self" ? /* @__PURE__ */ jsx2("div", { "data-theme": theme, children }) : children });
132
+ }
133
+ function useTheme() {
134
+ const ctx = React.useContext(ThemeContext);
135
+ if (!ctx) throw new Error("useTheme must be used within a <ThemeProvider>");
136
+ return ctx;
137
+ }
138
+ function ThemeToggle({ className, ...props }) {
139
+ const { theme, toggle } = useTheme();
140
+ return /* @__PURE__ */ jsx2(
141
+ "button",
142
+ {
143
+ type: "button",
144
+ "aria-label": "Toggle color theme",
145
+ onClick: toggle,
146
+ className: cx("pui-btn", "pui-btn--secondary", "pui-btn--icon", className),
147
+ ...props,
148
+ children: theme === "light" ? /* @__PURE__ */ jsx2(Moon, { className: "pui-icon" }) : /* @__PURE__ */ jsx2(Sun, { className: "pui-icon" })
149
+ }
150
+ );
151
+ }
152
+
153
+ // src/components/Button.tsx
154
+ import * as React2 from "react";
155
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
156
+ var Button = React2.forwardRef(function Button2({
157
+ variant = "primary",
158
+ size = "md",
159
+ loading = false,
160
+ iconOnly = false,
161
+ block = false,
162
+ leftIcon,
163
+ rightIcon,
164
+ disabled,
165
+ className,
166
+ children,
167
+ ...props
168
+ }, ref) {
169
+ return /* @__PURE__ */ jsxs2(
170
+ "button",
171
+ {
172
+ ref,
173
+ disabled: disabled || loading,
174
+ className: cx(
175
+ "pui-btn",
176
+ `pui-btn--${variant}`,
177
+ size !== "md" && `pui-btn--${size}`,
178
+ iconOnly && "pui-btn--icon",
179
+ block && "pui-btn--block",
180
+ className
181
+ ),
182
+ ...props,
183
+ children: [
184
+ loading && /* @__PURE__ */ jsx3("span", { className: "pui-btn__spinner", "aria-hidden": "true" }),
185
+ !loading && leftIcon,
186
+ children,
187
+ !loading && rightIcon
188
+ ]
189
+ }
190
+ );
191
+ });
192
+
193
+ // src/components/Form.tsx
194
+ import * as React3 from "react";
195
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
196
+ function Field({ label, hint, error, htmlFor, children, className, style }) {
197
+ return /* @__PURE__ */ jsxs3("div", { className: cx("pui-field", className), style, children: [
198
+ label && /* @__PURE__ */ jsx4("label", { className: "pui-label", htmlFor, children: label }),
199
+ children,
200
+ error ? /* @__PURE__ */ jsxs3("span", { className: "pui-error", children: [
201
+ /* @__PURE__ */ jsx4(XCircle, { style: { width: 13, height: 13 } }),
202
+ error
203
+ ] }) : hint && /* @__PURE__ */ jsx4("span", { className: "pui-hint", children: hint })
204
+ ] });
205
+ }
206
+ var Input = React3.forwardRef(function Input2({ invalid, className, ...props }, ref) {
207
+ return /* @__PURE__ */ jsx4(
208
+ "input",
209
+ {
210
+ ref,
211
+ "aria-invalid": invalid || void 0,
212
+ className: cx("pui-input", invalid && "pui-input--invalid", className),
213
+ ...props
214
+ }
215
+ );
216
+ });
217
+ var SearchInput = React3.forwardRef(function SearchInput2({ className, ...props }, ref) {
218
+ return /* @__PURE__ */ jsxs3("span", { className: "pui-input-wrap", children: [
219
+ /* @__PURE__ */ jsx4(Search, { className: "pui-input-wrap__icon" }),
220
+ /* @__PURE__ */ jsx4(Input, { ref, className, ...props })
221
+ ] });
222
+ });
223
+ var Textarea = React3.forwardRef(function Textarea2({ invalid, className, rows = 3, ...props }, ref) {
224
+ return /* @__PURE__ */ jsx4(
225
+ "textarea",
226
+ {
227
+ ref,
228
+ rows,
229
+ "aria-invalid": invalid || void 0,
230
+ className: cx("pui-textarea", invalid && "pui-textarea--invalid", className),
231
+ ...props
232
+ }
233
+ );
234
+ });
235
+ var Select = React3.forwardRef(function Select2({ options, className, children, ...props }, ref) {
236
+ return /* @__PURE__ */ jsxs3("span", { className: "pui-select-wrap", children: [
237
+ /* @__PURE__ */ jsx4("select", { ref, className: cx("pui-select", className), ...props, children: options ? options.map((o) => /* @__PURE__ */ jsx4("option", { value: o.value, children: o.label }, o.value)) : children }),
238
+ /* @__PURE__ */ jsx4(ChevronDown, { className: "pui-select-wrap__caret" })
239
+ ] });
240
+ });
241
+ var Checkbox = React3.forwardRef(function Checkbox2({ label, disabled, className, ...props }, ref) {
242
+ return /* @__PURE__ */ jsxs3("label", { className: cx("pui-checkbox", disabled && "pui-checkbox--disabled", className), children: [
243
+ /* @__PURE__ */ jsx4("input", { ref, type: "checkbox", disabled, ...props }),
244
+ /* @__PURE__ */ jsx4("span", { className: "pui-checkbox__box", children: /* @__PURE__ */ jsx4(Check, {}) }),
245
+ label
246
+ ] });
247
+ });
248
+ var Switch = React3.forwardRef(function Switch2({ className, ...props }, ref) {
249
+ return /* @__PURE__ */ jsxs3("label", { className: cx("pui-switch", className), children: [
250
+ /* @__PURE__ */ jsx4("input", { ref, type: "checkbox", role: "switch", ...props }),
251
+ /* @__PURE__ */ jsx4("span", { className: "pui-switch__track" })
252
+ ] });
253
+ });
254
+ function RadioGroup({ name, value, onValueChange, options, className }) {
255
+ return /* @__PURE__ */ jsx4("div", { className: cx("pui-radio-group", className), role: "radiogroup", children: options.map((o) => /* @__PURE__ */ jsxs3("label", { className: "pui-radio-card", children: [
256
+ /* @__PURE__ */ jsx4(
257
+ "input",
258
+ {
259
+ type: "radio",
260
+ name,
261
+ value: o.value,
262
+ checked: value === o.value,
263
+ onChange: () => onValueChange(o.value)
264
+ }
265
+ ),
266
+ /* @__PURE__ */ jsx4("span", { className: "pui-radio-card__dot" }),
267
+ /* @__PURE__ */ jsxs3("span", { children: [
268
+ /* @__PURE__ */ jsx4("span", { className: "pui-radio-card__title", children: o.title }),
269
+ o.description && /* @__PURE__ */ jsx4("span", { className: "pui-radio-card__desc", children: o.description })
270
+ ] })
271
+ ] }, o.value)) });
272
+ }
273
+ function Slider({ value, min = 0, max = 100, onValueChange, className, ...props }) {
274
+ const pct = (value - min) / (max - min) * 100;
275
+ return /* @__PURE__ */ jsx4("div", { className: cx("pui-slider", className), children: /* @__PURE__ */ jsxs3("div", { className: "pui-slider__row", children: [
276
+ /* @__PURE__ */ jsx4("div", { className: "pui-slider__track" }),
277
+ /* @__PURE__ */ jsx4("div", { className: "pui-slider__fill", style: { width: `${pct}%` } }),
278
+ /* @__PURE__ */ jsx4(
279
+ "input",
280
+ {
281
+ type: "range",
282
+ min,
283
+ max,
284
+ value,
285
+ onChange: (e) => onValueChange(Number(e.target.value)),
286
+ ...props
287
+ }
288
+ )
289
+ ] }) });
290
+ }
291
+
292
+ // src/components/Display.tsx
293
+ import * as React4 from "react";
294
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
295
+ function Badge({ tone = "neutral", solid, dot, className, children, ...props }) {
296
+ return /* @__PURE__ */ jsxs4(
297
+ "span",
298
+ {
299
+ className: cx("pui-badge", className),
300
+ "data-tone": tone === "neutral" ? void 0 : tone,
301
+ "data-solid": solid || void 0,
302
+ ...props,
303
+ children: [
304
+ dot && /* @__PURE__ */ jsx5("span", { className: "pui-badge__dot" }),
305
+ children
306
+ ]
307
+ }
308
+ );
309
+ }
310
+ function Tag({ onRemove, className, children, ...props }) {
311
+ return /* @__PURE__ */ jsxs4("span", { className: cx("pui-tag", className), ...props, children: [
312
+ children,
313
+ onRemove && /* @__PURE__ */ jsx5("button", { type: "button", className: "pui-tag__close", "aria-label": "Remove", onClick: onRemove, children: /* @__PURE__ */ jsx5(X, {}) })
314
+ ] });
315
+ }
316
+ function Avatar({ name, src, size = "md", tone = "accent", status, className, ...props }) {
317
+ return /* @__PURE__ */ jsxs4(
318
+ "span",
319
+ {
320
+ className: cx("pui-avatar", className),
321
+ "data-size": size,
322
+ "data-tone": tone === "accent" ? void 0 : tone,
323
+ title: name,
324
+ ...props,
325
+ children: [
326
+ src ? /* @__PURE__ */ jsx5("img", { src, alt: name }) : initials(name),
327
+ status && /* @__PURE__ */ jsx5("span", { className: "pui-avatar__status", "data-status": status })
328
+ ]
329
+ }
330
+ );
331
+ }
332
+ function AvatarGroup({ children, max, size = "md", className }) {
333
+ const items = React4.Children.toArray(children);
334
+ const shown = max ? items.slice(0, max) : items;
335
+ const extra = max ? items.length - shown.length : 0;
336
+ return /* @__PURE__ */ jsxs4("div", { className: cx("pui-avatar-group", className), children: [
337
+ shown,
338
+ extra > 0 && /* @__PURE__ */ jsxs4("span", { className: "pui-avatar pui-avatar-group__more", "data-size": size, children: [
339
+ "+",
340
+ extra
341
+ ] })
342
+ ] });
343
+ }
344
+ function Card({ padded = true, className, children, ...props }) {
345
+ return /* @__PURE__ */ jsx5("div", { className: cx("pui-card", padded && "pui-card--pad", className), ...props, children });
346
+ }
347
+ function StatCard({ label, value, delta, className }) {
348
+ return /* @__PURE__ */ jsxs4("div", { className: cx("pui-stat", className), children: [
349
+ /* @__PURE__ */ jsx5("div", { className: "pui-stat__label", children: label }),
350
+ /* @__PURE__ */ jsx5("div", { className: "pui-stat__value", children: value }),
351
+ delta && /* @__PURE__ */ jsxs4("div", { className: "pui-stat__delta", "data-dir": delta.direction, children: [
352
+ delta.direction === "up" ? "\u25B2" : "\u25BC",
353
+ " ",
354
+ delta.value
355
+ ] })
356
+ ] });
357
+ }
358
+ function Progress({ value, className, ...props }) {
359
+ const indeterminate = value == null;
360
+ return /* @__PURE__ */ jsx5(
361
+ "div",
362
+ {
363
+ className: cx("pui-progress", className),
364
+ "data-indeterminate": indeterminate || void 0,
365
+ role: "progressbar",
366
+ "aria-valuenow": indeterminate ? void 0 : value,
367
+ "aria-valuemin": 0,
368
+ "aria-valuemax": 100,
369
+ ...props,
370
+ children: /* @__PURE__ */ jsx5("div", { className: "pui-progress__bar", style: indeterminate ? void 0 : { width: `${value}%` } })
371
+ }
372
+ );
373
+ }
374
+ function Spinner({ size = 30, className, style, ...props }) {
375
+ return /* @__PURE__ */ jsx5("span", { role: "status", "aria-label": "Loading", className: cx("pui-spinner", className), style: { width: size, height: size, ...style }, ...props });
376
+ }
377
+ function Skeleton({ width, height = 11, className, style, ...props }) {
378
+ return /* @__PURE__ */ jsx5("div", { className: cx("pui-skeleton", className), style: { width, height, ...style }, ...props });
379
+ }
380
+ function EmptyState({ icon, title, description, action, className }) {
381
+ return /* @__PURE__ */ jsxs4("div", { className: cx("pui-empty", className), children: [
382
+ icon && /* @__PURE__ */ jsx5("span", { className: "pui-empty__icon", children: icon }),
383
+ /* @__PURE__ */ jsx5("div", { className: "pui-empty__title", children: title }),
384
+ description && /* @__PURE__ */ jsx5("div", { className: "pui-empty__desc", children: description }),
385
+ action && /* @__PURE__ */ jsx5("div", { style: { marginTop: 10 }, children: action })
386
+ ] });
387
+ }
388
+
389
+ // src/components/Accordion.tsx
390
+ import * as React5 from "react";
391
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
392
+ var AccordionContext = React5.createContext(null);
393
+ function Accordion({ children, type = "single", defaultValue, className }) {
394
+ const [open, setOpen] = React5.useState(
395
+ defaultValue == null ? [] : Array.isArray(defaultValue) ? defaultValue : [defaultValue]
396
+ );
397
+ const value = React5.useMemo(
398
+ () => ({
399
+ isOpen: (v) => open.includes(v),
400
+ toggle: (v) => setOpen((prev) => {
401
+ const has = prev.includes(v);
402
+ if (type === "single") return has ? [] : [v];
403
+ return has ? prev.filter((x) => x !== v) : [...prev, v];
404
+ })
405
+ }),
406
+ [open, type]
407
+ );
408
+ return /* @__PURE__ */ jsx6("div", { className: cx("pui-accordion", className), children: /* @__PURE__ */ jsx6(AccordionContext.Provider, { value, children }) });
409
+ }
410
+ function AccordionItem({ value, title, children }) {
411
+ const ctx = React5.useContext(AccordionContext);
412
+ if (!ctx) throw new Error("AccordionItem must be used within <Accordion>");
413
+ const open = ctx.isOpen(value);
414
+ return /* @__PURE__ */ jsxs5("div", { className: "pui-accordion__item", "data-open": open || void 0, children: [
415
+ /* @__PURE__ */ jsxs5(
416
+ "button",
417
+ {
418
+ type: "button",
419
+ className: "pui-accordion__trigger",
420
+ "aria-expanded": open,
421
+ onClick: () => ctx.toggle(value),
422
+ children: [
423
+ title,
424
+ /* @__PURE__ */ jsx6(ChevronDown, { className: "pui-accordion__chevron" })
425
+ ]
426
+ }
427
+ ),
428
+ open && /* @__PURE__ */ jsx6("div", { className: "pui-accordion__panel", children })
429
+ ] });
430
+ }
431
+
432
+ // src/components/Tabs.tsx
433
+ import * as React6 from "react";
434
+ import { jsx as jsx7 } from "react/jsx-runtime";
435
+ var TabsContext = React6.createContext(null);
436
+ function Tabs({ children, defaultValue, value, onValueChange, className }) {
437
+ const [internal, setInternal] = React6.useState(defaultValue);
438
+ const current = value ?? internal;
439
+ const setValue = (v) => {
440
+ if (value === void 0) setInternal(v);
441
+ onValueChange?.(v);
442
+ };
443
+ return /* @__PURE__ */ jsx7(TabsContext.Provider, { value: { value: current, setValue }, children: /* @__PURE__ */ jsx7("div", { className, children }) });
444
+ }
445
+ function useTabs(component) {
446
+ const ctx = React6.useContext(TabsContext);
447
+ if (!ctx) throw new Error(`${component} must be used within <Tabs>`);
448
+ return ctx;
449
+ }
450
+ function TabList({ children, className }) {
451
+ return /* @__PURE__ */ jsx7("div", { role: "tablist", className: cx("pui-tabs__list", className), children });
452
+ }
453
+ function Tab({ value, children }) {
454
+ const ctx = useTabs("Tab");
455
+ const active = ctx.value === value;
456
+ return /* @__PURE__ */ jsx7(
457
+ "button",
458
+ {
459
+ type: "button",
460
+ role: "tab",
461
+ "aria-selected": active,
462
+ "data-active": active || void 0,
463
+ className: "pui-tabs__tab",
464
+ onClick: () => ctx.setValue(value),
465
+ children
466
+ }
467
+ );
468
+ }
469
+ function TabPanel({ value, children, className }) {
470
+ const ctx = useTabs("TabPanel");
471
+ if (ctx.value !== value) return null;
472
+ return /* @__PURE__ */ jsx7("div", { role: "tabpanel", className: cx("pui-tabs__panel", className), children });
473
+ }
474
+
475
+ // src/components/Navigation.tsx
476
+ import * as React7 from "react";
477
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
478
+ function Breadcrumbs({ items, className }) {
479
+ return /* @__PURE__ */ jsx8("nav", { "aria-label": "Breadcrumb", className: cx("pui-breadcrumbs", className), children: items.map((c, i) => {
480
+ const last = i === items.length - 1;
481
+ return /* @__PURE__ */ jsxs6(React7.Fragment, { children: [
482
+ last || !c.href ? /* @__PURE__ */ jsx8("span", { className: last ? "pui-breadcrumbs__current" : void 0, "aria-current": last ? "page" : void 0, children: c.label }) : /* @__PURE__ */ jsx8("a", { href: c.href, children: c.label }),
483
+ !last && /* @__PURE__ */ jsx8(ChevronRight, { className: "pui-breadcrumbs__sep" })
484
+ ] }, i);
485
+ }) });
486
+ }
487
+ function range(start, end) {
488
+ return Array.from({ length: end - start + 1 }, (_, i) => start + i);
489
+ }
490
+ function Pagination({ page, count, onChange, siblings = 1, className }) {
491
+ const pages = React7.useMemo(() => {
492
+ const total = count;
493
+ const left = Math.max(2, page - siblings);
494
+ const right = Math.min(total - 1, page + siblings);
495
+ const out = [1];
496
+ if (left > 2) out.push("\u2026");
497
+ out.push(...range(left, right));
498
+ if (right < total - 1) out.push("\u2026");
499
+ if (total > 1) out.push(total);
500
+ return out.filter((v, i, a) => v !== a[i - 1]);
501
+ }, [page, count, siblings]);
502
+ return /* @__PURE__ */ jsxs6("nav", { className: cx("pui-pagination", className), "aria-label": "Pagination", children: [
503
+ /* @__PURE__ */ jsxs6("button", { className: "pui-pagination__btn", disabled: page <= 1, onClick: () => onChange(page - 1), children: [
504
+ /* @__PURE__ */ jsx8(ChevronLeft, {}),
505
+ "Prev"
506
+ ] }),
507
+ pages.map(
508
+ (p, i) => p === "\u2026" ? /* @__PURE__ */ jsx8("span", { className: "pui-pagination__ellipsis", children: "\u2026" }, `e${i}`) : /* @__PURE__ */ jsx8(
509
+ "button",
510
+ {
511
+ className: "pui-pagination__btn",
512
+ "data-active": p === page || void 0,
513
+ "aria-current": p === page ? "page" : void 0,
514
+ onClick: () => onChange(p),
515
+ children: p
516
+ },
517
+ p
518
+ )
519
+ ),
520
+ /* @__PURE__ */ jsxs6("button", { className: "pui-pagination__btn", disabled: page >= count, onClick: () => onChange(page + 1), children: [
521
+ "Next",
522
+ /* @__PURE__ */ jsx8(ChevronRight, {})
523
+ ] })
524
+ ] });
525
+ }
526
+ function Segmented({ options, value, onValueChange, className }) {
527
+ return /* @__PURE__ */ jsx8("div", { className: cx("pui-segmented", className), role: "tablist", children: options.map((o) => /* @__PURE__ */ jsx8(
528
+ "button",
529
+ {
530
+ type: "button",
531
+ role: "tab",
532
+ "aria-selected": value === o.value,
533
+ "data-active": value === o.value || void 0,
534
+ className: "pui-segmented__btn",
535
+ onClick: () => onValueChange(o.value),
536
+ children: o.label
537
+ },
538
+ o.value
539
+ )) });
540
+ }
541
+
542
+ // src/components/Table.tsx
543
+ import * as React8 from "react";
544
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
545
+ function Table({ columns, data, rowKey, className }) {
546
+ const [sort, setSort] = React8.useState(null);
547
+ const sorted = React8.useMemo(() => {
548
+ if (!sort) return data;
549
+ const col = columns.find((c) => c.key === sort.key);
550
+ if (!col?.sortValue) return data;
551
+ const next = [...data].sort((a, b) => {
552
+ const va = col.sortValue(a);
553
+ const vb = col.sortValue(b);
554
+ if (va < vb) return sort.dir === "asc" ? -1 : 1;
555
+ if (va > vb) return sort.dir === "asc" ? 1 : -1;
556
+ return 0;
557
+ });
558
+ return next;
559
+ }, [data, columns, sort]);
560
+ const onSort = (col) => {
561
+ if (!col.sortable) return;
562
+ setSort(
563
+ (prev) => prev?.key === col.key ? { key: col.key, dir: prev.dir === "asc" ? "desc" : "asc" } : { key: col.key, dir: "asc" }
564
+ );
565
+ };
566
+ return /* @__PURE__ */ jsx9("div", { className: cx("pui-table-wrap", className), children: /* @__PURE__ */ jsxs7("table", { className: "pui-table", children: [
567
+ /* @__PURE__ */ jsx9("thead", { children: /* @__PURE__ */ jsx9("tr", { children: columns.map((col) => /* @__PURE__ */ jsx9(
568
+ "th",
569
+ {
570
+ "data-sortable": col.sortable || void 0,
571
+ onClick: () => onSort(col),
572
+ style: { width: col.width, textAlign: col.align },
573
+ children: col.sortable ? /* @__PURE__ */ jsxs7("span", { className: "pui-table__sort", children: [
574
+ col.header,
575
+ /* @__PURE__ */ jsx9(Sort, { style: { width: 13, height: 13 } })
576
+ ] }) : col.header
577
+ },
578
+ col.key
579
+ )) }) }),
580
+ /* @__PURE__ */ jsx9("tbody", { children: sorted.map((row, i) => /* @__PURE__ */ jsx9("tr", { children: columns.map((col) => /* @__PURE__ */ jsx9("td", { style: { textAlign: col.align }, children: col.cell(row) }, col.key)) }, rowKey(row, i))) })
581
+ ] }) });
582
+ }
583
+
584
+ // src/components/Alert.tsx
585
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
586
+ var ICONS = {
587
+ info: Info,
588
+ success: CheckCircle,
589
+ warning: AlertTriangle,
590
+ danger: XCircle
591
+ };
592
+ function Alert({ tone = "info", title, onDismiss, className, children, ...props }) {
593
+ const Icon = ICONS[tone];
594
+ return /* @__PURE__ */ jsxs8("div", { className: cx("pui-alert", className), "data-tone": tone === "info" ? void 0 : tone, role: "alert", ...props, children: [
595
+ /* @__PURE__ */ jsx10(Icon, { className: "pui-alert__icon" }),
596
+ /* @__PURE__ */ jsxs8("div", { style: { flex: 1 }, children: [
597
+ title && /* @__PURE__ */ jsx10("div", { className: "pui-alert__title", children: title }),
598
+ children && /* @__PURE__ */ jsx10("div", { className: "pui-alert__body", children })
599
+ ] }),
600
+ onDismiss && /* @__PURE__ */ jsx10("button", { type: "button", className: "pui-alert__close", "aria-label": "Dismiss", onClick: onDismiss, children: /* @__PURE__ */ jsx10(X, { style: { width: 16, height: 16 } }) })
601
+ ] });
602
+ }
603
+
604
+ // src/components/Toast.tsx
605
+ import * as React9 from "react";
606
+ import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
607
+ var ToastContext = React9.createContext(null);
608
+ var ICONS2 = { info: Info, success: CheckCircle, danger: XCircle };
609
+ function ToastProvider({ children }) {
610
+ const [items, setItems] = React9.useState([]);
611
+ const timers = React9.useRef({});
612
+ const dismiss = React9.useCallback((id) => {
613
+ setItems((prev) => prev.filter((t) => t.id !== id));
614
+ if (timers.current[id]) {
615
+ clearTimeout(timers.current[id]);
616
+ delete timers.current[id];
617
+ }
618
+ }, []);
619
+ const toast = React9.useCallback(
620
+ (opts) => {
621
+ const id = Date.now() + Math.random();
622
+ setItems((prev) => [...prev, { id, ...opts }]);
623
+ const duration = opts.duration ?? 4500;
624
+ if (duration > 0) timers.current[id] = setTimeout(() => dismiss(id), duration);
625
+ return id;
626
+ },
627
+ [dismiss]
628
+ );
629
+ React9.useEffect(() => () => Object.values(timers.current).forEach(clearTimeout), []);
630
+ return /* @__PURE__ */ jsxs9(ToastContext.Provider, { value: { toast, dismiss }, children: [
631
+ children,
632
+ /* @__PURE__ */ jsx11("div", { className: "pui-toast-region", role: "region", "aria-label": "Notifications", children: items.map((t) => {
633
+ const Icon = ICONS2[t.tone ?? "info"];
634
+ return /* @__PURE__ */ jsxs9("div", { className: "pui-toast", children: [
635
+ /* @__PURE__ */ jsx11("span", { className: "pui-toast__icon", "data-tone": t.tone ?? "info", children: /* @__PURE__ */ jsx11(Icon, {}) }),
636
+ /* @__PURE__ */ jsxs9("div", { style: { flex: 1, minWidth: 0 }, children: [
637
+ /* @__PURE__ */ jsx11("div", { className: "pui-toast__title", children: t.title }),
638
+ t.description && /* @__PURE__ */ jsx11("div", { className: "pui-toast__desc", children: t.description })
639
+ ] }),
640
+ /* @__PURE__ */ jsx11("button", { className: "pui-toast__close", "aria-label": "Dismiss", onClick: () => dismiss(t.id), children: /* @__PURE__ */ jsx11(X, { style: { width: 15, height: 15 } }) })
641
+ ] }, t.id);
642
+ }) })
643
+ ] });
644
+ }
645
+ function useToast() {
646
+ const ctx = React9.useContext(ToastContext);
647
+ if (!ctx) throw new Error("useToast must be used within a <ToastProvider>");
648
+ return ctx;
649
+ }
650
+
651
+ // src/components/Overlay.tsx
652
+ import * as React10 from "react";
653
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
654
+ function Tooltip({ label, children, className }) {
655
+ return /* @__PURE__ */ jsxs10("span", { className: cx("pui-tooltip", className), children: [
656
+ children,
657
+ /* @__PURE__ */ jsx12("span", { role: "tooltip", className: "pui-tooltip__bubble", children: label })
658
+ ] });
659
+ }
660
+ function useDismiss(open, onClose) {
661
+ const ref = React10.useRef(null);
662
+ React10.useEffect(() => {
663
+ if (!open) return;
664
+ const onClick = (e) => {
665
+ if (ref.current && !ref.current.contains(e.target)) onClose();
666
+ };
667
+ const onKey = (e) => e.key === "Escape" && onClose();
668
+ document.addEventListener("mousedown", onClick);
669
+ document.addEventListener("keydown", onKey);
670
+ return () => {
671
+ document.removeEventListener("mousedown", onClick);
672
+ document.removeEventListener("keydown", onKey);
673
+ };
674
+ }, [open, onClose]);
675
+ return ref;
676
+ }
677
+ function DropdownMenu({ trigger, children, items, align = "start", className }) {
678
+ const [open, setOpen] = React10.useState(false);
679
+ const ref = useDismiss(open, () => setOpen(false));
680
+ return /* @__PURE__ */ jsxs10("div", { className: cx("pui-menu", className), ref, children: [
681
+ /* @__PURE__ */ jsx12("span", { onClick: () => setOpen((o) => !o), children: trigger }),
682
+ open && /* @__PURE__ */ jsx12("div", { className: "pui-menu__panel", "data-align": align, role: "menu", children: items ? items.map((item, i) => /* @__PURE__ */ jsxs10(
683
+ "button",
684
+ {
685
+ type: "button",
686
+ role: "menuitem",
687
+ className: "pui-menu__item",
688
+ "data-tone": item.tone === "danger" ? "danger" : void 0,
689
+ onClick: () => {
690
+ item.onSelect?.();
691
+ setOpen(false);
692
+ },
693
+ children: [
694
+ item.icon,
695
+ item.label
696
+ ]
697
+ },
698
+ i
699
+ )) : children })
700
+ ] });
701
+ }
702
+ function MenuSeparator() {
703
+ return /* @__PURE__ */ jsx12("div", { className: "pui-menu__sep" });
704
+ }
705
+ function Modal({ open, onClose, title, children, footer, icon }) {
706
+ React10.useEffect(() => {
707
+ if (!open) return;
708
+ const onKey = (e) => e.key === "Escape" && onClose();
709
+ document.addEventListener("keydown", onKey);
710
+ return () => document.removeEventListener("keydown", onKey);
711
+ }, [open, onClose]);
712
+ if (!open) return null;
713
+ return /* @__PURE__ */ jsx12("div", { className: "pui-overlay", onClick: onClose, children: /* @__PURE__ */ jsxs10("div", { className: "pui-dialog", role: "dialog", "aria-modal": "true", onClick: (e) => e.stopPropagation(), children: [
714
+ /* @__PURE__ */ jsxs10("div", { className: "pui-dialog__head", children: [
715
+ /* @__PURE__ */ jsxs10("div", { style: { display: "flex", gap: 13, alignItems: "flex-start" }, children: [
716
+ icon,
717
+ title && /* @__PURE__ */ jsx12("div", { className: "pui-dialog__title", children: title })
718
+ ] }),
719
+ /* @__PURE__ */ jsx12("button", { type: "button", className: "pui-dialog__close", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx12(X, { style: { width: 20, height: 20 } }) })
720
+ ] }),
721
+ children && /* @__PURE__ */ jsx12("div", { className: "pui-dialog__body", children }),
722
+ footer && /* @__PURE__ */ jsx12("div", { className: "pui-dialog__footer", children: footer })
723
+ ] }) });
724
+ }
725
+
726
+ // src/components/Extras.tsx
727
+ import * as React11 from "react";
728
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
729
+ function Separator({ orientation = "horizontal", className, ...props }) {
730
+ return /* @__PURE__ */ jsx13(
731
+ "div",
732
+ {
733
+ role: "separator",
734
+ "aria-orientation": orientation,
735
+ "data-orientation": orientation,
736
+ className: cx("pui-separator", className),
737
+ ...props
738
+ }
739
+ );
740
+ }
741
+ function Kbd({ children, className }) {
742
+ return /* @__PURE__ */ jsx13("kbd", { className: cx("pui-kbd", className), children });
743
+ }
744
+ function Link({ external, className, children, ...props }) {
745
+ return /* @__PURE__ */ jsx13(
746
+ "a",
747
+ {
748
+ className: cx("pui-link", className),
749
+ ...external ? { target: "_blank", rel: "noreferrer" } : {},
750
+ ...props,
751
+ children
752
+ }
753
+ );
754
+ }
755
+ function useDismiss2(open, onClose) {
756
+ const ref = React11.useRef(null);
757
+ React11.useEffect(() => {
758
+ if (!open) return;
759
+ const onClick = (e) => {
760
+ if (ref.current && !ref.current.contains(e.target)) onClose();
761
+ };
762
+ const onKey = (e) => e.key === "Escape" && onClose();
763
+ document.addEventListener("mousedown", onClick);
764
+ document.addEventListener("keydown", onKey);
765
+ return () => {
766
+ document.removeEventListener("mousedown", onClick);
767
+ document.removeEventListener("keydown", onKey);
768
+ };
769
+ }, [open, onClose]);
770
+ return ref;
771
+ }
772
+ function Popover({ trigger, children, align = "start", className }) {
773
+ const [open, setOpen] = React11.useState(false);
774
+ const ref = useDismiss2(open, () => setOpen(false));
775
+ return /* @__PURE__ */ jsxs11("div", { className: cx("pui-menu", className), ref, children: [
776
+ /* @__PURE__ */ jsx13("span", { onClick: () => setOpen((o) => !o), children: trigger }),
777
+ open && /* @__PURE__ */ jsx13("div", { className: "pui-popover", "data-align": align, role: "dialog", children })
778
+ ] });
779
+ }
780
+ function Drawer({ open, onClose, side = "right", title, children, footer }) {
781
+ React11.useEffect(() => {
782
+ if (!open) return;
783
+ const onKey = (e) => e.key === "Escape" && onClose();
784
+ document.addEventListener("keydown", onKey);
785
+ return () => document.removeEventListener("keydown", onKey);
786
+ }, [open, onClose]);
787
+ if (!open) return null;
788
+ return /* @__PURE__ */ jsx13("div", { className: "pui-drawer-overlay", onClick: onClose, children: /* @__PURE__ */ jsxs11("div", { className: "pui-drawer", "data-side": side, role: "dialog", "aria-modal": "true", onClick: (e) => e.stopPropagation(), children: [
789
+ /* @__PURE__ */ jsxs11("div", { className: "pui-drawer__head", children: [
790
+ title && /* @__PURE__ */ jsx13("div", { className: "pui-drawer__title", children: title }),
791
+ /* @__PURE__ */ jsx13("button", { type: "button", className: "pui-dialog__close", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx13(X, { style: { width: 20, height: 20 } }) })
792
+ ] }),
793
+ /* @__PURE__ */ jsx13("div", { className: "pui-drawer__body", children }),
794
+ footer && /* @__PURE__ */ jsx13("div", { className: "pui-drawer__footer", children: footer })
795
+ ] }) });
796
+ }
797
+ function ToggleGroup({ options, value, onValueChange, className }) {
798
+ return /* @__PURE__ */ jsx13("div", { className: cx("pui-toggle-group", className), role: "group", children: options.map((o) => /* @__PURE__ */ jsxs11(
799
+ "button",
800
+ {
801
+ type: "button",
802
+ "aria-pressed": value === o.value,
803
+ "data-active": value === o.value || void 0,
804
+ className: "pui-toggle",
805
+ onClick: () => onValueChange(o.value),
806
+ children: [
807
+ o.icon,
808
+ o.label
809
+ ]
810
+ },
811
+ o.value
812
+ )) });
813
+ }
814
+ function Stepper({ steps, current, className }) {
815
+ return /* @__PURE__ */ jsx13("ol", { className: cx("pui-stepper", className), children: steps.map((s, i) => {
816
+ const state = i < current ? "done" : i === current ? "current" : "upcoming";
817
+ return /* @__PURE__ */ jsxs11("li", { className: "pui-step", "data-state": state, children: [
818
+ /* @__PURE__ */ jsx13("span", { className: "pui-step__marker", children: i < current ? /* @__PURE__ */ jsx13(Check, {}) : i + 1 }),
819
+ /* @__PURE__ */ jsxs11("span", { className: "pui-step__body", children: [
820
+ /* @__PURE__ */ jsx13("span", { className: "pui-step__label", children: s.label }),
821
+ s.description && /* @__PURE__ */ jsx13("span", { className: "pui-step__desc", children: s.description })
822
+ ] })
823
+ ] }, i);
824
+ }) });
825
+ }
826
+ function CodeBlock({ code, filename, className }) {
827
+ return /* @__PURE__ */ jsxs11("div", { className: cx("pui-code", className), children: [
828
+ filename && /* @__PURE__ */ jsx13("div", { className: "pui-code__bar", children: filename }),
829
+ /* @__PURE__ */ jsx13("pre", { className: "pui-code__pre", children: /* @__PURE__ */ jsx13("code", { children: code }) })
830
+ ] });
831
+ }
832
+ function List({ children, className }) {
833
+ return /* @__PURE__ */ jsx13("ul", { className: cx("pui-list", className), children });
834
+ }
835
+ function ListItem({ children, className, ...props }) {
836
+ return /* @__PURE__ */ jsx13("li", { className: cx("pui-list-item", className), ...props, children });
837
+ }
838
+ export {
839
+ Accordion,
840
+ AccordionItem,
841
+ Alert,
842
+ Avatar,
843
+ AvatarGroup,
844
+ Badge,
845
+ Breadcrumbs,
846
+ Button,
847
+ Card,
848
+ Checkbox,
849
+ CodeBlock,
850
+ Drawer,
851
+ DropdownMenu,
852
+ EmptyState,
853
+ Field,
854
+ icons_exports as Icons,
855
+ Input,
856
+ Kbd,
857
+ Link,
858
+ List,
859
+ ListItem,
860
+ MenuSeparator,
861
+ Modal,
862
+ Pagination,
863
+ Popover,
864
+ Progress,
865
+ RadioGroup,
866
+ SearchInput,
867
+ Segmented,
868
+ Select,
869
+ Separator,
870
+ Skeleton,
871
+ Slider,
872
+ Spinner,
873
+ StatCard,
874
+ Stepper,
875
+ Switch,
876
+ Tab,
877
+ TabList,
878
+ TabPanel,
879
+ Table,
880
+ Tabs,
881
+ Tag,
882
+ Textarea,
883
+ ThemeProvider,
884
+ ThemeToggle,
885
+ ToastProvider,
886
+ ToggleGroup,
887
+ Tooltip,
888
+ cx,
889
+ initials,
890
+ useTheme,
891
+ useToast
892
+ };
893
+ //# sourceMappingURL=index.js.map