@apliteni/apliteni-ui 0.26.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +2 -2
  2. package/package.json +1 -1
  3. package/react/README.md +111 -2
  4. package/react/dist/index.css +11 -13
  5. package/react/dist/index.d.ts +111 -4
  6. package/react/dist/index.js +811 -167
  7. package/src/components/back.js +57 -0
  8. package/src/components/command-palette.js +597 -0
  9. package/src/components/confirm.js +3 -2
  10. package/src/components/drawer.js +31 -2
  11. package/src/components/dropdown.js +192 -17
  12. package/src/components/feedback.js +2 -2
  13. package/src/components/index.js +5 -2
  14. package/src/components/loading.js +3 -2
  15. package/src/components/nav.js +15 -7
  16. package/src/components/overlay.js +67 -14
  17. package/src/components/pagination.js +344 -0
  18. package/src/components/shell.js +11 -2
  19. package/src/components/tabs.js +7 -1
  20. package/src/components/tooltip.js +237 -0
  21. package/src/components/topbar.js +9 -4
  22. package/src/index.css +4 -0
  23. package/src/index.js +4 -0
  24. package/src/inline.js +8 -0
  25. package/src/motion.js +43 -2
  26. package/src/styles/back.css +42 -0
  27. package/src/styles/badge.css +5 -7
  28. package/src/styles/base.css +8 -7
  29. package/src/styles/button.css +14 -0
  30. package/src/styles/card.css +12 -8
  31. package/src/styles/code.css +3 -4
  32. package/src/styles/command-palette.css +321 -0
  33. package/src/styles/confirm.css +11 -11
  34. package/src/styles/drawer.css +57 -15
  35. package/src/styles/dropdown.css +70 -11
  36. package/src/styles/feedback.css +2 -0
  37. package/src/styles/footer.css +3 -4
  38. package/src/styles/input.css +7 -1
  39. package/src/styles/layout.css +3 -2
  40. package/src/styles/loading.css +5 -0
  41. package/src/styles/nav.css +12 -8
  42. package/src/styles/pagination.css +124 -0
  43. package/src/styles/success.css +3 -2
  44. package/src/styles/table.css +4 -5
  45. package/src/styles/tabs.css +3 -0
  46. package/src/styles/tooltip.css +65 -0
  47. package/src/styles/topbar.css +3 -4
  48. package/src/tokens/tokens.css +14 -8
@@ -20,7 +20,7 @@ var cx = (...a) => a.filter(Boolean).join(" ");
20
20
  function Button({
21
21
  variant = "secondary",
22
22
  size = "md",
23
- icon: icon2,
23
+ icon: icon3,
24
24
  iconRight,
25
25
  iconOnly,
26
26
  block,
@@ -38,7 +38,7 @@ function Button({
38
38
  iconOnly && "ui-btn--icon"
39
39
  );
40
40
  const labelled = rest["aria-label"] != null || rest["aria-labelledby"] != null;
41
- const fallback = typeof children === "string" && children.trim() ? children.trim() : icon2;
41
+ const fallback = typeof children === "string" && children.trim() ? children.trim() : icon3;
42
42
  const named = iconOnly && !labelled && fallback ? { "aria-label": fallback, title: rest.title ?? fallback } : {};
43
43
  return /* @__PURE__ */ jsxs(
44
44
  "button",
@@ -51,7 +51,7 @@ function Button({
51
51
  ...rest,
52
52
  ...named,
53
53
  children: [
54
- icon2 && /* @__PURE__ */ jsx2(Icon, { name: icon2 }),
54
+ icon3 && /* @__PURE__ */ jsx2(Icon, { name: icon3 }),
55
55
  !iconOnly && children != null && /* @__PURE__ */ jsx2("span", { children }),
56
56
  iconRight && /* @__PURE__ */ jsx2(Icon, { name: iconRight }),
57
57
  busy && /* @__PURE__ */ jsxs("span", { className: "ui-btn__bars", children: [
@@ -72,18 +72,30 @@ function Badge({ variant = "neutral", children }) {
72
72
 
73
73
  // src/primitives/Card.tsx
74
74
  import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
75
- function Card({ title, sub, children }) {
75
+ function Card({ title, sub, level = 2, children }) {
76
+ const n = Number(level);
77
+ const Heading = `h${[2, 3, 4, 5, 6].includes(n) ? n : 2}`;
78
+ const titled = title != null && title !== false && title !== "";
76
79
  return /* @__PURE__ */ jsxs2("div", { className: "ui-card", children: [
77
- title != null && /* @__PURE__ */ jsx4("div", { className: "ui-card__title", children: title }),
80
+ titled && /* @__PURE__ */ jsx4(Heading, { className: "ui-card__title", children: title }),
78
81
  sub != null && /* @__PURE__ */ jsx4("div", { className: "ui-card__sub", children: sub }),
79
82
  children
80
83
  ] });
81
84
  }
82
85
 
83
86
  // src/Modal.tsx
84
- import { useEffect, useRef } from "react";
87
+ import { useRef as useRef2 } from "react";
85
88
  import { createPortal } from "react-dom";
86
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
89
+
90
+ // src/dialog.ts
91
+ import {
92
+ createContext,
93
+ useContext,
94
+ useEffect,
95
+ useLayoutEffect,
96
+ useRef,
97
+ useState
98
+ } from "react";
87
99
  var FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]), details > summary:first-of-type';
88
100
  function tabbable(el) {
89
101
  const tabindex = el.getAttribute("tabindex");
@@ -103,75 +115,682 @@ var dismissOnScrim = (onClose) => (e) => {
103
115
  e.preventDefault();
104
116
  onClose();
105
117
  };
106
- function Modal({ open, title, onClose, footer, children }) {
107
- const panel = useRef(null);
108
- useEffect(() => {
109
- if (!open) return;
110
- const onKey = (e) => {
111
- if (e.key === "Escape") {
112
- onClose();
113
- return;
114
- }
115
- if (e.key !== "Tab" || !panel.current) return;
116
- const items = tabbablesIn(panel.current);
117
- if (items.length === 0) {
118
- e.preventDefault();
119
- panel.current.focus();
120
- return;
121
- }
122
- const first = items[0];
123
- const last = items[items.length - 1];
124
- const active = document.activeElement;
125
- if (!panel.current.contains(active)) {
126
- e.preventDefault();
127
- first.focus();
128
- return;
129
- }
130
- if (!e.shiftKey && active === last) {
131
- e.preventDefault();
132
- first.focus();
133
- } else if (e.shiftKey && (active === first || active === panel.current)) {
134
- e.preventDefault();
135
- last.focus();
136
- }
118
+ var useIsoLayoutEffect = typeof document === "undefined" ? useEffect : useLayoutEffect;
119
+ function transitionMs(el) {
120
+ const cs = getComputedStyle(el);
121
+ const times = (list) => list.split(",").map((t) => {
122
+ const n = Number.parseFloat(t);
123
+ if (!Number.isFinite(n)) return 0;
124
+ return t.trim().endsWith("ms") ? n : n * 1e3;
125
+ });
126
+ const durations = times(cs.transitionDuration);
127
+ const delays = times(cs.transitionDelay);
128
+ return Math.max(0, ...durations.map((d, i) => d + delays[i % delays.length]));
129
+ }
130
+ var DialogScope = createContext(null);
131
+ var stack = [];
132
+ var leaving = /* @__PURE__ */ new Set();
133
+ var marked = [];
134
+ var listening = false;
135
+ var firstIn = (entry) => (entry.body ? tabbablesIn(entry.body) : [])[0] || entry.panel;
136
+ function trapTab(panel, e) {
137
+ const items = tabbablesIn(panel);
138
+ if (items.length === 0) {
139
+ e.preventDefault();
140
+ panel.focus();
141
+ return;
142
+ }
143
+ const first = items[0];
144
+ const last = items[items.length - 1];
145
+ const focused = document.activeElement;
146
+ if (!panel.contains(focused)) {
147
+ e.preventDefault();
148
+ first.focus();
149
+ return;
150
+ }
151
+ if (!e.shiftKey && focused === last) {
152
+ e.preventDefault();
153
+ first.focus();
154
+ } else if (e.shiftKey && (focused === first || focused === panel)) {
155
+ e.preventDefault();
156
+ last.focus();
157
+ }
158
+ }
159
+ function onKey(e) {
160
+ const top = stack[stack.length - 1];
161
+ if (!top) return;
162
+ if (e.key === "Escape") top.close();
163
+ else if (e.key === "Tab") trapTab(top.panel, e);
164
+ }
165
+ function sync() {
166
+ for (const [el, was] of marked) if (!was) el.removeAttribute("inert");
167
+ marked = [];
168
+ const top = stack[stack.length - 1];
169
+ for (let node = top?.root; node?.parentElement && node !== document.body; node = node.parentElement) {
170
+ for (const sib of Array.from(node.parentElement.children)) {
171
+ if (sib === node || leaving.has(sib)) continue;
172
+ marked.push([sib, sib.hasAttribute("inert")]);
173
+ sib.setAttribute("inert", "");
174
+ }
175
+ }
176
+ leaving.forEach((el) => {
177
+ el.toggleAttribute("inert", true);
178
+ if (el.getAttribute("aria-hidden") !== "true") el.setAttribute("aria-hidden", "true");
179
+ });
180
+ if (Boolean(top) !== listening) {
181
+ listening = Boolean(top);
182
+ if (listening) document.addEventListener("keydown", onKey);
183
+ else document.removeEventListener("keydown", onKey);
184
+ }
185
+ }
186
+ function returnFocus(opener) {
187
+ const live = opener && !opener.isConnected && opener.id ? document.getElementById(opener.id) : opener;
188
+ if (live?.isConnected) live.focus?.();
189
+ if (document.activeElement !== live) document.activeElement?.blur?.();
190
+ }
191
+ function enter(entry) {
192
+ const at = stack.findIndex((e) => {
193
+ for (let s = e.scope.parent; s; s = s.parent) if (s === entry.scope) return true;
194
+ return false;
195
+ });
196
+ if (at !== -1) {
197
+ entry.opener = stack[at].opener;
198
+ stack[at].opener = null;
199
+ stack.splice(at, 0, entry);
200
+ sync();
201
+ return;
202
+ }
203
+ const covered = stack[stack.length - 1];
204
+ if (covered?.panel.contains(document.activeElement)) covered.resume = document.activeElement;
205
+ stack.push(entry);
206
+ sync();
207
+ firstIn(entry).focus();
208
+ }
209
+ function exit(entry) {
210
+ const at = stack.indexOf(entry);
211
+ if (at === -1) return;
212
+ stack.splice(at, 1);
213
+ const above = stack[at];
214
+ if (above && (!above.opener || entry.root.contains(above.opener))) above.opener = entry.opener;
215
+ sync();
216
+ if (above) return;
217
+ const next = stack[stack.length - 1];
218
+ if (!next) {
219
+ returnFocus(entry.opener);
220
+ return;
221
+ }
222
+ const back = [entry.opener, next.resume].find((el) => el?.isConnected && next.panel.contains(el));
223
+ (back ?? firstIn(next)).focus();
224
+ }
225
+ var EXIT_SLACK_MS = 50;
226
+ function usePresence(open, root, panel) {
227
+ const [present, setPresent] = useState(open);
228
+ const [entered, setEntered] = useState(false);
229
+ if (open && !present) setPresent(true);
230
+ const shown = open && entered;
231
+ useIsoLayoutEffect(() => {
232
+ if (!open || entered || !panel.current) return;
233
+ panel.current.getBoundingClientRect();
234
+ setEntered(true);
235
+ }, [open, entered]);
236
+ useIsoLayoutEffect(() => {
237
+ const el = root.current;
238
+ if (open || !present || !el) return;
239
+ leaving.add(el);
240
+ sync();
241
+ return () => {
242
+ leaving.delete(el);
243
+ el.removeAttribute("inert");
244
+ el.removeAttribute("aria-hidden");
245
+ sync();
137
246
  };
138
- document.addEventListener("keydown", onKey);
139
- return () => document.removeEventListener("keydown", onKey);
140
- }, [open, onClose]);
141
- useEffect(() => {
142
- if (!open) return;
143
- const opener = document.activeElement;
144
- const portalRoot = panel.current?.closest(".rx-scrim");
145
- const muted = Array.from(document.body.children).filter((el) => el !== portalRoot && !el.hasAttribute("inert"));
146
- muted.forEach((el) => el.setAttribute("inert", ""));
247
+ }, [open, present]);
248
+ useIsoLayoutEffect(() => {
249
+ if (open || !present) return;
250
+ let done = false;
251
+ const finish = () => {
252
+ if (done) return;
253
+ done = true;
254
+ setPresent(false);
255
+ setEntered(false);
256
+ };
257
+ const el = panel.current;
258
+ if (!el) {
259
+ finish();
260
+ return;
261
+ }
262
+ const onEnd = (e) => {
263
+ if (e.target === el) finish();
264
+ };
265
+ el.addEventListener("transitionend", onEnd);
266
+ const timer = setTimeout(finish, transitionMs(el) + EXIT_SLACK_MS);
147
267
  return () => {
148
- muted.forEach((el) => el.removeAttribute("inert"));
149
- if (opener && document.contains(opener)) opener.focus();
268
+ done = true;
269
+ el.removeEventListener("transitionend", onEnd);
270
+ clearTimeout(timer);
150
271
  };
151
- }, [open]);
272
+ }, [open, present]);
273
+ return { mounted: present, shown };
274
+ }
275
+ function useDialog(active, { root, panel, body }, onClose) {
276
+ const parent = useContext(DialogScope);
277
+ const [scope] = useState(() => ({ parent }));
278
+ const close = useRef(onClose);
279
+ useIsoLayoutEffect(() => {
280
+ close.current = onClose;
281
+ });
152
282
  useEffect(() => {
153
- if (!open) return;
154
- const body = panel.current?.querySelector(".rx-modal__body");
155
- const target = (body ? tabbablesIn(body) : [])[0] || panel.current;
156
- target?.focus();
157
- }, [open]);
158
- if (!open) return null;
283
+ if (!active || !root.current || !panel.current) return;
284
+ const entry = {
285
+ scope,
286
+ root: root.current,
287
+ panel: panel.current,
288
+ body: body.current,
289
+ close: () => close.current(),
290
+ opener: document.activeElement,
291
+ resume: null
292
+ };
293
+ enter(entry);
294
+ return () => exit(entry);
295
+ }, [active]);
296
+ return scope;
297
+ }
298
+
299
+ // src/Modal.tsx
300
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
301
+ function Modal({ open, title, onClose, footer, children }) {
302
+ const root = useRef2(null);
303
+ const panel = useRef2(null);
304
+ const body = useRef2(null);
305
+ const { mounted, shown } = usePresence(open, root, panel);
306
+ const scope = useDialog(shown, { root, panel, body }, onClose);
307
+ if (!mounted) return null;
159
308
  return createPortal(
160
- /* @__PURE__ */ jsx5("div", { className: "rx-scrim", onMouseDown: dismissOnScrim(onClose), children: /* @__PURE__ */ jsxs3("div", { className: "rx-modal", role: "dialog", "aria-modal": "true", "aria-label": title, tabIndex: -1, ref: panel, children: [
309
+ /* @__PURE__ */ jsx5(DialogScope.Provider, { value: scope, children: /* @__PURE__ */ jsx5("div", { className: shown ? "rx-scrim is-open" : "rx-scrim", ref: root, onMouseDown: dismissOnScrim(onClose), children: /* @__PURE__ */ jsxs3("div", { className: "rx-modal", role: "dialog", "aria-modal": "true", "aria-label": title, tabIndex: -1, ref: panel, children: [
161
310
  /* @__PURE__ */ jsxs3("div", { className: "rx-modal__head", children: [
162
311
  /* @__PURE__ */ jsx5("div", { className: "rx-modal__title", children: title }),
163
312
  /* @__PURE__ */ jsx5(Button, { variant: "ghost", size: "sm", iconOnly: true, icon: "x", "aria-label": "Close", onClick: onClose })
164
313
  ] }),
165
- /* @__PURE__ */ jsx5("div", { className: "rx-modal__body", children }),
314
+ /* @__PURE__ */ jsx5("div", { className: "rx-modal__body", ref: body, children }),
166
315
  footer && /* @__PURE__ */ jsx5("div", { className: "rx-modal__foot", children: footer })
316
+ ] }) }) }),
317
+ document.body
318
+ );
319
+ }
320
+
321
+ // src/Drawer.tsx
322
+ import { useId, useRef as useRef3 } from "react";
323
+ import { createPortal as createPortal2 } from "react-dom";
324
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
325
+ function Drawer({
326
+ open,
327
+ title,
328
+ onClose,
329
+ side = "right",
330
+ size = "md",
331
+ footer,
332
+ children,
333
+ closeLabel = "Close"
334
+ }) {
335
+ const root = useRef3(null);
336
+ const panel = useRef3(null);
337
+ const body = useRef3(null);
338
+ const titleId = useId();
339
+ const { mounted, shown } = usePresence(open, root, panel);
340
+ const scope = useDialog(shown, { root, panel, body }, onClose);
341
+ if (!mounted) return null;
342
+ const cls = ["ui-drawer", `ui-drawer--${side}`, `ui-drawer--${size}`, shown && "is-open"].filter(Boolean).join(" ");
343
+ return createPortal2(
344
+ /* @__PURE__ */ jsx6(DialogScope.Provider, { value: scope, children: /* @__PURE__ */ jsxs4("div", { className: cls, ref: root, children: [
345
+ /* @__PURE__ */ jsx6("div", { className: "ui-drawer__scrim", onMouseDown: dismissOnScrim(onClose) }),
346
+ /* @__PURE__ */ jsxs4(
347
+ "aside",
348
+ {
349
+ className: "ui-drawer__panel",
350
+ role: "dialog",
351
+ "aria-modal": "true",
352
+ "aria-labelledby": titleId,
353
+ tabIndex: -1,
354
+ ref: panel,
355
+ children: [
356
+ /* @__PURE__ */ jsxs4("header", { className: "ui-drawer__header", children: [
357
+ /* @__PURE__ */ jsx6("h2", { className: "ui-drawer__title", id: titleId, children: title }),
358
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "ui-drawer__close", "aria-label": closeLabel, onClick: onClose, children: /* @__PURE__ */ jsx6(Icon, { name: "x" }) })
359
+ ] }),
360
+ /* @__PURE__ */ jsx6("div", { className: "ui-drawer__body", ref: body, children }),
361
+ footer && /* @__PURE__ */ jsx6("footer", { className: "ui-drawer__footer", children: footer })
362
+ ]
363
+ }
364
+ )
167
365
  ] }) }),
168
366
  document.body
169
367
  );
170
368
  }
171
369
 
370
+ // src/CommandPalette.tsx
371
+ import { useCallback, useEffect as useEffect2, useId as useId2, useMemo, useRef as useRef4, useState as useState2 } from "react";
372
+ import { createPortal as createPortal3 } from "react-dom";
373
+ import { rankGroups, icon as icon2 } from "@apliteni/apliteni-ui";
374
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
375
+ var cx2 = (...a) => a.filter(Boolean).join(" ");
376
+ var Glyph = ({ name, className }) => /* @__PURE__ */ jsx7("span", { className, "aria-hidden": "true", dangerouslySetInnerHTML: { __html: icon2(name) } });
377
+ var keysOf = (shortcut) => shortcut == null ? [] : Array.isArray(shortcut) ? shortcut : [shortcut];
378
+ function CommandPalette({
379
+ open,
380
+ onClose,
381
+ groups,
382
+ onSelect,
383
+ label = "Command palette",
384
+ placeholder = "Search or run a command\u2026",
385
+ empty = "No matches",
386
+ density = "compact",
387
+ hint = true,
388
+ rank = true,
389
+ onQueryChange
390
+ }) {
391
+ const uid = useId2().replace(/:/g, "");
392
+ const root = useRef4(null);
393
+ const panel = useRef4(null);
394
+ const search = useRef4(null);
395
+ const [query, setQuery] = useState2("");
396
+ const [at, setAt] = useState2(0);
397
+ useEffect2(() => {
398
+ if (open) {
399
+ setQuery("");
400
+ setAt(0);
401
+ }
402
+ }, [open]);
403
+ const shown = useMemo(
404
+ () => (rank ? rankGroups(groups, query) : groups).filter((g) => g.items.length),
405
+ [groups, query, rank]
406
+ );
407
+ const rows = useMemo(() => shown.flatMap((g) => g.items).filter((it) => !isDisabled(it)), [shown]);
408
+ const active = rows[Math.min(at, rows.length - 1)];
409
+ useEffect2(() => {
410
+ setAt(0);
411
+ }, [query]);
412
+ const scope = useDialog(open, { root, panel, body: search }, onClose);
413
+ const run = useCallback((item, newTab) => {
414
+ if (isDisabled(item)) return;
415
+ onSelect?.(item, { newTab });
416
+ if (item.danger && item.onConfirm) {
417
+ item.onConfirm();
418
+ return;
419
+ }
420
+ onClose();
421
+ if (!item.href) return;
422
+ if (newTab) window.open(item.href, "_blank", "noopener");
423
+ else window.location.href = item.href;
424
+ }, [onClose, onSelect]);
425
+ const onKeyDown = (e) => {
426
+ if (e.key === "ArrowDown" || e.key === "ArrowUp") {
427
+ if (!rows.length) return;
428
+ e.preventDefault();
429
+ const step = e.key === "ArrowDown" ? 1 : -1;
430
+ setAt((was) => (was + step + rows.length) % rows.length);
431
+ } else if (e.key === "Enter") {
432
+ if (!active) return;
433
+ e.preventDefault();
434
+ run(active, e.metaKey || e.ctrlKey);
435
+ }
436
+ };
437
+ if (!open) return null;
438
+ const listId = `${uid}-list`;
439
+ let n = 0;
440
+ return createPortal3(
441
+ /* @__PURE__ */ jsx7(DialogScope.Provider, { value: scope, children: /* @__PURE__ */ jsxs5("div", { className: cx2("ui-cmdk", density === "roomy" && "ui-cmdk--roomy", "is-open"), ref: root, children: [
442
+ /* @__PURE__ */ jsx7("div", { className: "ui-cmdk__scrim", onMouseDown: dismissOnScrim(onClose) }),
443
+ /* @__PURE__ */ jsxs5(
444
+ "div",
445
+ {
446
+ className: "ui-cmdk__panel",
447
+ role: "dialog",
448
+ "aria-modal": "true",
449
+ "aria-label": label,
450
+ tabIndex: -1,
451
+ ref: panel,
452
+ onKeyDown,
453
+ children: [
454
+ /* @__PURE__ */ jsxs5("div", { className: "ui-cmdk__search", ref: search, children: [
455
+ /* @__PURE__ */ jsx7(Glyph, { name: "search", className: "ui-cmdk__search-ic" }),
456
+ /* @__PURE__ */ jsx7(
457
+ "input",
458
+ {
459
+ className: "ui-cmdk__input",
460
+ type: "text",
461
+ role: "combobox",
462
+ "aria-autocomplete": "list",
463
+ "aria-expanded": true,
464
+ "aria-controls": listId,
465
+ "aria-label": label,
466
+ "aria-activedescendant": active ? rowId(uid, shown, active) : void 0,
467
+ placeholder,
468
+ value: query,
469
+ autoComplete: "off",
470
+ spellCheck: false,
471
+ onChange: (e) => {
472
+ setQuery(e.target.value);
473
+ onQueryChange?.(e.target.value);
474
+ }
475
+ }
476
+ )
477
+ ] }),
478
+ /* @__PURE__ */ jsx7("div", { className: "ui-cmdk__list", id: listId, role: "listbox", "aria-label": `${label} results`, children: shown.map((group, gi) => /* @__PURE__ */ jsxs5(
479
+ "div",
480
+ {
481
+ className: "ui-cmdk__group",
482
+ role: "group",
483
+ "aria-labelledby": group.label ? `${uid}-g${gi}` : void 0,
484
+ children: [
485
+ group.label && /* @__PURE__ */ jsx7("div", { className: "ui-cmdk__group-head", id: `${uid}-g${gi}`, children: group.label }),
486
+ group.items.map((item) => {
487
+ const disabled = isDisabled(item);
488
+ const id = `${uid}-o${n++}`;
489
+ const on = item === active;
490
+ return /* @__PURE__ */ jsxs5(
491
+ "div",
492
+ {
493
+ id,
494
+ className: cx2(
495
+ "ui-cmdk__item",
496
+ item.danger && !disabled && "is-danger",
497
+ disabled && "is-disabled",
498
+ on && "is-active"
499
+ ),
500
+ role: "option",
501
+ tabIndex: -1,
502
+ "aria-selected": on,
503
+ "aria-disabled": disabled || void 0,
504
+ "aria-haspopup": item.danger && item.onConfirm ? "dialog" : void 0,
505
+ onMouseMove: () => {
506
+ if (!disabled) setAt(rows.indexOf(item));
507
+ },
508
+ onClick: (e) => run(item, e.metaKey || e.ctrlKey),
509
+ children: [
510
+ item.icon && /* @__PURE__ */ jsx7(Glyph, { name: item.icon, className: "ui-cmdk__ic" }),
511
+ /* @__PURE__ */ jsxs5("span", { className: "ui-cmdk__main", children: [
512
+ /* @__PURE__ */ jsx7("span", { className: "ui-cmdk__label", children: item.label }),
513
+ item.description && /* @__PURE__ */ jsx7("span", { className: "ui-cmdk__desc", children: item.description })
514
+ ] }),
515
+ item.badge && /* @__PURE__ */ jsx7("span", { className: "ui-cmdk__badge", children: item.badge }),
516
+ keysOf(item.shortcut).length > 0 && /* @__PURE__ */ jsx7("span", { className: "ui-cmdk__keys", "aria-hidden": "true", children: keysOf(item.shortcut).map((k) => /* @__PURE__ */ jsx7("kbd", { className: "ui-cmdk__key", children: k }, k)) })
517
+ ]
518
+ },
519
+ item.id
520
+ );
521
+ })
522
+ ]
523
+ },
524
+ group.label ?? `g${gi}`
525
+ )) }),
526
+ /* @__PURE__ */ jsx7("p", { className: "ui-cmdk__empty", hidden: countOf(shown) > 0, children: empty }),
527
+ /* @__PURE__ */ jsx7("p", { className: "ui-sr", role: "status", "aria-live": "polite", children: countOf(shown) === 0 ? "No results" : `${countOf(shown)} result${countOf(shown) === 1 ? "" : "s"}` }),
528
+ hint && /* @__PURE__ */ jsxs5("div", { className: "ui-cmdk__foot", "aria-hidden": "true", children: [
529
+ /* @__PURE__ */ jsxs5("span", { children: [
530
+ /* @__PURE__ */ jsx7("kbd", { className: "ui-cmdk__key", children: "\u2191" }),
531
+ /* @__PURE__ */ jsx7("kbd", { className: "ui-cmdk__key", children: "\u2193" }),
532
+ " move"
533
+ ] }),
534
+ /* @__PURE__ */ jsxs5("span", { children: [
535
+ /* @__PURE__ */ jsx7("kbd", { className: "ui-cmdk__key", children: "\u21B5" }),
536
+ " run"
537
+ ] }),
538
+ /* @__PURE__ */ jsxs5("span", { children: [
539
+ /* @__PURE__ */ jsx7("kbd", { className: "ui-cmdk__key", children: "esc" }),
540
+ " close"
541
+ ] })
542
+ ] })
543
+ ]
544
+ }
545
+ )
546
+ ] }) }),
547
+ document.body
548
+ );
549
+ }
550
+ var isDisabled = (item) => !!item.disabled || !!item.danger && !item.onConfirm;
551
+ var countOf = (groups) => groups.reduce((t, g) => t + g.items.length, 0);
552
+ function rowId(uid, groups, item) {
553
+ let n = 0;
554
+ for (const group of groups) {
555
+ for (const it of group.items) {
556
+ if (it === item) return `${uid}-o${n}`;
557
+ n += 1;
558
+ }
559
+ }
560
+ return void 0;
561
+ }
562
+
563
+ // src/DataTable.tsx
564
+ import { useMemo as useMemo2, useState as useState4 } from "react";
565
+ import { DEFAULT_PAGE_SIZE as DEFAULT_PAGE_SIZE2 } from "@apliteni/apliteni-ui";
566
+
567
+ // src/Pagination.tsx
568
+ import { useEffect as useEffect3, useId as useId3, useRef as useRef5, useState as useState3 } from "react";
569
+ import { DEFAULT_PAGE_SIZE } from "@apliteni/apliteni-ui";
570
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
571
+ var GHOST_SM = "ui-btn ui-btn--ghost ui-btn--sm";
572
+ var VARIANTS = ["steps", "numbered", "jump"];
573
+ var cx3 = (...a) => a.filter(Boolean).join(" ");
574
+ var CAP = Number.MAX_SAFE_INTEGER;
575
+ var int = (v, fallback) => {
576
+ const raw = typeof v === "number" ? v : typeof v === "string" && v.trim() !== "" ? Number(v) : NaN;
577
+ if (!Number.isFinite(raw)) return fallback;
578
+ return Math.min(Math.max(Math.trunc(raw), -CAP), CAP);
579
+ };
580
+ var sizeOf = (v, fallback) => {
581
+ const asked = int(v, fallback);
582
+ return asked > 0 ? asked : fallback;
583
+ };
584
+ var fmt = (n) => n.toLocaleString("en-US");
585
+ function slotsFor(page, pageCount) {
586
+ const wanted = [1, pageCount, page - 1, page, page + 1].filter((n) => n >= 1 && n <= pageCount);
587
+ const shown = [...new Set(wanted)].sort((a, b) => a - b);
588
+ const out = [];
589
+ for (const n of shown) {
590
+ const prev = out.length ? out[out.length - 1] : null;
591
+ if (prev != null) {
592
+ if (n - prev === 2) out.push(prev + 1);
593
+ else if (n - prev > 2) out.push(null);
594
+ }
595
+ out.push(n);
596
+ }
597
+ return out;
598
+ }
599
+ function Pagination({
600
+ page = 1,
601
+ pageSize,
602
+ total = null,
603
+ hasMore = false,
604
+ pageSizes = null,
605
+ variant = "steps",
606
+ label = "Pagination",
607
+ loading = false,
608
+ id,
609
+ onPageChange,
610
+ onPageSizeChange
611
+ }) {
612
+ const auto = useId3();
613
+ const [draft, setDraft] = useState3("");
614
+ const [drafting, setDrafting] = useState3(false);
615
+ const nav = useRef5(null);
616
+ const pressed = useRef5(null);
617
+ const uid = id ?? auto;
618
+ const kind = VARIANTS.includes(variant) ? variant : "steps";
619
+ const size = sizeOf(pageSize, DEFAULT_PAGE_SIZE);
620
+ const asRows = int(total, null);
621
+ const counted = total != null && asRows !== null;
622
+ const rows = counted ? Math.max(0, asRows) : null;
623
+ const last = counted ? Math.max(1, Math.ceil(rows / size)) : null;
624
+ const at = counted ? Math.min(Math.max(1, int(page, 1)), last) : Math.min(Math.max(1, int(page, 1)), CAP - 1);
625
+ const [draftFor, setDraftFor] = useState3(at);
626
+ if (draftFor !== at) {
627
+ setDraftFor(at);
628
+ setDraft("");
629
+ setDrafting(false);
630
+ }
631
+ const offered = (Array.isArray(pageSizes) ? pageSizes : []).slice(0, 12).map((s) => int(s, 0)).filter((s) => s > 0);
632
+ const sizes = offered.length ? [.../* @__PURE__ */ new Set([...offered, size])].sort((a, b) => a - b) : [];
633
+ useEffect3(() => {
634
+ const was = pressed.current;
635
+ if (!was || loading) return;
636
+ pressed.current = null;
637
+ const lost = document.activeElement === document.body || document.activeElement === was;
638
+ if (!lost || !nav.current?.contains(was)) return;
639
+ if (!was.disabled) {
640
+ was.focus();
641
+ return;
642
+ }
643
+ const strip = [...nav.current.querySelectorAll(".ui-pager__step")];
644
+ const from2 = strip.indexOf(was);
645
+ for (let d = 1; d < strip.length; d += 1) {
646
+ const near = [strip[from2 - d], strip[from2 + d]].find((el) => el && !el.disabled);
647
+ if (near) {
648
+ near.focus();
649
+ return;
650
+ }
651
+ }
652
+ }, [at, loading]);
653
+ const single = counted && last === 1;
654
+ if (single && !sizes.length) return null;
655
+ const from = (at - 1) * size + 1;
656
+ const to = counted ? Math.min(at * size, rows) : null;
657
+ const status = !counted ? `Page ${fmt(at)}` : rows === 0 ? "0 of 0" : from === to ? `${fmt(from)} of ${fmt(rows)}` : `${fmt(from)}\u2013${fmt(to)} of ${fmt(rows)}`;
658
+ const go = (n) => onPageChange?.(n);
659
+ const step = (label_, target, disabled) => (
660
+ // A control at an end is disabled and stays where it is: removing it slides
661
+ // the next control under a pointer already travelling toward it.
662
+ /* @__PURE__ */ jsx8(
663
+ "button",
664
+ {
665
+ type: "button",
666
+ className: `${GHOST_SM} ui-pager__step`,
667
+ "data-page": target,
668
+ disabled: loading || disabled,
669
+ "aria-disabled": loading || disabled ? true : void 0,
670
+ onClick: (e) => {
671
+ pressed.current = e.currentTarget;
672
+ go(target);
673
+ },
674
+ children: label_
675
+ },
676
+ label_
677
+ )
678
+ );
679
+ const atStart = at === 1;
680
+ const atEnd = counted && at === last;
681
+ const ends = kind !== "numbered";
682
+ const commitJump = () => {
683
+ setDrafting(false);
684
+ const typed = draft.trim();
685
+ if (typed === "") return;
686
+ const n = Math.min(Math.max(1, int(typed, at)), last);
687
+ if (n !== at) go(n);
688
+ };
689
+ const leaveJump = (to2) => {
690
+ if (to2 && nav.current?.contains(to2)) {
691
+ setDrafting(false);
692
+ setDraft("");
693
+ return;
694
+ }
695
+ commitJump();
696
+ };
697
+ let steps = null;
698
+ if (single) {
699
+ steps = null;
700
+ } else if (!counted) {
701
+ steps = /* @__PURE__ */ jsxs6("div", { className: "ui-pager__steps", children: [
702
+ step("Prev", Math.max(1, at - 1), atStart),
703
+ step("Next", at + 1, !hasMore)
704
+ ] });
705
+ } else {
706
+ let middle = null;
707
+ if (kind === "numbered") {
708
+ middle = slotsFor(at, last).map((n, i) => n == null ? /* @__PURE__ */ jsx8("span", { className: "ui-pager__gap", "aria-hidden": "true", children: "\u2026" }, `gap${i}`) : /* @__PURE__ */ jsx8(
709
+ "button",
710
+ {
711
+ type: "button",
712
+ "data-page": n,
713
+ className: cx3(`${GHOST_SM} ui-pager__page`, n === at && "is-current"),
714
+ "aria-current": n === at ? "page" : void 0,
715
+ disabled: loading,
716
+ "aria-disabled": loading ? true : void 0,
717
+ onClick: () => go(n),
718
+ children: fmt(n)
719
+ },
720
+ n
721
+ ));
722
+ } else if (kind === "jump") {
723
+ middle = /* @__PURE__ */ jsxs6("span", { className: "ui-pager__jump", children: [
724
+ /* @__PURE__ */ jsx8("label", { htmlFor: `${uid}-jump`, children: "Page" }),
725
+ /* @__PURE__ */ jsx8(
726
+ "input",
727
+ {
728
+ className: "ui-input ui-pager__jump-input",
729
+ id: `${uid}-jump`,
730
+ type: "number",
731
+ min: 1,
732
+ max: last,
733
+ disabled: loading,
734
+ value: drafting ? draft : String(at),
735
+ onChange: (e) => {
736
+ setDrafting(true);
737
+ setDraft(e.target.value);
738
+ },
739
+ onBlur: (e) => leaveJump(e.relatedTarget),
740
+ onKeyDown: (e) => {
741
+ if (e.key === "Enter") {
742
+ e.preventDefault();
743
+ commitJump();
744
+ }
745
+ }
746
+ }
747
+ ),
748
+ /* @__PURE__ */ jsxs6("span", { className: "ui-pager__jump-of", children: [
749
+ "of ",
750
+ fmt(last)
751
+ ] })
752
+ ] });
753
+ }
754
+ steps = /* @__PURE__ */ jsxs6("div", { className: "ui-pager__steps", children: [
755
+ ends ? step("First", 1, atStart) : null,
756
+ step("Prev", Math.max(1, at - 1), atStart),
757
+ middle,
758
+ step("Next", Math.min(last, at + 1), atEnd),
759
+ ends ? step("Last", last, atEnd) : null
760
+ ] });
761
+ }
762
+ return /* @__PURE__ */ jsxs6(
763
+ "nav",
764
+ {
765
+ ref: nav,
766
+ className: cx3("ui-pager", `ui-pager--${kind}`, !counted && "ui-pager--open", single && "ui-pager--single"),
767
+ "aria-label": label,
768
+ "aria-busy": loading ? true : void 0,
769
+ children: [
770
+ /* @__PURE__ */ jsx8("p", { className: "ui-pager__status", "aria-live": "polite", "aria-atomic": "true", children: status }),
771
+ sizes.length ? /* @__PURE__ */ jsxs6("div", { className: "ui-pager__size", children: [
772
+ /* @__PURE__ */ jsx8("label", { className: "ui-pager__size-label", htmlFor: `${uid}-size`, children: "Rows" }),
773
+ /* @__PURE__ */ jsx8(
774
+ "select",
775
+ {
776
+ className: "ui-select ui-pager__size-select",
777
+ id: `${uid}-size`,
778
+ disabled: loading,
779
+ value: size,
780
+ onChange: (e) => onPageSizeChange?.(Number(e.target.value)),
781
+ children: sizes.map((s) => /* @__PURE__ */ jsx8("option", { value: s, children: fmt(s) }, s))
782
+ }
783
+ )
784
+ ] }) : null,
785
+ steps
786
+ ]
787
+ }
788
+ );
789
+ }
790
+
172
791
  // src/DataTable.tsx
173
- import { useMemo, useState } from "react";
174
- import { Fragment, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
792
+ import { Fragment, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
793
+ var NO_SORT = { key: void 0, dir: -1 };
175
794
  function sortTableRows(rows, sort) {
176
795
  if (sort.key === void 0) return [...rows];
177
796
  const key = sort.key;
@@ -180,7 +799,12 @@ function sortTableRows(rows, sort) {
180
799
  function DataTable({
181
800
  columns,
182
801
  rows,
183
- pageSize = 4,
802
+ pageSize,
803
+ pageSizes = null,
804
+ onPageSizeChange,
805
+ pager = true,
806
+ pagerLabel,
807
+ loading = false,
184
808
  selectable = true,
185
809
  selected = /* @__PURE__ */ new Set(),
186
810
  onToggle = () => {
@@ -188,113 +812,124 @@ function DataTable({
188
812
  onTogglePage = () => {
189
813
  },
190
814
  sort: controlledSort,
191
- onSortChange
815
+ onSortChange,
816
+ page: controlledPage,
817
+ onPageChange,
818
+ total,
819
+ hasMore = false
192
820
  }) {
193
- const [localSort, setLocalSort] = useState(
821
+ const [localSort, setLocalSort] = useState4(
194
822
  { key: columns.find((c) => c.sortable)?.key, dir: -1 }
195
823
  );
196
- const sort = controlledSort ?? localSort;
197
- const [page, setPage] = useState(0);
198
- const [pagedSort, setPagedSort] = useState(sort);
824
+ const owned = controlledPage === void 0;
825
+ const sort = controlledSort ?? (owned ? localSort : NO_SORT);
826
+ const sortIsKnown = owned || controlledSort !== void 0;
827
+ const [localPage, setLocalPage] = useState4(1);
828
+ const [localSize, setLocalSize] = useState4(DEFAULT_PAGE_SIZE2);
829
+ const size = sizeOf(pageSize, owned ? localSize : rows.length || DEFAULT_PAGE_SIZE2);
830
+ const [pagedSort, setPagedSort] = useState4(sort);
199
831
  if (pagedSort.key !== sort.key || pagedSort.dir !== sort.dir) {
200
832
  setPagedSort(sort);
201
- setPage(0);
833
+ setLocalPage(1);
202
834
  }
203
- const sorted = useMemo(() => sortTableRows(rows, sort), [rows, sort.key, sort.dir]);
204
- const pages = Math.max(1, Math.ceil(sorted.length / pageSize));
205
- const safePage = Math.min(page, pages - 1);
206
- const slice = sorted.slice(safePage * pageSize, safePage * pageSize + pageSize);
835
+ const ordered = useMemo2(
836
+ () => owned ? sortTableRows(rows, sort) : rows,
837
+ [owned, rows, sort.key, sort.dir]
838
+ );
839
+ const pages = Math.max(1, Math.ceil(ordered.length / size));
840
+ const page = owned ? Math.min(Math.max(1, localPage), pages) : controlledPage;
841
+ const slice = owned ? ordered.slice((page - 1) * size, page * size) : ordered;
842
+ const goTo = (n) => owned ? setLocalPage(n) : onPageChange?.(n);
843
+ const toFirstPage = () => {
844
+ if (owned) setLocalPage(1);
845
+ else if (page !== 1) onPageChange?.(1);
846
+ };
207
847
  const onSort = (key) => {
208
848
  const next = sort.key === key ? { key, dir: sort.dir === 1 ? -1 : 1 } : { key, dir: -1 };
209
849
  if (controlledSort === void 0) setLocalSort(next);
210
850
  onSortChange?.(next);
211
- setPage(0);
851
+ toFirstPage();
212
852
  };
213
853
  const caret = (k) => sort.key === k ? sort.dir === 1 ? " \u25B2" : " \u25BC" : " \u2195";
214
854
  const pageAllOn = selectable && slice.length > 0 && slice.every((r) => selected.has(r.name));
215
- return /* @__PURE__ */ jsxs4(Fragment, { children: [
216
- /* @__PURE__ */ jsxs4("table", { className: "ui-table ui-table--hover ui-table--zebra", children: [
217
- /* @__PURE__ */ jsx6("thead", { children: /* @__PURE__ */ jsxs4("tr", { children: [
218
- selectable ? /* @__PURE__ */ jsx6("th", { scope: "col", children: /* @__PURE__ */ jsx6(
219
- "input",
220
- {
221
- type: "checkbox",
222
- checked: pageAllOn,
223
- "aria-label": "Select all rows on this page",
224
- onChange: () => onTogglePage(slice.map((r) => r.name))
225
- }
226
- ) }) : null,
227
- columns.map((c) => (
228
- // The sort control is a real <button> inside the header cell. It used to be
229
- // role="button" ON the <th>, which threw away the columnheader role and put
230
- // aria-sort on a role that forbids it.
231
- /* @__PURE__ */ jsx6(
232
- "th",
233
- {
234
- scope: "col",
235
- className: [c.num && "ui-table__num", c.sortable && "rx-sortable"].filter(Boolean).join(" "),
236
- "aria-sort": sort.key === c.key ? sort.dir === 1 ? "ascending" : "descending" : c.sortable ? "none" : void 0,
237
- children: c.sortable ? /* @__PURE__ */ jsxs4("button", { type: "button", className: "rx-sort", onClick: () => onSort(c.key), children: [
238
- c.label,
239
- /* @__PURE__ */ jsx6("span", { className: "rx-caret", "aria-hidden": "true", children: caret(c.key) })
240
- ] }) : c.label
241
- },
242
- c.key
243
- )
244
- ))
245
- ] }) }),
246
- /* @__PURE__ */ jsx6("tbody", { children: slice.map((r) => /* @__PURE__ */ jsxs4("tr", { children: [
247
- selectable ? /* @__PURE__ */ jsx6("td", { children: /* @__PURE__ */ jsx6(
248
- "input",
249
- {
250
- type: "checkbox",
251
- checked: selected.has(r.name),
252
- "aria-label": `Select ${r.name}`,
253
- onChange: () => onToggle(r.name)
254
- }
255
- ) }) : null,
256
- columns.map((c) => /* @__PURE__ */ jsx6("td", { className: c.num ? "ui-table__num" : void 0, children: c.render ? c.render(r) : String(r[c.key]) }, c.key))
257
- ] }, r.name)) })
258
- ] }),
259
- /* @__PURE__ */ jsxs4("div", { className: "rx-pager", children: [
260
- /* @__PURE__ */ jsxs4("span", { className: "rx-pager__info", children: [
261
- "Page ",
262
- safePage + 1,
263
- " of ",
264
- pages,
265
- " \xB7 ",
266
- sorted.length,
267
- " rows"
268
- ] }),
269
- /* @__PURE__ */ jsx6(
270
- Button,
271
- {
272
- variant: "ghost",
273
- size: "sm",
274
- icon: "chevronLeft",
275
- disabled: safePage === 0,
276
- onClick: () => setPage(safePage - 1),
277
- children: "Prev"
278
- }
279
- ),
280
- /* @__PURE__ */ jsx6(
281
- Button,
282
- {
283
- variant: "ghost",
284
- size: "sm",
285
- iconRight: "chevronRight",
286
- disabled: safePage >= pages - 1,
287
- onClick: () => setPage(safePage + 1),
288
- children: "Next"
855
+ return /* @__PURE__ */ jsxs7(Fragment, { children: [
856
+ /* @__PURE__ */ jsxs7(
857
+ "table",
858
+ {
859
+ className: "ui-table ui-table--hover ui-table--zebra",
860
+ "aria-busy": loading || void 0,
861
+ children: [
862
+ /* @__PURE__ */ jsx9("thead", { children: /* @__PURE__ */ jsxs7("tr", { children: [
863
+ selectable ? /* @__PURE__ */ jsx9("th", { scope: "col", children: /* @__PURE__ */ jsx9(
864
+ "input",
865
+ {
866
+ type: "checkbox",
867
+ checked: pageAllOn,
868
+ "aria-label": "Select all rows on this page",
869
+ onChange: () => onTogglePage(slice.map((r) => r.name))
870
+ }
871
+ ) }) : null,
872
+ columns.map((c) => (
873
+ // The sort control is a real <button> inside the header cell. It used to be
874
+ // role="button" ON the <th>, which threw away the columnheader role and put
875
+ // aria-sort on a role that forbids it.
876
+ /* @__PURE__ */ jsx9(
877
+ "th",
878
+ {
879
+ scope: "col",
880
+ className: [c.num && "ui-table__num", c.sortable && "rx-sortable"].filter(Boolean).join(" "),
881
+ "aria-sort": sort.key === c.key ? sort.dir === 1 ? "ascending" : "descending" : c.sortable && sortIsKnown ? "none" : void 0,
882
+ children: c.sortable ? /* @__PURE__ */ jsxs7("button", { type: "button", className: "rx-sort", onClick: () => onSort(c.key), children: [
883
+ c.label,
884
+ /* @__PURE__ */ jsx9("span", { className: "rx-caret", "aria-hidden": "true", children: caret(c.key) })
885
+ ] }) : c.label
886
+ },
887
+ c.key
888
+ )
889
+ ))
890
+ ] }) }),
891
+ /* @__PURE__ */ jsx9("tbody", { children: slice.map((r) => /* @__PURE__ */ jsxs7("tr", { children: [
892
+ selectable ? /* @__PURE__ */ jsx9("td", { children: /* @__PURE__ */ jsx9(
893
+ "input",
894
+ {
895
+ type: "checkbox",
896
+ checked: selected.has(r.name),
897
+ "aria-label": `Select ${r.name}`,
898
+ onChange: () => onToggle(r.name)
899
+ }
900
+ ) }) : null,
901
+ columns.map((c) => /* @__PURE__ */ jsx9("td", { className: c.num ? "ui-table__num" : void 0, children: c.render ? c.render(r) : String(r[c.key]) }, c.key))
902
+ ] }, r.name)) })
903
+ ]
904
+ }
905
+ ),
906
+ pager ? /* @__PURE__ */ jsx9(
907
+ Pagination,
908
+ {
909
+ page,
910
+ pageSize: size,
911
+ total: owned ? ordered.length : total ?? null,
912
+ hasMore,
913
+ pageSizes,
914
+ loading,
915
+ ...pagerLabel === void 0 ? {} : { label: pagerLabel },
916
+ onPageChange: goTo,
917
+ onPageSizeChange: (s) => {
918
+ if (pageSize === void 0) setLocalSize(s);
919
+ onPageSizeChange?.(s);
920
+ if (owned) setLocalPage(1);
289
921
  }
290
- )
291
- ] })
922
+ }
923
+ ) : null
292
924
  ] });
293
925
  }
294
926
 
927
+ // src/index.ts
928
+ import { PAGE_SIZES as KIT_PAGE_SIZES, DEFAULT_PAGE_SIZE as KIT_DEFAULT_PAGE_SIZE } from "@apliteni/apliteni-ui";
929
+
295
930
  // src/Loading.tsx
296
- import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
297
- var cx2 = (...a) => a.filter(Boolean).join(" ");
931
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
932
+ var cx4 = (...a) => a.filter(Boolean).join(" ");
298
933
  function Skeleton({ lines = 3, width, height, radius, className }) {
299
934
  const widths = Array.isArray(lines) ? lines : null;
300
935
  const n = widths ? widths.length : Math.max(1, lines);
@@ -303,19 +938,19 @@ function Skeleton({ lines = 3, width, height, radius, className }) {
303
938
  if (!w && !height && !radius) return void 0;
304
939
  return { ...w && { width: w }, ...height && { height }, ...radius && { borderRadius: radius } };
305
940
  };
306
- return /* @__PURE__ */ jsx7("div", { className: cx2("ui-skel", className), "aria-hidden": "true", children: Array.from({ length: n }, (_, i) => /* @__PURE__ */ jsx7("span", { className: "ui-skel__bar m-skeleton", style: styleFor(i) }, i)) });
941
+ return /* @__PURE__ */ jsx10("div", { className: cx4("ui-skel", className), "aria-hidden": "true", children: Array.from({ length: n }, (_, i) => /* @__PURE__ */ jsx10("span", { className: "ui-skel__bar m-skeleton", style: styleFor(i) }, i)) });
307
942
  }
308
943
  function SkeletonTable({ rows = 5, cols = 4, head = true }) {
309
- const cells = Array.from({ length: Math.max(1, cols) }, (_, i) => /* @__PURE__ */ jsx7("span", { className: "ui-skel__bar m-skeleton" }, i));
310
- return /* @__PURE__ */ jsxs5(
944
+ const cells = Array.from({ length: Math.max(1, cols) }, (_, i) => /* @__PURE__ */ jsx10("span", { className: "ui-skel__bar m-skeleton" }, i));
945
+ return /* @__PURE__ */ jsxs8(
311
946
  "div",
312
947
  {
313
948
  className: "ui-skel ui-skel--table",
314
949
  style: { "--skel-cols": Math.max(1, cols) },
315
950
  "aria-hidden": "true",
316
951
  children: [
317
- head && /* @__PURE__ */ jsx7("div", { className: "ui-skel__row ui-skel__row--head", children: cells }),
318
- Array.from({ length: Math.max(1, rows) }, (_, i) => /* @__PURE__ */ jsx7("div", { className: "ui-skel__row", children: cells }, i))
952
+ head && /* @__PURE__ */ jsx10("div", { className: "ui-skel__row ui-skel__row--head", children: cells }),
953
+ Array.from({ length: Math.max(1, rows) }, (_, i) => /* @__PURE__ */ jsx10("div", { className: "ui-skel__row", children: cells }, i))
319
954
  ]
320
955
  }
321
956
  );
@@ -328,16 +963,16 @@ function BusyRegion({
328
963
  className,
329
964
  children
330
965
  }) {
331
- return /* @__PURE__ */ jsxs5(
966
+ return /* @__PURE__ */ jsxs8(
332
967
  "div",
333
968
  {
334
- className: cx2("ui-busy", className),
969
+ className: cx4("ui-busy", className),
335
970
  role: "status",
336
971
  "aria-live": "polite",
337
972
  "aria-busy": busy,
338
973
  children: [
339
- /* @__PURE__ */ jsx7("span", { className: "ui-sr", children: busy ? label : message }),
340
- /* @__PURE__ */ jsx7("div", { className: "ui-busy__body", children: busy ? placeholder ?? /* @__PURE__ */ jsx7(Skeleton, { lines: 3 }) : children })
974
+ /* @__PURE__ */ jsx10("span", { className: "ui-sr", children: busy ? label : message }),
975
+ /* @__PURE__ */ jsx10("div", { className: "ui-busy__body", children: busy ? placeholder ?? /* @__PURE__ */ jsx10(Skeleton, { lines: 3 }) : children })
341
976
  ]
342
977
  }
343
978
  );
@@ -346,30 +981,39 @@ function Denied({
346
981
  title = "You don\u2019t have access",
347
982
  sub,
348
983
  need,
349
- icon: icon2 = "lock",
984
+ icon: icon3 = "lock",
350
985
  className,
351
986
  children
352
987
  }) {
353
- return /* @__PURE__ */ jsxs5("div", { className: cx2("ui-denied", className), children: [
354
- /* @__PURE__ */ jsx7("div", { className: "ui-denied__seal", children: /* @__PURE__ */ jsx7(Icon, { name: icon2 }) }),
355
- /* @__PURE__ */ jsx7("div", { className: "ui-denied__title", children: title }),
356
- sub && /* @__PURE__ */ jsx7("div", { className: "ui-denied__sub", children: sub }),
357
- need && /* @__PURE__ */ jsxs5("div", { className: "ui-denied__need", children: [
988
+ return /* @__PURE__ */ jsxs8("div", { className: cx4("ui-denied", className), children: [
989
+ /* @__PURE__ */ jsx10("div", { className: "ui-denied__seal", children: /* @__PURE__ */ jsx10(Icon, { name: icon3 }) }),
990
+ /* @__PURE__ */ jsx10("div", { className: "ui-denied__title", children: title }),
991
+ sub && /* @__PURE__ */ jsx10("div", { className: "ui-denied__sub", children: sub }),
992
+ need && /* @__PURE__ */ jsxs8("div", { className: "ui-denied__need", children: [
358
993
  "Needs ",
359
- /* @__PURE__ */ jsx7("code", { className: "ui-code", children: need })
994
+ /* @__PURE__ */ jsx10("code", { className: "ui-code", children: need })
360
995
  ] }),
361
- children && /* @__PURE__ */ jsx7("div", { className: "ui-denied__actions", children })
996
+ children && /* @__PURE__ */ jsx10("div", { className: "ui-denied__actions", children })
362
997
  ] });
363
998
  }
999
+
1000
+ // src/index.ts
1001
+ var PAGE_SIZES = KIT_PAGE_SIZES;
1002
+ var DEFAULT_PAGE_SIZE3 = KIT_DEFAULT_PAGE_SIZE;
364
1003
  export {
365
1004
  Badge,
366
1005
  BusyRegion,
367
1006
  Button,
368
1007
  Card,
1008
+ CommandPalette,
1009
+ DEFAULT_PAGE_SIZE3 as DEFAULT_PAGE_SIZE,
369
1010
  DataTable,
370
1011
  Denied,
1012
+ Drawer,
371
1013
  Icon,
372
1014
  Modal,
1015
+ PAGE_SIZES,
1016
+ Pagination,
373
1017
  Skeleton,
374
1018
  SkeletonTable,
375
1019
  sortTableRows