@handamade/psi-react 0.15.0 → 0.16.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.
@@ -20,7 +20,11 @@
20
20
  * standing in for one number is worse than the number. */
21
21
  export declare function paginationRange(page: number, pageCount: number, siblingCount: number): Array<number | "ellipsis">;
22
22
  export interface PaginationProps {
23
- /** Current page, 1-based. */
23
+ /**
24
+ * Current page, 1-based. A value outside `[1, pageCount]` (including
25
+ * non-finite values like `NaN`) is clamped for rendering and logs a
26
+ * `console.warn` in development. `pageCount < 1` renders no page buttons.
27
+ */
24
28
  page: number;
25
29
  /** Total number of pages. */
26
30
  pageCount: number;
@@ -1 +1 @@
1
- {"version":3,"file":"Pagination.d.ts","sourceRoot":"","sources":["../../src/Pagination/Pagination.tsx"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;;;;0DAmB0D;AAC1D,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC,CAkC5B;AAED,MAAM,WAAW,eAAe;IAC9B,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;OAMG;IACH,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;2BAE2B;AAC3B,wBAAgB,UAAU,CAAC,EACzB,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,YAAgB,EAChB,YAAY,EAAE,SAAwB,EACtC,SAAS,GACV,EAAE,eAAe,+BA+CjB"}
1
+ {"version":3,"file":"Pagination.d.ts","sourceRoot":"","sources":["../../src/Pagination/Pagination.tsx"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;;;;0DAmB0D;AAC1D,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC,CAkC5B;AAED,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;OAMG;IACH,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;2BAE2B;AAC3B,wBAAgB,UAAU,CAAC,EACzB,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,YAAgB,EAChB,YAAY,EAAE,SAAwB,EACtC,SAAS,GACV,EAAE,eAAe,+BA6DjB"}
@@ -15,10 +15,13 @@ export interface TableProps {
15
15
  /** Controlled sort state; `null` when nothing is sorted. */
16
16
  sort?: TableSortState | null;
17
17
  /**
18
- * Called when a header's sort control is activated. Optional in the type
19
- * because `sortable` may be false; a discriminated union expressing the real
20
- * contract does not survive docgen's flat prop extraction, which would strip
21
- * these props from the manifest entirely (D62).
18
+ * Called with the **next** sort state, already toggled — store it as given
19
+ * and do not toggle again. A fresh column arrives `"asc"`; an active `"asc"`
20
+ * column emits `"desc"`; an active `"desc"` column emits `"asc"`.
21
+ *
22
+ * Optional in the type because `sortable` may be false; a discriminated union
23
+ * expressing the real contract does not survive docgen's flat prop
24
+ * extraction, which would strip these props from the manifest entirely (D62).
22
25
  */
23
26
  onSortChange?: (sort: TableSortState) => void;
24
27
  /** Renders the row-selection checkbox column. */
@@ -1 +1 @@
1
- {"version":3,"file":"Table.d.ts","sourceRoot":"","sources":["../../src/Table/Table.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAK5C,MAAM,MAAM,SAAS,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAErC,+DAA+D;AAC/D,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,KAAK,GAAG,MAAM,CAAC;CAC3B;AAiDD,MAAM,WAAW,UAAU;IACzB,oCAAoC;IACpC,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,8CAA8C;IAC9C,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IAC7B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAC9C,iDAAiD;IACjD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC/B,gFAAgF;IAChF,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;IAC5D,+BAA+B;IAC/B,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,GAAG,CAAC,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;CAC7B;AAED;iEACiE;AACjE,wBAAgB,KAAK,CAAC,EACpB,IAAS,EACT,YAAY,EACZ,QAAgB,EAChB,IAAW,EACX,YAAY,EACZ,UAAkB,EAClB,QAA0B,EAC1B,iBAAiB,EACjB,QAAQ,EACR,SAAS,EACT,GAAG,GACJ,EAAE,UAAU,+BAkBZ"}
1
+ {"version":3,"file":"Table.d.ts","sourceRoot":"","sources":["../../src/Table/Table.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAK5C,MAAM,MAAM,SAAS,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAErC,+DAA+D;AAC/D,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,KAAK,GAAG,MAAM,CAAC;CAC3B;AAiDD,MAAM,WAAW,UAAU;IACzB,oCAAoC;IACpC,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,8CAA8C;IAC9C,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IAC7B;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAC9C,iDAAiD;IACjD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC/B,gFAAgF;IAChF,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;IAC5D,+BAA+B;IAC/B,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,GAAG,CAAC,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;CAC7B;AAED;iEACiE;AACjE,wBAAgB,KAAK,CAAC,EACpB,IAAS,EACT,YAAY,EACZ,QAAgB,EAChB,IAAW,EACX,YAAY,EACZ,UAAkB,EAClB,QAA0B,EAC1B,iBAAiB,EACjB,QAAQ,EACR,SAAS,EACT,GAAG,GACJ,EAAE,UAAU,+BAkBZ"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { jsx as n, jsxs as f, Fragment as X } from "react/jsx-runtime";
2
- import { createContext as R, useId as H, useRef as k, useEffect as N, useContext as S, useState as _e, useCallback as g, cloneElement as ne, isValidElement as U, useMemo as oe, Children as A, Fragment as ce } from "react";
3
- const Be = "_button_c94w9_3", $e = "_disabled_c94w9_30", Ne = "_size24_c94w9_44", Le = "_size32_c94w9_54", Se = "_size40_c94w9_64", je = "_size48_c94w9_74", ze = "_accent_c94w9_87", Te = "_accentSubtle_c94w9_99", Ie = "_neutral_c94w9_111", Ee = "_neutralSubtle_c94w9_123", Me = "_ghost_c94w9_135", Ae = "_danger_c94w9_147", Pe = "_dangerSubtle_c94w9_159", We = "_outline_c94w9_171", w = {
2
+ import { createContext as R, useId as H, useRef as k, useEffect as N, useContext as S, useState as _e, useCallback as g, cloneElement as ne, isValidElement as U, useMemo as oe, Children as P, Fragment as ce } from "react";
3
+ const Be = "_button_c94w9_3", $e = "_disabled_c94w9_30", Ne = "_size24_c94w9_44", Le = "_size32_c94w9_54", Se = "_size40_c94w9_64", je = "_size48_c94w9_74", ze = "_accent_c94w9_87", Te = "_accentSubtle_c94w9_99", Ie = "_neutral_c94w9_111", Ee = "_neutralSubtle_c94w9_123", Me = "_ghost_c94w9_135", Pe = "_danger_c94w9_147", Ae = "_dangerSubtle_c94w9_159", We = "_outline_c94w9_171", w = {
4
4
  button: Be,
5
5
  disabled: $e,
6
6
  size24: Ne,
@@ -12,8 +12,8 @@ const Be = "_button_c94w9_3", $e = "_disabled_c94w9_30", Ne = "_size24_c94w9_44"
12
12
  neutral: Ie,
13
13
  neutralSubtle: Ee,
14
14
  ghost: Me,
15
- danger: Ae,
16
- dangerSubtle: Pe,
15
+ danger: Pe,
16
+ dangerSubtle: Ae,
17
17
  outline: We
18
18
  }, De = {
19
19
  accent: w.accent,
@@ -35,10 +35,10 @@ function He({
35
35
  size: t = 32,
36
36
  href: i,
37
37
  target: s,
38
- rel: c,
38
+ rel: a,
39
39
  disabled: r,
40
40
  className: o,
41
- onClick: a,
41
+ onClick: c,
42
42
  ref: d,
43
43
  ...l
44
44
  }) {
@@ -56,9 +56,9 @@ function He({
56
56
  className: u,
57
57
  href: r ? void 0 : i,
58
58
  target: s,
59
- rel: c,
59
+ rel: a,
60
60
  "aria-disabled": r || void 0,
61
- onClick: r ? void 0 : a,
61
+ onClick: r ? void 0 : c,
62
62
  ...l
63
63
  }
64
64
  ) : /* @__PURE__ */ n(
@@ -67,7 +67,7 @@ function He({
67
67
  ref: d,
68
68
  className: u,
69
69
  disabled: r,
70
- onClick: a,
70
+ onClick: c,
71
71
  ...l
72
72
  }
73
73
  );
@@ -99,10 +99,10 @@ function J({
99
99
  size: t = 32,
100
100
  href: i,
101
101
  target: s,
102
- rel: c,
102
+ rel: a,
103
103
  disabled: r,
104
104
  className: o,
105
- onClick: a,
105
+ onClick: c,
106
106
  ref: d,
107
107
  ...l
108
108
  }) {
@@ -120,9 +120,9 @@ function J({
120
120
  className: u,
121
121
  href: r ? void 0 : i,
122
122
  target: s,
123
- rel: c,
123
+ rel: a,
124
124
  "aria-disabled": r || void 0,
125
- onClick: r ? void 0 : a,
125
+ onClick: r ? void 0 : c,
126
126
  ...l
127
127
  }
128
128
  ) : /* @__PURE__ */ n(
@@ -131,7 +131,7 @@ function J({
131
131
  ref: d,
132
132
  className: u,
133
133
  disabled: r,
134
- onClick: a,
134
+ onClick: c,
135
135
  ...l
136
136
  }
137
137
  );
@@ -149,28 +149,28 @@ function Eo({
149
149
  media: t,
150
150
  hoverLift: i = !1,
151
151
  className: s,
152
- children: c,
152
+ children: a,
153
153
  ref: r,
154
154
  ...o
155
155
  }) {
156
- const a = [
156
+ const c = [
157
157
  I.card,
158
158
  e === "featured" ? I.featured : I.stacked,
159
159
  i ? I.hoverLift : void 0,
160
160
  s
161
161
  ].filter(Boolean).join(" ");
162
- return /* @__PURE__ */ f("div", { ref: r, className: a, ...o, children: [
162
+ return /* @__PURE__ */ f("div", { ref: r, className: c, ...o, children: [
163
163
  t != null && /* @__PURE__ */ n("div", { className: I.media, children: t }),
164
- /* @__PURE__ */ n("div", { className: I.body, children: c })
164
+ /* @__PURE__ */ n("div", { className: I.body, children: a })
165
165
  ] });
166
166
  }
167
167
  const st = "_panel_jn8w4_1", at = "_padding16_jn8w4_8", le = {
168
168
  panel: st,
169
169
  padding16: at
170
170
  };
171
- function Mo({ padding: e = 24, className: t, children: i, ref: s, ...c }) {
171
+ function Mo({ padding: e = 24, className: t, children: i, ref: s, ...a }) {
172
172
  const r = [le.panel, e === 16 ? le.padding16 : void 0, t].filter(Boolean).join(" ");
173
- return /* @__PURE__ */ n("div", { ref: s, className: r, ...c, children: i });
173
+ return /* @__PURE__ */ n("div", { ref: s, className: r, ...a, children: i });
174
174
  }
175
175
  const ct = "_list_1lh3q_8", lt = "_item_1lh3q_26", dt = "_term_1lh3q_44", ut = "_value_1lh3q_50", Z = {
176
176
  list: ct,
@@ -178,18 +178,18 @@ const ct = "_list_1lh3q_8", lt = "_item_1lh3q_26", dt = "_term_1lh3q_44", ut = "
178
178
  term: dt,
179
179
  value: ut
180
180
  };
181
- function Ao({
181
+ function Po({
182
182
  layout: e = "stacked",
183
183
  gap: t = 12,
184
184
  className: i,
185
185
  children: s,
186
- ref: c,
186
+ ref: a,
187
187
  ...r
188
188
  }) {
189
189
  return /* @__PURE__ */ n(
190
190
  "dl",
191
191
  {
192
- ref: c,
192
+ ref: a,
193
193
  className: [Z.list, i].filter(Boolean).join(" "),
194
194
  "data-layout": e,
195
195
  "data-gap": t,
@@ -198,8 +198,8 @@ function Ao({
198
198
  }
199
199
  );
200
200
  }
201
- function Po({ term: e, className: t, children: i, ref: s, ...c }) {
202
- return /* @__PURE__ */ f("div", { ref: s, className: [Z.item, t].filter(Boolean).join(" "), ...c, children: [
201
+ function Ao({ term: e, className: t, children: i, ref: s, ...a }) {
202
+ return /* @__PURE__ */ f("div", { ref: s, className: [Z.item, t].filter(Boolean).join(" "), ...a, children: [
203
203
  /* @__PURE__ */ n("dt", { className: Z.term, children: e }),
204
204
  /* @__PURE__ */ n("dd", { className: Z.value, children: i })
205
205
  ] });
@@ -211,9 +211,9 @@ const ht = "_navbar_1gyow_1", _t = "_inner_1gyow_12", ft = "_brand_1gyow_19", pt
211
211
  links: pt,
212
212
  actions: vt
213
213
  };
214
- function Wo({ brand: e, actions: t, children: i, className: s, ref: c, ...r }) {
214
+ function Wo({ brand: e, actions: t, children: i, className: s, ref: a, ...r }) {
215
215
  const o = [F.navbar, s].filter(Boolean).join(" ");
216
- return /* @__PURE__ */ n("header", { ref: c, className: o, ...r, children: /* @__PURE__ */ f("div", { className: `psi-container ${F.inner}`, children: [
216
+ return /* @__PURE__ */ n("header", { ref: a, className: o, ...r, children: /* @__PURE__ */ f("div", { className: `psi-container ${F.inner}`, children: [
217
217
  e != null && /* @__PURE__ */ n("div", { className: F.brand, children: e }),
218
218
  /* @__PURE__ */ n("nav", { className: F.links, children: i }),
219
219
  t != null && /* @__PURE__ */ n("div", { className: F.actions, children: t })
@@ -222,9 +222,9 @@ function Wo({ brand: e, actions: t, children: i, className: s, ref: c, ...r }) {
222
222
  const bt = "_frame_et4fc_1", gt = {
223
223
  frame: bt
224
224
  };
225
- function Do({ ratio: e, className: t, style: i, children: s, ref: c, ...r }) {
225
+ function Do({ ratio: e, className: t, style: i, children: s, ref: a, ...r }) {
226
226
  const o = [gt.frame, t].filter(Boolean).join(" ");
227
- return /* @__PURE__ */ n("div", { ref: c, className: o, style: { aspectRatio: String(e), ...i }, ...r, children: s });
227
+ return /* @__PURE__ */ n("div", { ref: a, className: o, style: { aspectRatio: String(e), ...i }, ...r, children: s });
228
228
  }
229
229
  const mt = "_field_lplye_1", yt = "_label_lplye_10", wt = "_marker_lplye_16", kt = "_message_lplye_20", xt = "_invalid_lplye_26", E = {
230
230
  field: mt,
@@ -238,10 +238,10 @@ function Ro({
238
238
  description: t,
239
239
  error: i,
240
240
  required: s = !1,
241
- group: c = !1,
241
+ group: a = !1,
242
242
  htmlFor: r,
243
243
  className: o,
244
- children: a,
244
+ children: c,
245
245
  ref: d,
246
246
  ...l
247
247
  }) {
@@ -249,11 +249,11 @@ function Ro({
249
249
  e,
250
250
  s && /* @__PURE__ */ n("span", { "aria-hidden": "true", className: E.marker, children: " *" })
251
251
  ] }) : null, b = /* @__PURE__ */ f(X, { children: [
252
- C != null && (c ? /* @__PURE__ */ n("legend", { className: E.label, children: C }) : /* @__PURE__ */ n("label", { className: E.label, htmlFor: h, children: C })),
253
- /* @__PURE__ */ n(re.Provider, { value: { id: h, describedBy: y, invalid: _, required: s }, children: a }),
252
+ C != null && (a ? /* @__PURE__ */ n("legend", { className: E.label, children: C }) : /* @__PURE__ */ n("label", { className: E.label, htmlFor: h, children: C })),
253
+ /* @__PURE__ */ n(re.Provider, { value: { id: h, describedBy: y, invalid: _, required: s }, children: c }),
254
254
  y && /* @__PURE__ */ n("p", { id: y, className: E.message, "aria-live": "polite", children: m })
255
255
  ] });
256
- return c ? /* @__PURE__ */ n(
256
+ return a ? /* @__PURE__ */ n(
257
257
  "fieldset",
258
258
  {
259
259
  ref: d,
@@ -413,7 +413,7 @@ function ar({ size: e = 20, ...t }) {
413
413
  /* @__PURE__ */ n("circle", { cx: "19", cy: "12", r: "1.5", fill: "currentColor", stroke: "none" })
414
414
  ] });
415
415
  }
416
- const Lt = "_dialog_h5f0e_2", St = "_center_h5f0e_32", jt = "_inlineStart_h5f0e_40", zt = "_inlineEnd_h5f0e_41", Tt = "_w400_h5f0e_90", It = "_w560_h5f0e_91", Et = "_w720_h5f0e_92", Mt = "_panel_h5f0e_94", At = "_header_h5f0e_114", Pt = "_title_h5f0e_121", Wt = "_footer_h5f0e_127", L = {
416
+ const Lt = "_dialog_h5f0e_2", St = "_center_h5f0e_32", jt = "_inlineStart_h5f0e_40", zt = "_inlineEnd_h5f0e_41", Tt = "_w400_h5f0e_90", It = "_w560_h5f0e_91", Et = "_w720_h5f0e_92", Mt = "_panel_h5f0e_94", Pt = "_header_h5f0e_114", At = "_title_h5f0e_121", Wt = "_footer_h5f0e_127", L = {
417
417
  dialog: Lt,
418
418
  center: St,
419
419
  inlineStart: jt,
@@ -422,8 +422,8 @@ const Lt = "_dialog_h5f0e_2", St = "_center_h5f0e_32", jt = "_inlineStart_h5f0e_
422
422
  w560: It,
423
423
  w720: Et,
424
424
  panel: Mt,
425
- header: At,
426
- title: Pt,
425
+ header: Pt,
426
+ title: At,
427
427
  footer: Wt
428
428
  }, Dt = {
429
429
  400: L.w400,
@@ -439,10 +439,10 @@ function cr({
439
439
  onClose: t,
440
440
  title: i,
441
441
  footer: s,
442
- width: c = 560,
442
+ width: a = 560,
443
443
  placement: r = "center",
444
444
  dismissible: o = !0,
445
- className: a,
445
+ className: c,
446
446
  children: d,
447
447
  ref: l,
448
448
  ...u
@@ -464,7 +464,7 @@ function cr({
464
464
  b.preventDefault(), o && t("esc");
465
465
  }, z = (b) => {
466
466
  o && b.target === h.current && t("backdrop");
467
- }, C = [L.dialog, Dt[c], Rt[r], a].filter(Boolean).join(" ");
467
+ }, C = [L.dialog, Dt[a], Rt[r], c].filter(Boolean).join(" ");
468
468
  return /* @__PURE__ */ n(
469
469
  "dialog",
470
470
  {
@@ -495,7 +495,7 @@ function cr({
495
495
  }
496
496
  );
497
497
  }
498
- const Ht = "_input_hpei8_3", qt = "_size24_hpei8_37", Vt = "_size32_hpei8_43", Ft = "_size40_hpei8_49", Kt = "_size48_hpei8_55", Ot = "_error_hpei8_63", P = {
498
+ const Ht = "_input_hpei8_3", qt = "_size24_hpei8_37", Vt = "_size32_hpei8_43", Ft = "_size40_hpei8_49", Kt = "_size48_hpei8_55", Ot = "_error_hpei8_63", A = {
499
499
  input: Ht,
500
500
  size24: qt,
501
501
  size32: Vt,
@@ -503,17 +503,17 @@ const Ht = "_input_hpei8_3", qt = "_size24_hpei8_37", Vt = "_size32_hpei8_43", F
503
503
  size48: Kt,
504
504
  error: Ot
505
505
  }, Ut = {
506
- 24: P.size24,
507
- 32: P.size32,
508
- 40: P.size40,
509
- 48: P.size48
506
+ 24: A.size24,
507
+ 32: A.size32,
508
+ 40: A.size40,
509
+ 48: A.size48
510
510
  };
511
- function lr({ size: e = 32, error: t = !1, type: i = "text", className: s, ref: c, ...r }) {
512
- const o = S(re), a = t || ((o == null ? void 0 : o.invalid) ?? !1), d = [o == null ? void 0 : o.describedBy, r["aria-describedby"]].filter(Boolean).join(" ") || void 0, l = [P.input, Ut[e], a && P.error, s].filter(Boolean).join(" ");
511
+ function lr({ size: e = 32, error: t = !1, type: i = "text", className: s, ref: a, ...r }) {
512
+ const o = S(re), c = t || ((o == null ? void 0 : o.invalid) ?? !1), d = [o == null ? void 0 : o.describedBy, r["aria-describedby"]].filter(Boolean).join(" ") || void 0, l = [A.input, Ut[e], c && A.error, s].filter(Boolean).join(" ");
513
513
  return /* @__PURE__ */ n(
514
514
  "input",
515
515
  {
516
- ref: c,
516
+ ref: a,
517
517
  className: l,
518
518
  id: r.id ?? (o == null ? void 0 : o.id),
519
519
  required: r.required ?? ((o == null ? void 0 : o.required) || void 0),
@@ -542,13 +542,13 @@ function dr({
542
542
  error: t = !1,
543
543
  className: i,
544
544
  ref: s,
545
- children: c,
545
+ children: a,
546
546
  ...r
547
547
  }) {
548
- const o = S(re), a = t || ((o == null ? void 0 : o.invalid) ?? !1), d = [o == null ? void 0 : o.describedBy, r["aria-describedby"]].filter(Boolean).join(" ") || void 0, l = [
548
+ const o = S(re), c = t || ((o == null ? void 0 : o.invalid) ?? !1), d = [o == null ? void 0 : o.describedBy, r["aria-describedby"]].filter(Boolean).join(" ") || void 0, l = [
549
549
  W.select,
550
550
  en[e],
551
- a && W.error,
551
+ c && W.error,
552
552
  i
553
553
  ].filter(Boolean).join(" ");
554
554
  return /* @__PURE__ */ n(
@@ -561,7 +561,7 @@ function dr({
561
561
  "aria-invalid": (o == null ? void 0 : o.invalid) || void 0,
562
562
  ...r,
563
563
  "aria-describedby": d,
564
- children: c
564
+ children: a
565
565
  }
566
566
  );
567
567
  }
@@ -577,7 +577,7 @@ function pe({
577
577
  className: t,
578
578
  disabled: i,
579
579
  ref: s,
580
- ...c
580
+ ...a
581
581
  }) {
582
582
  const r = [K.label, i && K.disabled, t].filter(Boolean).join(" ");
583
583
  return /* @__PURE__ */ f("label", { className: r, children: [
@@ -588,7 +588,7 @@ function pe({
588
588
  type: "checkbox",
589
589
  className: K.input,
590
590
  disabled: i,
591
- ...c
591
+ ...a
592
592
  }
593
593
  ),
594
594
  /* @__PURE__ */ n("span", { className: K.indicator, "aria-hidden": "true" }),
@@ -608,7 +608,7 @@ function ur({
608
608
  className: t,
609
609
  disabled: i,
610
610
  ref: s,
611
- ...c
611
+ ...a
612
612
  }) {
613
613
  const r = [M.label, i && M.disabled, t].filter(Boolean).join(" ");
614
614
  return /* @__PURE__ */ f("label", { className: r, children: [
@@ -620,7 +620,7 @@ function ur({
620
620
  role: "switch",
621
621
  className: M.input,
622
622
  disabled: i,
623
- ...c
623
+ ...a
624
624
  }
625
625
  ),
626
626
  /* @__PURE__ */ n("span", { className: M.track, "aria-hidden": "true", children: /* @__PURE__ */ n("span", { className: M.thumb }) }),
@@ -658,13 +658,13 @@ function hr({
658
658
  subtle: t = !1,
659
659
  onDismiss: i,
660
660
  className: s,
661
- children: c,
661
+ children: a,
662
662
  ref: r,
663
663
  ...o
664
664
  }) {
665
- const a = t ? $n : Bn, d = [B.tag, a[e], s].filter(Boolean).join(" ");
665
+ const c = t ? $n : Bn, d = [B.tag, c[e], s].filter(Boolean).join(" ");
666
666
  return /* @__PURE__ */ f("span", { ref: r, className: d, ...o, children: [
667
- c,
667
+ a,
668
668
  i && /* @__PURE__ */ n(
669
669
  "button",
670
670
  {
@@ -712,10 +712,10 @@ function _r({
712
712
  children: t,
713
713
  placement: i = "top"
714
714
  }) {
715
- const [s, c] = _e(!1), r = H(), o = k(void 0), a = g(() => {
715
+ const [s, a] = _e(!1), r = H(), o = k(void 0), c = g(() => {
716
716
  o.current !== void 0 && (window.clearTimeout(o.current), o.current = void 0);
717
- }, []), d = g(() => c(!0), []), l = g(() => c(!1), []);
718
- N(() => a, [a]), N(() => {
717
+ }, []), d = g(() => a(!0), []), l = g(() => a(!1), []);
718
+ N(() => c, [c]), N(() => {
719
719
  if (!s) return;
720
720
  const h = (_) => {
721
721
  _.key === "Escape" && l();
@@ -725,12 +725,12 @@ function _r({
725
725
  const u = ne(t, {
726
726
  "aria-describedby": s ? r : void 0,
727
727
  onMouseEnter: (h) => {
728
- a(), o.current = window.setTimeout(d, En);
728
+ c(), o.current = window.setTimeout(d, En);
729
729
  const _ = t.props.onMouseEnter;
730
730
  _ == null || _(h);
731
731
  },
732
732
  onMouseLeave: (h) => {
733
- a(), l();
733
+ c(), l();
734
734
  const _ = t.props.onMouseLeave;
735
735
  _ == null || _(h);
736
736
  },
@@ -740,7 +740,7 @@ function _r({
740
740
  _ == null || _(h);
741
741
  },
742
742
  onBlur: (h) => {
743
- a(), l();
743
+ c(), l();
744
744
  const _ = t.props.onBlur;
745
745
  _ == null || _(h);
746
746
  }
@@ -758,15 +758,15 @@ function _r({
758
758
  )
759
759
  ] });
760
760
  }
761
- const Mn = "_toolbar_1i848_1", An = "_gap8_1i848_7", Pn = "_gap12_1i848_8", Wn = "_gap16_1i848_9", G = {
761
+ const Mn = "_toolbar_1i848_1", Pn = "_gap8_1i848_7", An = "_gap12_1i848_8", Wn = "_gap16_1i848_9", G = {
762
762
  toolbar: Mn,
763
- gap8: An,
764
- gap12: Pn,
763
+ gap8: Pn,
764
+ gap12: An,
765
765
  gap16: Wn
766
766
  }, Dn = { 8: G.gap8, 12: G.gap12, 16: G.gap16 };
767
- function fr({ gap: e = 8, className: t, children: i, ref: s, ...c }) {
767
+ function fr({ gap: e = 8, className: t, children: i, ref: s, ...a }) {
768
768
  const r = [G.toolbar, Dn[e], t].filter(Boolean).join(" ");
769
- return /* @__PURE__ */ n("div", { ref: s, role: c["aria-label"] != null ? "group" : void 0, className: r, ...c, children: i });
769
+ return /* @__PURE__ */ n("div", { ref: s, role: a["aria-label"] != null ? "group" : void 0, className: r, ...a, children: i });
770
770
  }
771
771
  const Rn = "_trigger_vpa0u_4", Hn = "_menu_vpa0u_16", qn = "_item_vpa0u_37", Vn = "_separator_vpa0u_74", Q = {
772
772
  trigger: Rn,
@@ -775,12 +775,12 @@ const Rn = "_trigger_vpa0u_4", Hn = "_menu_vpa0u_16", qn = "_item_vpa0u_37", Vn
775
775
  separator: Vn
776
776
  }, Fn = 500;
777
777
  function Kn({ popoverRef: e, triggerRef: t, open: i, onEsc: s }) {
778
- const c = k(""), r = k(0), o = g(() => {
778
+ const a = k(""), r = k(0), o = g(() => {
779
779
  const l = e.current;
780
780
  return l ? Array.from(
781
781
  l.querySelectorAll('[data-psi-menu-item]:not([aria-disabled="true"])')
782
782
  ) : [];
783
- }, [e]), a = g(
783
+ }, [e]), c = g(
784
784
  (l) => {
785
785
  if (l) {
786
786
  for (const u of o()) u.tabIndex = u === l ? 0 : -1;
@@ -796,11 +796,11 @@ function Kn({ popoverRef: e, triggerRef: t, open: i, onEsc: s }) {
796
796
  return N(() => {
797
797
  if (!i) return;
798
798
  const l = o();
799
- return a(l[0]), () => {
799
+ return c(l[0]), () => {
800
800
  var u;
801
801
  (u = e.current) != null && u.contains(document.activeElement) && d();
802
802
  };
803
- }, [i, e, o, a, d]), g(
803
+ }, [i, e, o, c, d]), g(
804
804
  (l) => {
805
805
  if (l.key === "Escape") {
806
806
  l.preventDefault(), s();
@@ -811,46 +811,46 @@ function Kn({ popoverRef: e, triggerRef: t, open: i, onEsc: s }) {
811
811
  const h = u.indexOf(document.activeElement);
812
812
  switch (l.key) {
813
813
  case "ArrowDown":
814
- l.preventDefault(), a(u[(h + 1 + u.length) % u.length]);
814
+ l.preventDefault(), c(u[(h + 1 + u.length) % u.length]);
815
815
  return;
816
816
  case "ArrowUp":
817
- l.preventDefault(), a(u[h <= 0 ? u.length - 1 : h - 1]);
817
+ l.preventDefault(), c(u[h <= 0 ? u.length - 1 : h - 1]);
818
818
  return;
819
819
  case "Home":
820
- l.preventDefault(), a(u[0]);
820
+ l.preventDefault(), c(u[0]);
821
821
  return;
822
822
  case "End":
823
- l.preventDefault(), a(u[u.length - 1]);
823
+ l.preventDefault(), c(u[u.length - 1]);
824
824
  return;
825
825
  }
826
826
  if (l.key.length !== 1 || l.metaKey || l.ctrlKey || l.altKey) return;
827
827
  const _ = Date.now();
828
- c.current = _ - r.current > Fn ? l.key.toLowerCase() : c.current + l.key.toLowerCase(), r.current = _;
828
+ a.current = _ - r.current > Fn ? l.key.toLowerCase() : a.current + l.key.toLowerCase(), r.current = _;
829
829
  const m = u.find(
830
- (y) => (y.textContent ?? "").trim().toLowerCase().startsWith(c.current)
830
+ (y) => (y.textContent ?? "").trim().toLowerCase().startsWith(a.current)
831
831
  );
832
- m && (l.preventDefault(), a(m));
832
+ m && (l.preventDefault(), c(m));
833
833
  },
834
- [o, a, s]
834
+ [o, c, s]
835
835
  );
836
836
  }
837
837
  function On() {
838
838
  return typeof CSS < "u" && CSS.supports("anchor-name", "--x");
839
839
  }
840
- function Un({ popoverRef: e, triggerRef: t, open: i, placement: s, anchorName: c }) {
840
+ function Un({ popoverRef: e, triggerRef: t, open: i, placement: s, anchorName: a }) {
841
841
  N(() => {
842
842
  const r = t.current, o = e.current;
843
- !r || !o || (r.style.setProperty("anchor-name", c), o.style.setProperty("position-anchor", c));
844
- }, [e, t, c]), N(() => {
843
+ !r || !o || (r.style.setProperty("anchor-name", a), o.style.setProperty("position-anchor", a));
844
+ }, [e, t, a]), N(() => {
845
845
  if (!i || On()) return;
846
846
  const r = t.current, o = e.current;
847
847
  if (!r || !o) return;
848
- const a = () => {
848
+ const c = () => {
849
849
  const d = r.getBoundingClientRect(), l = s.startsWith("bottom"), u = s.endsWith("end"), h = getComputedStyle(r).direction === "rtl", _ = u !== h;
850
850
  o.style.position = "fixed", o.style.top = l ? `${d.bottom}px` : "", o.style.bottom = l ? "" : `${window.innerHeight - d.top}px`, o.style.left = _ ? "" : `${d.left}px`, o.style.right = _ ? `${window.innerWidth - d.right}px` : "";
851
851
  };
852
- return a(), window.addEventListener("scroll", a, !0), window.addEventListener("resize", a), () => {
853
- window.removeEventListener("scroll", a, !0), window.removeEventListener("resize", a);
852
+ return c(), window.addEventListener("scroll", c, !0), window.addEventListener("resize", c), () => {
853
+ window.removeEventListener("scroll", c, !0), window.removeEventListener("resize", c);
854
854
  };
855
855
  }, [i, s, e, t]);
856
856
  }
@@ -864,10 +864,10 @@ function pr({
864
864
  onClose: t,
865
865
  trigger: i,
866
866
  placement: s = "bottom-start",
867
- children: c,
867
+ children: a,
868
868
  className: r,
869
869
  ref: o,
870
- ...a
870
+ ...c
871
871
  }) {
872
872
  const d = k(null), l = k(null), u = k(!1), h = `--psi-menu-${H().replace(/:/g, "")}`;
873
873
  Un({ popoverRef: d, triggerRef: l, open: e, placement: s, anchorName: h });
@@ -911,7 +911,7 @@ function pr({
911
911
  /* @__PURE__ */ n(
912
912
  "div",
913
913
  {
914
- ...a,
914
+ ...c,
915
915
  ref: _,
916
916
  popover: "auto",
917
917
  role: "menu",
@@ -920,13 +920,13 @@ function pr({
920
920
  className: [Q.menu, r].filter(Boolean).join(" "),
921
921
  onToggle: m,
922
922
  onKeyDown: y,
923
- children: /* @__PURE__ */ n(ve.Provider, { value: { close: z }, children: c })
923
+ children: /* @__PURE__ */ n(ve.Provider, { value: { close: z }, children: a })
924
924
  }
925
925
  )
926
926
  ] });
927
927
  }
928
928
  function vr({ children: e, onSelect: t, variant: i = "neutral", disabled: s = !1 }) {
929
- const { close: c } = S(ve);
929
+ const { close: a } = S(ve);
930
930
  return /* @__PURE__ */ n(
931
931
  "button",
932
932
  {
@@ -938,7 +938,7 @@ function vr({ children: e, onSelect: t, variant: i = "neutral", disabled: s = !1
938
938
  tabIndex: -1,
939
939
  className: Q.item,
940
940
  onClick: () => {
941
- s || (t(), c("item-select"));
941
+ s || (t(), a("item-select"));
942
942
  },
943
943
  children: e
944
944
  }
@@ -970,24 +970,24 @@ function oo({ children: e, className: t }) {
970
970
  function ro(e) {
971
971
  const t = [];
972
972
  let i = !1, s = !1;
973
- const c = (r) => {
974
- A.forEach(r, function o(a) {
975
- if (!U(a)) return;
976
- if (a.type === ce) {
977
- A.forEach(a.props.children, o);
973
+ const a = (r) => {
974
+ P.forEach(r, function o(c) {
975
+ if (!U(c)) return;
976
+ if (c.type === ce) {
977
+ P.forEach(c.props.children, o);
978
978
  return;
979
979
  }
980
- const d = a.props.rowId;
980
+ const d = c.props.rowId;
981
981
  typeof d == "string" ? t.push(d) : s = !0;
982
982
  });
983
983
  };
984
- return A.forEach(e, function r(o) {
984
+ return P.forEach(e, function r(o) {
985
985
  if (U(o)) {
986
986
  if (o.type === ce) {
987
- A.forEach(o.props.children, r);
987
+ P.forEach(o.props.children, r);
988
988
  return;
989
989
  }
990
- o.type === oo && (i = !0, c(o.props.children));
990
+ o.type === oo && (i = !0, a(o.props.children));
991
991
  }
992
992
  }), { ids: t, sawBody: i, hasRowWithoutId: s };
993
993
  }
@@ -996,39 +996,39 @@ function gr({
996
996
  stickyHeader: t,
997
997
  sortable: i = !1,
998
998
  sort: s = null,
999
- onSortChange: c,
999
+ onSortChange: a,
1000
1000
  selectable: r = !1,
1001
1001
  selected: o = be,
1002
- onSelectionChange: a,
1002
+ onSelectionChange: c,
1003
1003
  children: d,
1004
1004
  className: l,
1005
1005
  ref: u
1006
1006
  }) {
1007
1007
  const h = [x.table, t && x.sticky, l].filter(Boolean).join(" "), { ids: _, sawBody: m, hasRowWithoutId: y } = oe(() => ro(d), [d]);
1008
- return process.env.NODE_ENV !== "production" && (i && !c && console.warn("Psi Table: `sortable` is set without `onSortChange`; sorting will not respond."), r && !a && console.warn("Psi Table: `selectable` is set without `onSelectionChange`; selection will not respond."), r && !m && console.warn("Psi Table: `selectable` is set but no `TableBody` is visible to `Table`. Wrapping `TableBody` in a component other than a Fragment hides its rows, and select-all will clear the selection instead of filling it."), r && y && console.warn("Psi Table: `selectable` is set but a row in `TableBody` has no `rowId`. That row will render without a selection checkbox and select-all will not include it.")), /* @__PURE__ */ n(q.Provider, { value: { size: e, sortable: i, sort: s, onSortChange: c, selectable: r, selected: o, onSelectionChange: a }, children: /* @__PURE__ */ n(ge.Provider, { value: _, children: /* @__PURE__ */ n("table", { ref: u, className: h, "data-size": e, children: d }) }) });
1008
+ return process.env.NODE_ENV !== "production" && (i && !a && console.warn("Psi Table: `sortable` is set without `onSortChange`; sorting will not respond."), r && !c && console.warn("Psi Table: `selectable` is set without `onSelectionChange`; selection will not respond."), r && !m && console.warn("Psi Table: `selectable` is set but no `TableBody` is visible to `Table`. Wrapping `TableBody` in a component other than a Fragment hides its rows, and select-all will clear the selection instead of filling it."), r && y && console.warn("Psi Table: `selectable` is set but a row in `TableBody` has no `rowId`. That row will render without a selection checkbox and select-all will not include it.")), /* @__PURE__ */ n(q.Provider, { value: { size: e, sortable: i, sort: s, onSortChange: a, selectable: r, selected: o, onSelectionChange: c }, children: /* @__PURE__ */ n(ge.Provider, { value: _, children: /* @__PURE__ */ n("table", { ref: u, className: h, "data-size": e, children: d }) }) });
1009
1009
  }
1010
1010
  function io({ rowId: e, label: t }) {
1011
- const { selected: i, onSelectionChange: s } = S(q), c = () => {
1011
+ const { selected: i, onSelectionChange: s } = S(q), a = () => {
1012
1012
  const r = new Set(i);
1013
1013
  r.has(e) ? r.delete(e) : r.add(e), s == null || s(r);
1014
1014
  };
1015
- return /* @__PURE__ */ n("td", { className: `${x.cell} ${x.selectCell}`, children: /* @__PURE__ */ n(pe, { "aria-label": t, checked: i.has(e), onChange: c }) });
1015
+ return /* @__PURE__ */ n("td", { className: `${x.cell} ${x.selectCell}`, children: /* @__PURE__ */ n(pe, { "aria-label": t, checked: i.has(e), onChange: a }) });
1016
1016
  }
1017
1017
  function so() {
1018
- const { selected: e, onSelectionChange: t } = S(q), i = S(ge), s = k(null), c = i.length > 0 && i.every((a) => e.has(a)), r = i.some((a) => e.has(a));
1018
+ const { selected: e, onSelectionChange: t } = S(q), i = S(ge), s = k(null), a = i.length > 0 && i.every((c) => e.has(c)), r = i.some((c) => e.has(c));
1019
1019
  N(() => {
1020
- s.current && (s.current.indeterminate = r && !c);
1021
- }, [r, c]);
1020
+ s.current && (s.current.indeterminate = r && !a);
1021
+ }, [r, a]);
1022
1022
  const o = () => {
1023
- const a = new Set(e);
1024
- c ? i.forEach((d) => a.delete(d)) : i.forEach((d) => a.add(d)), t == null || t(a);
1023
+ const c = new Set(e);
1024
+ a ? i.forEach((d) => c.delete(d)) : i.forEach((d) => c.add(d)), t == null || t(c);
1025
1025
  };
1026
- return /* @__PURE__ */ n("th", { scope: "col", className: `${x.headerCell} ${x.selectCell}`, children: /* @__PURE__ */ n(pe, { ref: s, "aria-label": "Select all rows", checked: c, onChange: o }) });
1026
+ return /* @__PURE__ */ n("th", { scope: "col", className: `${x.headerCell} ${x.selectCell}`, children: /* @__PURE__ */ n(pe, { ref: s, "aria-label": "Select all rows", checked: a, onChange: o }) });
1027
1027
  }
1028
1028
  function mr({ children: e, className: t }) {
1029
1029
  const { selectable: i } = S(q);
1030
1030
  let s = !1;
1031
- const c = i ? A.map(e, (r) => {
1031
+ const a = i ? P.map(e, (r) => {
1032
1032
  if (s || !U(r)) return r;
1033
1033
  s = !0;
1034
1034
  const o = r;
@@ -1041,10 +1041,10 @@ function mr({ children: e, className: t }) {
1041
1041
  ] })
1042
1042
  );
1043
1043
  }) : e;
1044
- return /* @__PURE__ */ n("thead", { className: [x.head, t].filter(Boolean).join(" "), children: c });
1044
+ return /* @__PURE__ */ n("thead", { className: [x.head, t].filter(Boolean).join(" "), children: a });
1045
1045
  }
1046
1046
  function yr({ rowId: e, selectLabel: t, children: i, className: s }) {
1047
- const { selectable: c, selected: r } = S(q), o = e !== void 0 && r.has(e);
1047
+ const { selectable: a, selected: r } = S(q), o = e !== void 0 && r.has(e);
1048
1048
  return /* @__PURE__ */ f(
1049
1049
  "tr",
1050
1050
  {
@@ -1052,14 +1052,14 @@ function yr({ rowId: e, selectLabel: t, children: i, className: s }) {
1052
1052
  "data-row-id": e,
1053
1053
  "data-selected": o || void 0,
1054
1054
  children: [
1055
- c && e !== void 0 && /* @__PURE__ */ n(io, { rowId: e, label: t ?? `Select row ${e}` }),
1055
+ a && e !== void 0 && /* @__PURE__ */ n(io, { rowId: e, label: t ?? `Select row ${e}` }),
1056
1056
  i
1057
1057
  ]
1058
1058
  }
1059
1059
  );
1060
1060
  }
1061
1061
  function wr({ sortKey: e, numeric: t, children: i, className: s }) {
1062
- const { sortable: c, sort: r, onSortChange: o } = S(q), a = c && e !== void 0, d = a && (r == null ? void 0 : r.key) === e, l = a ? d ? r.direction === "asc" ? "ascending" : "descending" : "none" : void 0, u = () => {
1062
+ const { sortable: a, sort: r, onSortChange: o } = S(q), c = a && e !== void 0, d = c && (r == null ? void 0 : r.key) === e, l = c ? d ? r.direction === "asc" ? "ascending" : "descending" : "none" : void 0, u = () => {
1063
1063
  if (!e) return;
1064
1064
  const h = d && r.direction === "asc" ? "desc" : "asc";
1065
1065
  o == null || o({ key: e, direction: h });
@@ -1071,7 +1071,7 @@ function wr({ sortKey: e, numeric: t, children: i, className: s }) {
1071
1071
  className: [x.headerCell, s].filter(Boolean).join(" "),
1072
1072
  "data-numeric": t || void 0,
1073
1073
  "aria-sort": l,
1074
- children: a ? /* @__PURE__ */ f("button", { type: "button", className: x.sortButton, onClick: u, children: [
1074
+ children: c ? /* @__PURE__ */ f("button", { type: "button", className: x.sortButton, onClick: u, children: [
1075
1075
  i,
1076
1076
  /* @__PURE__ */ n("span", { className: x.sortIndicator, "aria-hidden": "true", children: d ? r.direction === "asc" ? "↑" : "↓" : "" })
1077
1077
  ] }) : i
@@ -1089,52 +1089,56 @@ function O(e, t) {
1089
1089
  return Array.from({ length: t - e + 1 }, (i, s) => e + s);
1090
1090
  }
1091
1091
  function lo(e, t, i) {
1092
- const s = Math.max(0, i), c = s * 2 + 1, r = c + 4;
1092
+ const s = Math.max(0, i), a = s * 2 + 1, r = a + 4;
1093
1093
  if (t <= r)
1094
1094
  return O(1, t);
1095
- const o = Math.max(e - s, 1), a = Math.min(e + s, t), d = o > 3, l = a < t - 2;
1095
+ const o = Math.max(e - s, 1), c = Math.min(e + s, t), d = o > 3, l = c < t - 2;
1096
1096
  if (!d && l) {
1097
- const u = Math.max(a, c);
1097
+ const u = Math.max(c, a);
1098
1098
  return [...O(1, u), "ellipsis", t];
1099
1099
  }
1100
1100
  if (d && !l) {
1101
- const u = Math.min(o, t - c + 1);
1101
+ const u = Math.min(o, t - a + 1);
1102
1102
  return [1, "ellipsis", ...O(u, t)];
1103
1103
  }
1104
- return d && l ? [1, "ellipsis", ...O(o, a), "ellipsis", t] : O(1, t);
1104
+ return d && l ? [1, "ellipsis", ...O(o, c), "ellipsis", t] : O(1, t);
1105
1105
  }
1106
1106
  function xr({
1107
1107
  page: e,
1108
1108
  pageCount: t,
1109
1109
  onPageChange: i,
1110
1110
  siblingCount: s = 1,
1111
- "aria-label": c = "Pagination",
1111
+ "aria-label": a = "Pagination",
1112
1112
  className: r
1113
1113
  }) {
1114
- const o = lo(e, t, s);
1115
- return /* @__PURE__ */ f("nav", { "aria-label": c, className: [ue.nav, r].filter(Boolean).join(" "), children: [
1114
+ const o = t >= 1, c = Number.isFinite(e) ? e : 1, d = o ? Math.min(Math.max(c, 1), t) : 0;
1115
+ process.env.NODE_ENV !== "production" && (!o || e !== d) && console.warn(
1116
+ `Psi Pagination: \`page\` ${e} is out of range for \`pageCount\` ${t}; rendering page ${o ? d : "none"}. Clamp \`page\` when \`pageCount\` shrinks — e.g. after filtering.`
1117
+ );
1118
+ const l = o ? lo(d, t, s) : [];
1119
+ return /* @__PURE__ */ f("nav", { "aria-label": a, className: [ue.nav, r].filter(Boolean).join(" "), children: [
1116
1120
  /* @__PURE__ */ n(
1117
1121
  J,
1118
1122
  {
1119
1123
  "aria-label": "Previous page",
1120
1124
  size: 32,
1121
1125
  variant: "ghost",
1122
- disabled: e <= 1,
1123
- onClick: () => i(e - 1),
1126
+ disabled: !o || d <= 1,
1127
+ onClick: () => i(d - 1),
1124
1128
  children: /* @__PURE__ */ n("svg", { "aria-hidden": "true", viewBox: "0 0 16 16", width: "16", height: "16", children: /* @__PURE__ */ n("path", { d: "M10 3 5 8l5 5", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }) })
1125
1129
  }
1126
1130
  ),
1127
- o.map(
1128
- (a, d) => a === "ellipsis" ? /* @__PURE__ */ n("span", { className: ue.ellipsis, "aria-hidden": "true", children: "…" }, `e${d}`) : /* @__PURE__ */ n(
1131
+ l.map(
1132
+ (u, h) => u === "ellipsis" ? /* @__PURE__ */ n("span", { className: ue.ellipsis, "aria-hidden": "true", children: "…" }, `e${h}`) : /* @__PURE__ */ n(
1129
1133
  He,
1130
1134
  {
1131
1135
  size: 32,
1132
- variant: a === e ? "accent-subtle" : "ghost",
1133
- "aria-current": a === e ? "page" : void 0,
1134
- onClick: () => i(a),
1135
- children: String(a)
1136
+ variant: u === d ? "accent-subtle" : "ghost",
1137
+ "aria-current": u === d ? "page" : void 0,
1138
+ onClick: () => i(u),
1139
+ children: String(u)
1136
1140
  },
1137
- a
1141
+ u
1138
1142
  )
1139
1143
  ),
1140
1144
  /* @__PURE__ */ n(
@@ -1143,8 +1147,8 @@ function xr({
1143
1147
  "aria-label": "Next page",
1144
1148
  size: 32,
1145
1149
  variant: "ghost",
1146
- disabled: e >= t,
1147
- onClick: () => i(e + 1),
1150
+ disabled: !o || d >= t,
1151
+ onClick: () => i(d + 1),
1148
1152
  children: /* @__PURE__ */ n("svg", { "aria-hidden": "true", viewBox: "0 0 16 16", width: "16", height: "16", children: /* @__PURE__ */ n("path", { d: "m6 3 5 5-5 5", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }) })
1149
1153
  }
1150
1154
  )
@@ -1183,14 +1187,14 @@ function me({
1183
1187
  children: t,
1184
1188
  action: i,
1185
1189
  onDismiss: s,
1186
- className: c,
1190
+ className: a,
1187
1191
  ref: r
1188
1192
  }) {
1189
- const o = ko[e], a = xo[e], d = [$.toast, c].filter(Boolean).join(" ");
1193
+ const o = ko[e], c = xo[e], d = [$.toast, a].filter(Boolean).join(" ");
1190
1194
  return /* @__PURE__ */ f("div", { ref: r, className: d, "data-variant": e, "data-psi-toast": !0, children: [
1191
1195
  /* @__PURE__ */ n("span", { className: [$.icon, Co[e]].join(" "), children: /* @__PURE__ */ n(o, { size: 20, "aria-hidden": "true" }) }),
1192
1196
  /* @__PURE__ */ f("div", { className: $.body, children: [
1193
- a && /* @__PURE__ */ n("span", { className: $.srOnly, children: a }),
1197
+ c && /* @__PURE__ */ n("span", { className: $.srOnly, children: c }),
1194
1198
  t
1195
1199
  ] }),
1196
1200
  i != null && /* @__PURE__ */ n("div", { className: $.action, children: i }),
@@ -1217,10 +1221,10 @@ function $o({
1217
1221
  "aria-label": t = "Notifications",
1218
1222
  children: i,
1219
1223
  className: s,
1220
- ref: c
1224
+ ref: a
1221
1225
  }) {
1222
1226
  const r = k(null), o = (u) => {
1223
- r.current = u, typeof c == "function" ? c(u) : c && (c.current = u);
1227
+ r.current = u, typeof a == "function" ? a(u) : a && (a.current = u);
1224
1228
  };
1225
1229
  N(() => {
1226
1230
  var h;
@@ -1231,7 +1235,7 @@ function $o({
1231
1235
  (_ = u.hidePopover) == null || _.call(u);
1232
1236
  };
1233
1237
  }, []);
1234
- const a = A.toArray(i), d = a.filter(he), l = a.filter((u) => !he(u));
1238
+ const c = P.toArray(i), d = c.filter(he), l = c.filter((u) => !he(u));
1235
1239
  return /* @__PURE__ */ f(
1236
1240
  "div",
1237
1241
  {
@@ -1262,15 +1266,15 @@ function Br({
1262
1266
  duration: t = 5e3,
1263
1267
  actionDuration: i = 1e4,
1264
1268
  placement: s = "bottom-end",
1265
- children: c
1269
+ children: a
1266
1270
  }) {
1267
- const [r, o] = _e([]), a = k(/* @__PURE__ */ new Map()), d = k(!1), l = k(0), u = H(), h = g((p) => {
1268
- const v = a.current.get(p);
1269
- (v == null ? void 0 : v.handle) !== void 0 && clearTimeout(v.handle), a.current.delete(p), o((T) => T.filter((V) => V.id !== p));
1271
+ const [r, o] = _e([]), c = k(/* @__PURE__ */ new Map()), d = k(!1), l = k(0), u = H(), h = g((p) => {
1272
+ const v = c.current.get(p);
1273
+ (v == null ? void 0 : v.handle) !== void 0 && clearTimeout(v.handle), c.current.delete(p), o((T) => T.filter((V) => V.id !== p));
1270
1274
  }, []), _ = g(
1271
1275
  (p, v) => {
1272
1276
  const T = d.current ? { handle: void 0, remaining: v, startedAt: void 0 } : { handle: setTimeout(() => h(p), v), remaining: v, startedAt: Date.now() };
1273
- a.current.set(p, T);
1277
+ c.current.set(p, T);
1274
1278
  },
1275
1279
  [h]
1276
1280
  ), m = g(
@@ -1279,25 +1283,25 @@ function Br({
1279
1283
  return o((Ce) => {
1280
1284
  const te = [...Ce, { id: V, variant: p, message: v, action: T, duration: se }];
1281
1285
  for (; te.length > e; ) {
1282
- const ae = te.shift(), Y = a.current.get(ae.id);
1283
- (Y == null ? void 0 : Y.handle) !== void 0 && clearTimeout(Y.handle), a.current.delete(ae.id);
1286
+ const ae = te.shift(), Y = c.current.get(ae.id);
1287
+ (Y == null ? void 0 : Y.handle) !== void 0 && clearTimeout(Y.handle), c.current.delete(ae.id);
1284
1288
  }
1285
1289
  return te;
1286
1290
  }), _(V, se), V;
1287
1291
  },
1288
1292
  [i, _, t, u, e]
1289
1293
  ), y = g(() => {
1290
- for (const p of a.current.values())
1294
+ for (const p of c.current.values())
1291
1295
  p.handle !== void 0 && clearTimeout(p.handle);
1292
- a.current.clear(), o([]);
1296
+ c.current.clear(), o([]);
1293
1297
  }, []), z = g(() => {
1294
1298
  if (!d.current) {
1295
1299
  d.current = !0;
1296
- for (const [p, v] of a.current) {
1300
+ for (const [p, v] of c.current) {
1297
1301
  if (v.handle === void 0) continue;
1298
1302
  clearTimeout(v.handle);
1299
1303
  const T = v.startedAt === void 0 ? 0 : Date.now() - v.startedAt;
1300
- a.current.set(p, {
1304
+ c.current.set(p, {
1301
1305
  handle: void 0,
1302
1306
  remaining: Math.max(0, v.remaining - T),
1303
1307
  startedAt: void 0
@@ -1307,8 +1311,8 @@ function Br({
1307
1311
  }, []), C = g(() => {
1308
1312
  if (d.current) {
1309
1313
  d.current = !1;
1310
- for (const [p, v] of a.current)
1311
- v.handle === void 0 && a.current.set(p, {
1314
+ for (const [p, v] of c.current)
1315
+ v.handle === void 0 && c.current.set(p, {
1312
1316
  handle: setTimeout(() => h(p), v.remaining),
1313
1317
  remaining: v.remaining,
1314
1318
  startedAt: Date.now()
@@ -1316,7 +1320,7 @@ function Br({
1316
1320
  }
1317
1321
  }, [h]);
1318
1322
  N(() => {
1319
- const p = a.current;
1323
+ const p = c.current;
1320
1324
  return () => {
1321
1325
  for (const v of p.values())
1322
1326
  v.handle !== void 0 && clearTimeout(v.handle);
@@ -1328,7 +1332,7 @@ function Br({
1328
1332
  [m, h, y]
1329
1333
  );
1330
1334
  return /* @__PURE__ */ f(ye.Provider, { value: b, children: [
1331
- c,
1335
+ a,
1332
1336
  /* @__PURE__ */ n(
1333
1337
  "div",
1334
1338
  {
@@ -1360,18 +1364,18 @@ function $r({
1360
1364
  onValueChange: t,
1361
1365
  orientation: i = "horizontal",
1362
1366
  children: s,
1363
- className: c,
1367
+ className: a,
1364
1368
  ref: r
1365
1369
  }) {
1366
- const o = H(), a = oe(
1370
+ const o = H(), c = oe(
1367
1371
  () => ({ value: e, onValueChange: t, orientation: i, idPrefix: o }),
1368
1372
  [e, t, i, o]
1369
1373
  );
1370
- return /* @__PURE__ */ n(we.Provider, { value: a, children: /* @__PURE__ */ n(
1374
+ return /* @__PURE__ */ n(we.Provider, { value: c, children: /* @__PURE__ */ n(
1371
1375
  "div",
1372
1376
  {
1373
1377
  ref: r,
1374
- className: [ee.tabs, c].filter(Boolean).join(" "),
1378
+ className: [ee.tabs, a].filter(Boolean).join(" "),
1375
1379
  "data-orientation": i,
1376
1380
  children: s
1377
1381
  }
@@ -1389,10 +1393,10 @@ function zo({ listRef: e, orientation: t, onValueChange: i }) {
1389
1393
  return r ? Array.from(
1390
1394
  r.querySelectorAll('[data-psi-tab]:not([aria-disabled="true"])')
1391
1395
  ) : [];
1392
- }, [e]), c = g(
1396
+ }, [e]), a = g(
1393
1397
  (r) => {
1394
1398
  if (!r) return;
1395
- for (const a of s()) a.tabIndex = a === r ? 0 : -1;
1399
+ for (const c of s()) c.tabIndex = c === r ? 0 : -1;
1396
1400
  r.focus();
1397
1401
  const o = r.dataset.value;
1398
1402
  o != null && i(o);
@@ -1403,48 +1407,48 @@ function zo({ listRef: e, orientation: t, onValueChange: i }) {
1403
1407
  (r) => {
1404
1408
  const o = s();
1405
1409
  if (o.length === 0) return;
1406
- const [a, d] = t === "vertical" ? ["ArrowUp", "ArrowDown"] : ["ArrowLeft", "ArrowRight"], l = o.indexOf(document.activeElement);
1410
+ const [c, d] = t === "vertical" ? ["ArrowUp", "ArrowDown"] : ["ArrowLeft", "ArrowRight"], l = o.indexOf(document.activeElement);
1407
1411
  switch (r.key) {
1408
1412
  case d:
1409
- r.preventDefault(), c(o[(l + 1 + o.length) % o.length]);
1413
+ r.preventDefault(), a(o[(l + 1 + o.length) % o.length]);
1410
1414
  break;
1411
- case a:
1412
- r.preventDefault(), c(o[(l - 1 + o.length) % o.length]);
1415
+ case c:
1416
+ r.preventDefault(), a(o[(l - 1 + o.length) % o.length]);
1413
1417
  break;
1414
1418
  case "Home":
1415
- r.preventDefault(), c(o[0]);
1419
+ r.preventDefault(), a(o[0]);
1416
1420
  break;
1417
1421
  case "End":
1418
- r.preventDefault(), c(o[o.length - 1]);
1422
+ r.preventDefault(), a(o[o.length - 1]);
1419
1423
  break;
1420
1424
  }
1421
1425
  },
1422
- [c, s, t]
1426
+ [a, s, t]
1423
1427
  );
1424
1428
  }
1425
1429
  function Nr({ "aria-label": e, children: t, className: i, ref: s }) {
1426
- const { orientation: c, onValueChange: r } = ie("TabList"), o = k(null), a = (l) => {
1430
+ const { orientation: a, onValueChange: r } = ie("TabList"), o = k(null), c = (l) => {
1427
1431
  o.current = l, typeof s == "function" ? s(l) : s && (s.current = l);
1428
- }, d = zo({ listRef: o, orientation: c, onValueChange: r });
1432
+ }, d = zo({ listRef: o, orientation: a, onValueChange: r });
1429
1433
  return /* @__PURE__ */ n(
1430
1434
  "div",
1431
1435
  {
1432
- ref: a,
1436
+ ref: c,
1433
1437
  role: "tablist",
1434
1438
  "aria-label": e,
1435
- "aria-orientation": c,
1439
+ "aria-orientation": a,
1436
1440
  className: [ee.list, i].filter(Boolean).join(" "),
1437
1441
  onKeyDown: d,
1438
1442
  children: t
1439
1443
  }
1440
1444
  );
1441
1445
  }
1442
- function Lr({ value: e, children: t, disabled: i = !1, className: s, ref: c }) {
1446
+ function Lr({ value: e, children: t, disabled: i = !1, className: s, ref: a }) {
1443
1447
  const r = ie("Tab"), o = r.value === e;
1444
1448
  return /* @__PURE__ */ n(
1445
1449
  "button",
1446
1450
  {
1447
- ref: c,
1451
+ ref: a,
1448
1452
  type: "button",
1449
1453
  role: "tab",
1450
1454
  id: ke(r.idPrefix, e),
@@ -1463,14 +1467,14 @@ function Lr({ value: e, children: t, disabled: i = !1, className: s, ref: c }) {
1463
1467
  );
1464
1468
  }
1465
1469
  function Sr({ value: e, children: t, className: i, ref: s }) {
1466
- const c = ie("TabPanel"), r = c.value === e;
1470
+ const a = ie("TabPanel"), r = a.value === e;
1467
1471
  return /* @__PURE__ */ n(
1468
1472
  "div",
1469
1473
  {
1470
1474
  ref: s,
1471
1475
  role: "tabpanel",
1472
- id: xe(c.idPrefix, e),
1473
- "aria-labelledby": ke(c.idPrefix, e),
1476
+ id: xe(a.idPrefix, e),
1477
+ "aria-labelledby": ke(a.idPrefix, e),
1474
1478
  hidden: !r,
1475
1479
  tabIndex: 0,
1476
1480
  className: [ee.panel, i].filter(Boolean).join(" "),
@@ -1483,8 +1487,8 @@ export {
1483
1487
  He as Button,
1484
1488
  Eo as Card,
1485
1489
  pe as Checkbox,
1486
- Po as DescriptionItem,
1487
- Ao as DescriptionList,
1490
+ Ao as DescriptionItem,
1491
+ Po as DescriptionList,
1488
1492
  cr as Dialog,
1489
1493
  Ro as Field,
1490
1494
  Nt as IconAlertCircle,
@@ -1034,7 +1034,7 @@
1034
1034
  "type": "((sort: TableSortState) => void)",
1035
1035
  "required": false,
1036
1036
  "default": null,
1037
- "description": "Called when a header's sort control is activated. Optional in the type\nbecause `sortable` may be false; a discriminated union expressing the real\ncontract does not survive docgen's flat prop extraction, which would strip\nthese props from the manifest entirely (D62)."
1037
+ "description": "Called with the **next** sort state, already toggled — store it as given\nand do not toggle again. A fresh column arrives `\"asc\"`; an active `\"asc\"`\ncolumn emits `\"desc\"`; an active `\"desc\"` column emits `\"asc\"`.\n\nOptional in the type because `sortable` may be false; a discriminated union\nexpressing the real contract does not survive docgen's flat prop\nextraction, which would strip these props from the manifest entirely (D62)."
1038
1038
  },
1039
1039
  {
1040
1040
  "name": "selectable",
@@ -1230,7 +1230,7 @@
1230
1230
  "type": "number",
1231
1231
  "required": true,
1232
1232
  "default": null,
1233
- "description": "Current page, 1-based."
1233
+ "description": "Current page, 1-based. A value outside `[1, pageCount]` (including\nnon-finite values like `NaN`) is clamped for rendering and logs a\n`console.warn` in development. `pageCount < 1` renders no page buttons."
1234
1234
  },
1235
1235
  {
1236
1236
  "name": "pageCount",
@@ -157,6 +157,10 @@
157
157
  "numeric": true
158
158
  },
159
159
  "content": "amount-label"
160
+ },
161
+ {
162
+ "component": "TableHeaderCell",
163
+ "content": "actions-label"
160
164
  }
161
165
  ]
162
166
  }
@@ -189,6 +193,10 @@
189
193
  "numeric": true
190
194
  },
191
195
  "content": "amount-cell"
196
+ },
197
+ {
198
+ "component": "TableCell",
199
+ "content": "actions-cell"
192
200
  }
193
201
  ]
194
202
  }
@@ -204,13 +212,15 @@
204
212
  "date-label": "Date",
205
213
  "payee-label": "Payee",
206
214
  "amount-label": "Amount",
215
+ "actions-label": "[a visible \"Actions\" header — the column needs an accessible name, so never an empty th]",
207
216
  "date-cell": "[one row per record]",
208
- "payee-cell": "[put the row-actions menu in the last cell]",
209
- "amount-cell": "[numeric columns align right and use tabular figures]"
217
+ "payee-cell": "[the record's payee]",
218
+ "amount-cell": "[numeric columns align right and use tabular figures]",
219
+ "actions-cell": "[the row-actions menu — compose it from the row-actions pattern]"
210
220
  },
211
221
  "gaps": [],
212
222
  "blocked": false,
213
- "preset": "<Table\n selectable={true}\n sortable={true}\n>\n <TableHead>\n <TableRow>\n <TableHeaderCell sortKey=\"date\">Date</TableHeaderCell>\n <TableHeaderCell sortKey=\"payee\">Payee</TableHeaderCell>\n <TableHeaderCell numeric={true} sortKey=\"amount\">Amount</TableHeaderCell>\n </TableRow>\n </TableHead>\n <TableBody>\n <TableRow\n rowId=\"row-id\"\n >\n <TableCell>[one row per record]</TableCell>\n <TableCell>[put the row-actions menu in the last cell]</TableCell>\n <TableCell numeric={true}>[numeric columns align right and use tabular figures]</TableCell>\n </TableRow>\n </TableBody>\n</Table>\n"
223
+ "preset": "<Table\n selectable={true}\n sortable={true}\n>\n <TableHead>\n <TableRow>\n <TableHeaderCell sortKey=\"date\">Date</TableHeaderCell>\n <TableHeaderCell sortKey=\"payee\">Payee</TableHeaderCell>\n <TableHeaderCell numeric={true} sortKey=\"amount\">Amount</TableHeaderCell>\n <TableHeaderCell>[a visible \"Actions\" header — the column needs an accessible name, so never an empty th]</TableHeaderCell>\n </TableRow>\n </TableHead>\n <TableBody>\n <TableRow\n rowId=\"row-id\"\n >\n <TableCell>[one row per record]</TableCell>\n <TableCell>[the record's payee]</TableCell>\n <TableCell numeric={true}>[numeric columns align right and use tabular figures]</TableCell>\n <TableCell>[the row-actions menu — compose it from the row-actions pattern]</TableCell>\n </TableRow>\n </TableBody>\n</Table>\n"
214
224
  },
215
225
  {
216
226
  "id": "date-range-filter",
@@ -7,7 +7,7 @@ Numbered pager with ellipsis truncation (D63). Standalone rather than a Table fa
7
7
 
8
8
  | Prop | Type | Default | Required | Description |
9
9
  |---|---|---|---|---|
10
- | `page` | `number` | — | yes | Current page, 1-based. |
10
+ | `page` | `number` | — | yes | Current page, 1-based. A value outside `[1, pageCount]` (including non-finite values like `NaN`) is clamped for rendering and logs a `console.warn` in development. `pageCount < 1` renders no page buttons. |
11
11
  | `pageCount` | `number` | — | yes | Total number of pages. |
12
12
  | `onPageChange` | `(page: number) => void` | — | yes | Called with the requested page. Required — unlike Table's `onSortChange`/ `onSelectionChange`, which are optional because `sortable`/`selectable` gate whether they're meaningful, `Pagination` has no such gating boolean: the prop is unconditionally meaningful, so a `Pagination` without it is a dead control (final review finding, D62). |
13
13
  | `siblingCount` | `number` | 1 | no | Pages shown either side of the current one before truncating. |
package/docs/Table.md CHANGED
@@ -11,7 +11,7 @@ Data table on native table semantics. Holds no state: sorting, selection and pag
11
11
  | `stickyHeader` | `boolean` | — | no | Pins the header while the body scrolls. |
12
12
  | `sortable` | `boolean` | false | no | Enables the sort affordance on header cells that declare a `sortKey`. |
13
13
  | `sort` | `TableSortState \| null` | — | no | Controlled sort state; `null` when nothing is sorted. |
14
- | `onSortChange` | `((sort: TableSortState) => void)` | — | no | Called when a header's sort control is activated. Optional in the type because `sortable` may be false; a discriminated union expressing the real contract does not survive docgen's flat prop extraction, which would strip these props from the manifest entirely (D62). |
14
+ | `onSortChange` | `((sort: TableSortState) => void)` | — | no | Called with the **next** sort state, already toggled — store it as given and do not toggle again. A fresh column arrives `"asc"`; an active `"asc"` column emits `"desc"`; an active `"desc"` column emits `"asc"`. Optional in the type because `sortable` may be false; a discriminated union expressing the real contract does not survive docgen's flat prop extraction, which would strip these props from the manifest entirely (D62). |
15
15
  | `selectable` | `boolean` | false | no | Renders the row-selection checkbox column. |
16
16
  | `selected` | `ReadonlySet<string>` | new Set<string>() | no | Controlled selection, keyed by each `TableRow`'s `rowId`. |
17
17
  | `onSelectionChange` | `((selected: ReadonlySet<string>) => void)` | — | no | Called with the next selection. See `onSortChange` on why it is optional. |
package/llms.txt CHANGED
@@ -45,12 +45,12 @@
45
45
  - Full icon set: IconPlus, IconMinus, IconCheck, IconClose, IconChevronDown, IconChevronRight, IconSearch, IconSettings, IconUser, IconEye, IconEyeOff, IconEdit, IconCopy, IconTrash, IconLoader, IconExternalLink, plus D36 additions — arrows IconArrowDown and IconArrowUpRight, and social glyphs IconLinkedIn, IconGitHub, IconX, IconInstagram — plus the D64 status glyphs IconInfo, IconAlertTriangle and IconAlertCircle, and the D70 IconMoreHorizontal — the ellipsis glyph for a row-actions trigger.
46
46
 
47
47
  ## Table family (6)
48
- - `<Table>` renders `<table>` and is **controlled-only** — it holds no state (D62). Sorting is `sort={{key, direction}}` + `onSortChange`; selection is `selected` (a `ReadonlySet<string>` keyed by each `<TableRow rowId>`) + `onSelectionChange`. `sortable` and `selectable` only enable the affordances. `size` is `32 | 40 | 48` px (default 40) and `stickyHeader` pins the header. It does no filtering, sorting or slicing of rows — the consumer does that.
48
+ - `<Table>` renders `<table>` and is **controlled-only** — it holds no state (D62). Sorting is `sort={{key, direction}}` + `onSortChange`, which is called with the **next** state, already toggled — store it as given (a fresh column arrives `asc`; an active `asc` emits `desc`); selection is `selected` (a `ReadonlySet<string>` keyed by each `<TableRow rowId>`) + `onSelectionChange`. `sortable` and `selectable` only enable the affordances. `size` is `32 | 40 | 48` px (default 40) and `stickyHeader` pins the header. It does no filtering, sorting or slicing of rows — the consumer does that.
49
49
  - `<TableHead>`, `<TableBody>`, `<TableRow>` render `<thead>`, `<tbody>`, `<tr>`. `<TableHeaderCell>` renders `<th scope="col">`, taking `sortKey` to become sortable and putting `aria-sort` on the `<th>`. `<TableCell>` renders `<td>`. Both cells take `numeric`, which right-aligns **and** renders tabular figures.
50
50
  - Selecting rows: give each `<TableRow>` a `rowId` and a `selectLabel` — the row checkbox has no visible label, so `selectLabel` is its accessible name. `<TableHead>` grows the select-all checkbox automatically; do not add one. Consult dist/patterns.json for the `data-table` pattern.
51
51
 
52
52
  ## Pagination
53
- - `<Pagination page={n} pageCount={total} onPageChange={...}>`: a numbered pager with ellipsis truncation (`‹ 1 … 4 5 6 … 13 ›`), built from Button/IconButton (D63). `siblingCount` (default 1) sets how many pages flank the current one before truncating; `aria-current="page"` marks the active page and the ellipsis is inert.
53
+ - `<Pagination page={n} pageCount={total} onPageChange={...}>`: a numbered pager with ellipsis truncation (`‹ 1 … 4 5 6 … 13 ›`), built from Button/IconButton (D63). `siblingCount` (default 1) sets how many pages flank the current one before truncating; `aria-current="page"` marks the active page and the ellipsis is inert. `page` outside `[1, pageCount]` (including non-finite values) is clamped for rendering and warns in development (D78).
54
54
  - It is **standalone, not a Table family member** and has no connection to `Table` — `Table` has no `page` prop. The `table-pagination` pattern composes it as a `Toolbar` sibling of a page-size `Select`; the consumer owns page state and slices the rows.
55
55
 
56
56
  ## Dialog, and drawers (there is no Drawer component)
@@ -71,10 +71,16 @@
71
71
  - **Every panel renders; unselected ones get `hidden`.** So `aria-controls` always resolves and panel DOM state survives a switch. If a panel is expensive, render less *inside* it (`{selected ? <Heavy/> : null}`) rather than omitting the `TabPanel` — omitting it leaves the tab's `aria-controls` pointing at nothing.
72
72
  - `TabList` requires `aria-label`. The active panel is itself a tab stop, so a panel with no focusable content is still reachable.
73
73
 
74
+ ## Menu family (3)
75
+ - `<Menu open onClose={(reason) => …} trigger={<IconButton …/>} placement="bottom-start|bottom-end|top-start|top-end" aria-label="…">`: action menu on the native Popover API — top layer and light dismiss come from the platform, roving keyboard and dismissal reasons from Psi (D53). **Controlled-only** like Dialog: `onClose(reason)` reports `"esc" | "outside" | "item-select"` and the consumer flips `open`. Compose `<MenuItem onSelect>` and `<MenuSeparator>`; the trigger needs an accessible name, so an icon-only trigger is `<IconButton aria-label>`.
76
+ - Many menus, one state: for a row-actions column, hold a single `openMenuId` and render each row's Menu with `open={openMenuId === row.id}`, closing with `onClose={() => setOpenMenuId(null)}`. Do not give each row its own boolean. This is the shape D58 fixed a real dismissal bug in — switching directly between two menus left both closed, because the platform light-dismisses the first before the consumer's click handler runs.
77
+
74
78
  ## Compositions (recipes for what has no component yet)
75
79
 
76
80
  - Form field: wrap the label, control, and message with `<Field label description error required>{control}</Field>` — label association (htmlFor/id), the description/error message line (swaps to error, aria-live="polite"), and aria-describedby/aria-invalid wiring onto a nested Input/Select are automatic (D49). `group` mode renders fieldset/legend for self-labeled controls (Checkbox/Switch). Standalone Input/Select used outside Field still need `aria-invalid` and `aria-describedby` paired manually. Spacing: `psi-gap-24` between fields, `psi-gap-12` for toggle groups, `psi-gap-8` for button rows and label+Tag rows.
77
81
  - Stacks/rows: `.psi-gap-*` utilities set only the gap — they do not create a flex/grid container. Pair them with `display: flex` (column for stacks, row for button groups).
82
+ - Page-level spacing: the form scale above (24/12/8) governs controls inside a form, not page layout. For layout use `psi-gap-32` between sections of a page, `psi-gap-24` between cards or panels in a grid, `psi-py-32` for a section's vertical padding, and `.psi-container` for the page's max-width and gutters (it is in utilities.css, not components.css). Marketing-scale rhythm is app-level and deliberately not tokenized — the system stops at the container.
83
+ - Utility classes: families are `psi-gap-*`, `psi-p-*`/`psi-px-*`/`psi-py-*`, `psi-m-*`/`psi-mx-*`/`psi-my-*` (all on the spacing scale), `psi-text-*` and `psi-display-*` (typography), plus `.psi-tabular`, `.psi-container` and `.psi-media-tint`. Utilities set one property and create no layout context — pair `psi-gap-*` with `display: flex`. Use `psi-m-0` to kill a UA margin rather than an inline style. The exact roster is `@handamade/psi-tokens/guidance.json` → `utilities.classes`.
78
84
  - Native pass-through: components spread unlisted props onto their underlying element (Input → `<input>`, Select → `<select>`, Button → `<button>`/`<a>`), so `value`, `onChange`, `type`, `id`, `autoComplete`, `checked` etc. all work even though manifest.json lists only the component-specific props. Exception: Tooltip takes no host props — put handlers/attributes on its trigger child.
79
85
  - Theming, programmatic: the standard setup is `data-psi-theme` on `<html>`. From JS, set it once at app init: `document.documentElement.dataset.psiTheme = "dark"` (React: in a root-level effect). Theme CSS does NOT paint the page background — paint `background: var(--psi-bg-primary)` at your app root yourself. For a themed subtree, set `data-psi-theme` on a wrapper and paint its background the same way; brand font combos do not reach nested subtrees (see guidance.fonts.scope).
80
86
  - Size selection: 32 is the default (compact UI); 40 for comfortable forms and marketing pages; 24 for inline/table-row actions; 48 for hero CTAs.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@handamade/psi-react",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -46,7 +46,7 @@
46
46
  "tsx": "^4.22.4",
47
47
  "vite": "^6.0.0",
48
48
  "vitest": "^3.2.6",
49
- "@handamade/psi-tokens": "0.15.0"
49
+ "@handamade/psi-tokens": "0.16.0"
50
50
  },
51
51
  "scripts": {
52
52
  "build": "vite build && tsc -p tsconfig.build.json && tsx scripts/emit-styles-dts.ts && tsx scripts/emit-manifest.ts && tsx scripts/emit-patterns.ts && tsx scripts/emit-docs.ts",