altitude-design 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/fonts.css ADDED
@@ -0,0 +1 @@
1
+ @import url('https://fonts.googleapis.com/css2?family=Instrument+Sans:wght@400..700&family=JetBrains+Mono:wght@400;500&display=swap');
package/dist/index.cjs CHANGED
@@ -56,6 +56,7 @@ __export(index_exports, {
56
56
  Drilldown: () => Drilldown,
57
57
  DropZone: () => DropZone,
58
58
  DropdownMenu: () => DropdownMenu,
59
+ EmptyState: () => EmptyState,
59
60
  Field: () => Field,
60
61
  HoldButton: () => HoldButton,
61
62
  HoverCard: () => HoverCard,
@@ -98,6 +99,7 @@ __export(index_exports, {
98
99
  Textarea: () => Textarea,
99
100
  Toaster: () => Toaster,
100
101
  Tooltip: () => Tooltip,
102
+ checkScheme: () => checkScheme,
101
103
  commandScore: () => commandScore,
102
104
  contrast: () => contrast,
103
105
  deriveAccent: () => deriveAccent,
@@ -340,6 +342,63 @@ function deriveAccent(accent, surfaces) {
340
342
  "--accent-soft": rgbToHex(mix(hexToRgb(ground), a, dark ? 0.2 : 0.13))
341
343
  };
342
344
  }
345
+ var SURFACES = ["surface-trench", "surface-well", "surface-ground", "surface-raised", "surface-lifted", "surface-floating", "surface-overlay", "surface-modal"];
346
+ var PAIRS = [];
347
+ for (const t of ["ink", "ink-muted", "accent-ink", "positive", "caution", "critical"]) for (const g of SURFACES) PAIRS.push([t, g, 4.5]);
348
+ for (const [t, g] of [
349
+ ["ink", "accent-soft"],
350
+ ["accent-ink", "accent-soft"],
351
+ ["positive", "positive-soft"],
352
+ ["caution", "caution-soft"],
353
+ ["critical", "critical-soft"],
354
+ ["ink", "critical-soft"],
355
+ ["ink", "positive-soft"],
356
+ ["ink", "caution-soft"],
357
+ ["ink-muted", "accent-soft"],
358
+ ["on-accent", "accent"],
359
+ ["on-critical", "critical"]
360
+ ]) PAIRS.push([t, g, 4.5]);
361
+ for (const g of ["surface-ground", "surface-well", "surface-raised", "surface-trench"]) PAIRS.push(["line-control", g, 3], ["accent", g, 3]);
362
+ for (const p of ["presence-1", "presence-2", "presence-3"]) {
363
+ PAIRS.push(["on-presence", p, 4.5]);
364
+ for (const g of SURFACES) PAIRS.push([p, g, 3]);
365
+ }
366
+ var isHex = (v) => /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(v);
367
+ function checkScheme(colors) {
368
+ const cs = typeof Element !== "undefined" && colors instanceof Element ? getComputedStyle(colors) : null;
369
+ const get = (n) => (cs ? cs.getPropertyValue("--" + n) : colors[n] ?? "").trim();
370
+ const out = [];
371
+ const seen = /* @__PURE__ */ new Set();
372
+ const ok = (n) => {
373
+ const v = get(n);
374
+ if (isHex(v)) return true;
375
+ if (!seen.has(n)) {
376
+ seen.add(n);
377
+ out.push(v ? `${n} is ${v}: use a 3- or 6-digit hex colour so it can be checked.` : `${n} is missing.`);
378
+ }
379
+ return false;
380
+ };
381
+ const L = (n) => lum(hexToRgb(get(n)));
382
+ for (let i = 1; i < SURFACES.length; i++) {
383
+ const lo = SURFACES[i - 1], hi = SURFACES[i];
384
+ if (!ok(lo) || !ok(hi)) continue;
385
+ if (i <= 3 ? L(hi) <= L(lo) : L(hi) < L(lo)) out.push(`${hi} (${get(hi)}) must be ${i <= 3 ? "lighter than" : "at least as light as"} ${lo} (${get(lo)}).`);
386
+ }
387
+ for (const [t, g, min] of PAIRS) {
388
+ if (!ok(t) || !ok(g)) continue;
389
+ const r = contrast(get(t), get(g));
390
+ if (r < min) out.push(`${t} on ${g} is ${r.toFixed(2)}:1; it needs ${min}:1.`);
391
+ }
392
+ return out;
393
+ }
394
+ var SHADOW_OR_LINE = /^(trench|well|elev-\d|lip-top|lip-bottom|line)$/;
395
+ function schemeCss(sel, tokens) {
396
+ const decl = (keep) => Object.entries(tokens).filter(([k, v]) => keep(k) && typeof v === "string" && /^[\w-]+$/.test(k) && !/[;{}<>]/.test(v)).map(([k, v]) => `--${k}: ${v};`).join(" ");
397
+ const plain = decl((k) => !SHADOW_OR_LINE.test(k)), soft = decl((k) => SHADOW_OR_LINE.test(k));
398
+ let css = plain ? `${sel} { ${plain} }` : "";
399
+ if (soft) css += `@media not (prefers-contrast: more) { ${sel}:not([data-contrast="more"], [data-contrast="more"] *) { ${soft} } }`;
400
+ return css;
401
+ }
343
402
  var AltitudeContext = React.createContext(null);
344
403
  var useAltitude = () => React.useContext(AltitudeContext);
345
404
  function AltitudeProvider({ scheme, accent, motion = "system", contrast: contrast2 = "system", recede = true, approach = true, className, style, children, ...rest }) {
@@ -347,6 +406,18 @@ function AltitudeProvider({ scheme, accent, motion = "system", contrast: contras
347
406
  const [host, setHost] = React.useState(null);
348
407
  const [layers, setLayers] = React.useState(0);
349
408
  const [accentVars, setAccentVars] = React.useState(null);
409
+ const custom = scheme && typeof scheme === "object" ? scheme : null;
410
+ const schemeKey = custom ? JSON.stringify(custom) : scheme;
411
+ const scope = React.useId();
412
+ const schemeSel = `.alt-root[data-scheme-id="${scope}"]`;
413
+ const warned = React.useRef("");
414
+ React.useEffect(() => {
415
+ if (!custom || !rootRef.current || warned.current === schemeKey) return;
416
+ warned.current = schemeKey;
417
+ const issues = checkScheme(rootRef.current);
418
+ if (issues.length) console.warn(`Altitude: the scheme "${custom.name}" breaks ${issues.length} of the scheme invariants (see Theming.md):
419
+ ` + issues.join("\n"));
420
+ }, [schemeKey]);
350
421
  useIsoLayoutEffect(() => {
351
422
  if (!accent || !rootRef.current) {
352
423
  setAccentVars(null);
@@ -362,7 +433,7 @@ function AltitudeProvider({ scheme, accent, motion = "system", contrast: contras
362
433
  const mo = new MutationObserver(compute);
363
434
  mo.observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] });
364
435
  return () => mo.disconnect();
365
- }, [accent, scheme]);
436
+ }, [accent, schemeKey]);
366
437
  const api = React.useMemo(() => ({
367
438
  host,
368
439
  pushLayer: () => {
@@ -375,13 +446,16 @@ function AltitudeProvider({ scheme, accent, motion = "system", contrast: contras
375
446
  {
376
447
  ref: rootRef,
377
448
  className: cx("alt-root", className),
378
- "data-theme": scheme,
449
+ "data-theme": custom ? custom.base ?? "daylight" : scheme,
450
+ "data-scheme": custom?.name,
451
+ "data-scheme-id": custom ? scope : void 0,
379
452
  "data-motion": motion === "system" ? void 0 : motion,
380
453
  "data-contrast": contrast2 === "more" ? "more" : void 0,
381
454
  "data-approach-off": approach ? void 0 : "",
382
455
  style: { ...accentVars, ...style },
383
456
  ...rest
384
457
  },
458
+ custom && /* @__PURE__ */ React.createElement("style", null, schemeCss(schemeSel, custom.tokens)),
385
459
  /* @__PURE__ */ React.createElement("div", { className: "alt-stage", "data-receded": recede && layers > 0 ? "" : void 0 }, children),
386
460
  /* @__PURE__ */ React.createElement("div", { className: "alt-layers", ref: setHost })
387
461
  ));
@@ -2058,6 +2132,8 @@ function CommandPalette({
2058
2132
  filter,
2059
2133
  empty,
2060
2134
  shortcut = "k",
2135
+ query: queryProp,
2136
+ defaultQuery = "",
2061
2137
  onQueryChange,
2062
2138
  closeOnSelect = true,
2063
2139
  hints = true,
@@ -2065,7 +2141,8 @@ function CommandPalette({
2065
2141
  ...rest
2066
2142
  }) {
2067
2143
  const [isOpen, set] = useControllable(open, defaultOpen, onOpenChange);
2068
- const [query, setQuery] = React7.useState("");
2144
+ const [innerQuery, setInnerQuery] = React7.useState(defaultQuery);
2145
+ const query = queryProp ?? innerQuery;
2069
2146
  const [active, setActive] = React7.useState(0);
2070
2147
  const [onset, setOnset] = React7.useState(true);
2071
2148
  const [said, setSaid] = React7.useState("");
@@ -2076,6 +2153,9 @@ function CommandPalette({
2076
2153
  const rowEls = React7.useRef(/* @__PURE__ */ new Map());
2077
2154
  const prevTops = React7.useRef(null);
2078
2155
  const byKeyboard = React7.useRef(false);
2156
+ const caretPlaced = React7.useRef(false);
2157
+ const seed = React7.useRef(defaultQuery);
2158
+ seed.current = defaultQuery;
2079
2159
  const [panelEl, setPanelEl] = React7.useState(null);
2080
2160
  const panelRef = React7.useCallback((el) => {
2081
2161
  panel.current = el;
@@ -2102,11 +2182,15 @@ function CommandPalette({
2102
2182
  }, [shortcut, isOpen, set]);
2103
2183
  React7.useEffect(() => {
2104
2184
  if (isOpen) {
2105
- setQuery("");
2185
+ setInnerQuery(seed.current);
2106
2186
  setActive(0);
2107
2187
  setOnset(true);
2188
+ caretPlaced.current = false;
2108
2189
  }
2109
2190
  }, [isOpen]);
2191
+ React7.useEffect(() => {
2192
+ setActive(0);
2193
+ }, [query]);
2110
2194
  const q = query.trim();
2111
2195
  const score = filter ?? itemScore;
2112
2196
  const view = items.map((g, gi) => {
@@ -2224,11 +2308,17 @@ function CommandPalette({
2224
2308
  leading: /* @__PURE__ */ React7.createElement(SearchIcon, null),
2225
2309
  trailing: /* @__PURE__ */ React7.createElement("span", { "aria-hidden": "true" }, /* @__PURE__ */ React7.createElement(Kbd, null, "esc")),
2226
2310
  onChange: (e) => {
2227
- setQuery(e.target.value);
2311
+ setInnerQuery(e.target.value);
2228
2312
  setActive(0);
2229
2313
  setOnset(false);
2230
2314
  onQueryChange?.(e.target.value);
2231
2315
  },
2316
+ onFocus: (e) => {
2317
+ if (caretPlaced.current) return;
2318
+ caretPlaced.current = true;
2319
+ const n2 = e.currentTarget.value.length;
2320
+ e.currentTarget.setSelectionRange(n2, n2);
2321
+ },
2232
2322
  onKeyDown: onInputKeyDown
2233
2323
  }
2234
2324
  )), /* @__PURE__ */ React7.createElement("div", { className: "alt-cmd-viewport", ref: viewport }, /* @__PURE__ */ React7.createElement("div", { ref: sizer }, /* @__PURE__ */ React7.createElement("div", { className: "alt-cmd-list", ref: list, role: "listbox", id: `${id}-list`, "aria-label": "Results" }, /* @__PURE__ */ React7.createElement(
@@ -2800,8 +2890,53 @@ function Stepper({ steps, current, onStepChange, orientation = "horizontal", cla
2800
2890
  })), /* @__PURE__ */ React9.createElement("span", { className: "alt-sr", "aria-live": "polite" }, said));
2801
2891
  }
2802
2892
 
2803
- // project/components/src/collections.tsx
2893
+ // project/components/src/empty.tsx
2804
2894
  var React10 = __toESM(require("react"), 1);
2895
+ function EmptyState({
2896
+ icon: icon2,
2897
+ title,
2898
+ description,
2899
+ action,
2900
+ secondary,
2901
+ size = "md",
2902
+ headingLevel = 3,
2903
+ announce,
2904
+ className,
2905
+ children,
2906
+ ...rest
2907
+ }) {
2908
+ const id = React10.useId();
2909
+ const H = `h${headingLevel}`;
2910
+ const words = typeof announce === "string" ? announce : announce && typeof title === "string" ? title : "";
2911
+ const [said, setSaid] = React10.useState("");
2912
+ React10.useEffect(() => {
2913
+ if (!words) {
2914
+ setSaid("");
2915
+ return;
2916
+ }
2917
+ const t = setTimeout(() => setSaid(words), 100);
2918
+ return () => clearTimeout(t);
2919
+ }, [words]);
2920
+ return /* @__PURE__ */ React10.createElement(
2921
+ "section",
2922
+ {
2923
+ className: cx("alt-empty", className),
2924
+ "data-size": size,
2925
+ "aria-labelledby": `${id}-t`,
2926
+ "aria-describedby": description ? `${id}-d` : void 0,
2927
+ ...rest
2928
+ },
2929
+ icon2 && /* @__PURE__ */ React10.createElement("span", { className: "alt-empty-icon", "aria-hidden": "true" }, icon2),
2930
+ /* @__PURE__ */ React10.createElement(H, { id: `${id}-t`, className: "alt-empty-title" }, title),
2931
+ description && /* @__PURE__ */ React10.createElement("p", { id: `${id}-d`, className: "alt-empty-desc" }, description),
2932
+ children,
2933
+ (action || secondary) && /* @__PURE__ */ React10.createElement("div", { className: "alt-empty-actions" }, action, secondary),
2934
+ announce && /* @__PURE__ */ React10.createElement("span", { className: "alt-sr", "aria-live": "polite" }, said)
2935
+ );
2936
+ }
2937
+
2938
+ // project/components/src/collections.tsx
2939
+ var React11 = __toESM(require("react"), 1);
2805
2940
  var dropSnapshots = /* @__PURE__ */ new WeakMap();
2806
2941
  var ms2 = (cs, name, fallback) => parseFloat(cs.getPropertyValue(name)) || fallback;
2807
2942
  function pagePlace(el) {
@@ -2823,7 +2958,7 @@ function placeIn(el, host) {
2823
2958
  return { x: a.x - b.x, y: a.y - b.y };
2824
2959
  }
2825
2960
  function useCarry(host, selector, attr, durToken = "--dur-travel-3") {
2826
- const prev = React10.useRef(null);
2961
+ const prev = React11.useRef(null);
2827
2962
  useIsoLayoutEffect(() => {
2828
2963
  const root = host.current;
2829
2964
  if (!root) return;
@@ -2852,12 +2987,12 @@ var moveItem = (arr, from, to) => {
2852
2987
  a.splice(to, 0, x);
2853
2988
  return a;
2854
2989
  };
2855
- var Table = React10.forwardRef(function Table2({ maxHeight, underlay, className, children, ...rest }, ref) {
2856
- const wrap = React10.useRef(null);
2857
- const table = React10.useRef(null);
2858
- React10.useImperativeHandle(ref, () => table.current);
2859
- const [edges, setEdges] = React10.useState({ top: false, bottom: false });
2860
- const measure = React10.useCallback(() => {
2990
+ var Table = React11.forwardRef(function Table2({ maxHeight, underlay, className, children, ...rest }, ref) {
2991
+ const wrap = React11.useRef(null);
2992
+ const table = React11.useRef(null);
2993
+ React11.useImperativeHandle(ref, () => table.current);
2994
+ const [edges, setEdges] = React11.useState({ top: false, bottom: false });
2995
+ const measure = React11.useCallback(() => {
2861
2996
  const el = wrap.current;
2862
2997
  if (!el) return;
2863
2998
  const top = el.scrollTop > 1, bottom = el.scrollTop + el.clientHeight < el.scrollHeight - 1;
@@ -2878,7 +3013,7 @@ var Table = React10.forwardRef(function Table2({ maxHeight, underlay, className,
2878
3013
  }, [measure]);
2879
3014
  useCarry(table, "tbody > tr[data-row]", "data-row");
2880
3015
  const scrolls = maxHeight !== void 0;
2881
- return /* @__PURE__ */ React10.createElement(
3016
+ return /* @__PURE__ */ React11.createElement(
2882
3017
  "div",
2883
3018
  {
2884
3019
  ref: wrap,
@@ -2890,27 +3025,27 @@ var Table = React10.forwardRef(function Table2({ maxHeight, underlay, className,
2890
3025
  ...scrolls ? { tabIndex: 0, role: "region", "aria-label": rest["aria-label"] } : {}
2891
3026
  },
2892
3027
  underlay,
2893
- /* @__PURE__ */ React10.createElement("table", { ref: table, className: cx("alt-table", className), ...rest }, children)
3028
+ /* @__PURE__ */ React11.createElement("table", { ref: table, className: cx("alt-table", className), ...rest }, children)
2894
3029
  );
2895
3030
  });
2896
- var TableHeader = ({ className, ...p }) => /* @__PURE__ */ React10.createElement("thead", { className: cx("alt-table-head", className), ...p });
2897
- var TableBody = React10.forwardRef(function TableBody2({ className, ...p }, ref) {
2898
- return /* @__PURE__ */ React10.createElement("tbody", { ref, className: cx("alt-table-body", className), ...p });
3031
+ var TableHeader = ({ className, ...p }) => /* @__PURE__ */ React11.createElement("thead", { className: cx("alt-table-head", className), ...p });
3032
+ var TableBody = React11.forwardRef(function TableBody2({ className, ...p }, ref) {
3033
+ return /* @__PURE__ */ React11.createElement("tbody", { ref, className: cx("alt-table-body", className), ...p });
2899
3034
  });
2900
- var TableFooter = ({ className, ...p }) => /* @__PURE__ */ React10.createElement("tfoot", { className: cx("alt-table-foot", className), ...p });
2901
- var TableCaption = ({ className, ...p }) => /* @__PURE__ */ React10.createElement("caption", { className: cx("alt-table-caption", className), ...p });
2902
- var TableRow = React10.forwardRef(function TableRow2({ rowId, selected, interactive, relevance, settle, held, className, style, ...rest }, ref) {
2903
- const [glow, setGlow] = React10.useState(void 0);
2904
- const [delay, setDelay] = React10.useState(0);
2905
- const first = React10.useRef(true);
2906
- React10.useEffect(() => {
3035
+ var TableFooter = ({ className, ...p }) => /* @__PURE__ */ React11.createElement("tfoot", { className: cx("alt-table-foot", className), ...p });
3036
+ var TableCaption = ({ className, ...p }) => /* @__PURE__ */ React11.createElement("caption", { className: cx("alt-table-caption", className), ...p });
3037
+ var TableRow = React11.forwardRef(function TableRow2({ rowId, selected, interactive, relevance, settle, held, className, style, ...rest }, ref) {
3038
+ const [glow, setGlow] = React11.useState(void 0);
3039
+ const [delay, setDelay] = React11.useState(0);
3040
+ const first = React11.useRef(true);
3041
+ React11.useEffect(() => {
2907
3042
  const wasFirst = first.current;
2908
3043
  first.current = false;
2909
3044
  if (settle === void 0 || settle === null || settle === false || wasFirst && settle !== true) return;
2910
3045
  setDelay(riseDelay());
2911
3046
  setGlow((g) => g === "a" ? "b" : "a");
2912
3047
  }, [settle]);
2913
- return /* @__PURE__ */ React10.createElement(
3048
+ return /* @__PURE__ */ React11.createElement(
2914
3049
  "tr",
2915
3050
  {
2916
3051
  ref,
@@ -2926,14 +3061,14 @@ var TableRow = React10.forwardRef(function TableRow2({ rowId, selected, interact
2926
3061
  }
2927
3062
  );
2928
3063
  });
2929
- var SortIcon = () => /* @__PURE__ */ React10.createElement("svg", { className: "alt-table-sort-icon", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.75, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React10.createElement("path", { d: "M6 15l6-6 6 6" }));
3064
+ var SortIcon = () => /* @__PURE__ */ React11.createElement("svg", { className: "alt-table-sort-icon", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.75, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React11.createElement("path", { d: "M6 15l6-6 6 6" }));
2930
3065
  function TableHead({ align, sort, onSort, className, children, ...rest }) {
2931
- return /* @__PURE__ */ React10.createElement("th", { scope: "col", className: cx("alt-th", className), "data-align": align, "aria-sort": onSort ? sort ?? "none" : void 0, ...rest }, onSort ? /* @__PURE__ */ React10.createElement("button", { type: "button", className: "alt-table-sort", "data-sort": sort ?? "none", onClick: onSort }, children, /* @__PURE__ */ React10.createElement(SortIcon, null)) : children);
3066
+ return /* @__PURE__ */ React11.createElement("th", { scope: "col", className: cx("alt-th", className), "data-align": align, "aria-sort": onSort ? sort ?? "none" : void 0, ...rest }, onSort ? /* @__PURE__ */ React11.createElement("button", { type: "button", className: "alt-table-sort", "data-sort": sort ?? "none", onClick: onSort }, children, /* @__PURE__ */ React11.createElement(SortIcon, null)) : children);
2932
3067
  }
2933
3068
  function TableCell({ align, className, ...rest }) {
2934
- return /* @__PURE__ */ React10.createElement("td", { className: cx("alt-td", className), "data-align": align, ...rest });
3069
+ return /* @__PURE__ */ React11.createElement("td", { className: cx("alt-td", className), "data-align": align, ...rest });
2935
3070
  }
2936
- var GripIcon = () => /* @__PURE__ */ React10.createElement("svg", { width: "12", height: "16", viewBox: "0 0 12 16", fill: "currentColor", "aria-hidden": "true" }, /* @__PURE__ */ React10.createElement("circle", { cx: "3", cy: "3", r: "1.3" }), /* @__PURE__ */ React10.createElement("circle", { cx: "9", cy: "3", r: "1.3" }), /* @__PURE__ */ React10.createElement("circle", { cx: "3", cy: "8", r: "1.3" }), /* @__PURE__ */ React10.createElement("circle", { cx: "9", cy: "8", r: "1.3" }), /* @__PURE__ */ React10.createElement("circle", { cx: "3", cy: "13", r: "1.3" }), /* @__PURE__ */ React10.createElement("circle", { cx: "9", cy: "13", r: "1.3" }));
3071
+ var GripIcon = () => /* @__PURE__ */ React11.createElement("svg", { width: "12", height: "16", viewBox: "0 0 12 16", fill: "currentColor", "aria-hidden": "true" }, /* @__PURE__ */ React11.createElement("circle", { cx: "3", cy: "3", r: "1.3" }), /* @__PURE__ */ React11.createElement("circle", { cx: "9", cy: "3", r: "1.3" }), /* @__PURE__ */ React11.createElement("circle", { cx: "3", cy: "8", r: "1.3" }), /* @__PURE__ */ React11.createElement("circle", { cx: "9", cy: "8", r: "1.3" }), /* @__PURE__ */ React11.createElement("circle", { cx: "3", cy: "13", r: "1.3" }), /* @__PURE__ */ React11.createElement("circle", { cx: "9", cy: "13", r: "1.3" }));
2937
3072
  function DataTable({
2938
3073
  columns,
2939
3074
  rows,
@@ -2958,11 +3093,11 @@ function DataTable({
2958
3093
  }) {
2959
3094
  const [sel, setSel] = useControllable(selected, defaultSelected, onSelectedChange);
2960
3095
  const [srt, setSrt] = useControllable(sort, defaultSort, onSortChange);
2961
- const [said, setSaid] = React10.useState("");
2962
- const table = React10.useRef(null);
2963
- const body = React10.useRef(null);
3096
+ const [said, setSaid] = React11.useState("");
3097
+ const table = React11.useRef(null);
3098
+ const body = React11.useRef(null);
2964
3099
  const name = (r) => getRowLabel?.(r) ?? getRowId(r);
2965
- const view = React10.useMemo(() => {
3100
+ const view = React11.useMemo(() => {
2966
3101
  const col = srt && columns.find((c) => c.key === srt.key);
2967
3102
  if (!srt || !col?.sortValue) return rows;
2968
3103
  const d = srt.direction === "descending" ? -1 : 1;
@@ -2979,9 +3114,9 @@ function DataTable({
2979
3114
  setSaid(next ? `Sorted by ${typeof c.header === "string" ? c.header : c.key}, ${next.direction}.` : "Sort cleared.");
2980
3115
  };
2981
3116
  const toggle = (id, on) => setSel(on ? [...sel.filter((x) => x !== id), id] : sel.filter((x) => x !== id));
2982
- const start = React10.useRef(null);
2983
- const [drag, setDrag] = React10.useState(null);
2984
- const [held, setHeld] = React10.useState(null);
3117
+ const start = React11.useRef(null);
3118
+ const [drag, setDrag] = React11.useState(null);
3119
+ const [held, setHeld] = React11.useState(null);
2985
3120
  const rowEls = () => Array.from(body.current?.querySelectorAll("tr[data-row]") ?? []);
2986
3121
  const snapshot = (at) => {
2987
3122
  if (!table.current) return;
@@ -3051,24 +3186,24 @@ function DataTable({
3051
3186
  setSaid(`Move cancelled; ${name(view[index])} is back at position ${held.origin + 1}.`);
3052
3187
  }
3053
3188
  };
3054
- React10.useEffect(() => {
3189
+ React11.useEffect(() => {
3055
3190
  if (!movable) setHeld(null);
3056
3191
  }, [movable]);
3057
3192
  const s = start.current;
3058
3193
  const h = drag && s ? s.hs[drag.from] : 0;
3059
3194
  const slotTop = drag && s ? drag.to <= drag.from ? s.tops[drag.to] : s.tops[drag.to] + s.hs[drag.to] - s.hs[drag.from] : 0;
3060
3195
  const span = columns.length + (selectable ? 1 : 0) + (movable ? 1 : 0);
3061
- return /* @__PURE__ */ React10.createElement("div", { className: cx("alt-data-table", className) }, (selectable || toolbar) && /* @__PURE__ */ React10.createElement("div", { className: "alt-table-tools" }, sel.length > 0 && /* @__PURE__ */ React10.createElement("div", { className: "alt-table-selection" }, /* @__PURE__ */ React10.createElement(Badge, { variant: "count", tone: "accent", "aria-hidden": "true" }, sel.length), /* @__PURE__ */ React10.createElement("span", null, "selected"), actions?.(sel), /* @__PURE__ */ React10.createElement(Button, { size: "sm", variant: "ghost", onClick: () => setSel([]) }, "Clear")), toolbar && /* @__PURE__ */ React10.createElement("div", { className: "alt-table-tools-end" }, toolbar)), /* @__PURE__ */ React10.createElement(
3196
+ return /* @__PURE__ */ React11.createElement("div", { className: cx("alt-data-table", className) }, (selectable || toolbar) && /* @__PURE__ */ React11.createElement("div", { className: "alt-table-tools" }, sel.length > 0 && /* @__PURE__ */ React11.createElement("div", { className: "alt-table-selection" }, /* @__PURE__ */ React11.createElement(Badge, { variant: "count", tone: "accent", "aria-hidden": "true" }, sel.length), /* @__PURE__ */ React11.createElement("span", null, "selected"), actions?.(sel), /* @__PURE__ */ React11.createElement(Button, { size: "sm", variant: "ghost", onClick: () => setSel([]) }, "Clear")), toolbar && /* @__PURE__ */ React11.createElement("div", { className: "alt-table-tools-end" }, toolbar)), /* @__PURE__ */ React11.createElement(
3062
3197
  Table,
3063
3198
  {
3064
3199
  ref: table,
3065
3200
  maxHeight,
3066
3201
  "aria-label": rest["aria-label"],
3067
3202
  "data-dragging": drag ? "" : void 0,
3068
- underlay: drag && s ? /* @__PURE__ */ React10.createElement("span", { className: "alt-table-slot", "aria-hidden": "true", style: { top: slotTop + (table.current?.offsetTop ?? 0), height: h } }) : null
3203
+ underlay: drag && s ? /* @__PURE__ */ React11.createElement("span", { className: "alt-table-slot", "aria-hidden": "true", style: { top: slotTop + (table.current?.offsetTop ?? 0), height: h } }) : null
3069
3204
  },
3070
- /* @__PURE__ */ React10.createElement(TableHeader, null, /* @__PURE__ */ React10.createElement(TableRow, null, movable && /* @__PURE__ */ React10.createElement(TableHead, { className: "alt-th-grip" }, /* @__PURE__ */ React10.createElement("span", { className: "alt-sr" }, "Move")), selectable && /* @__PURE__ */ React10.createElement(TableHead, { className: "alt-th-check" }, /* @__PURE__ */ React10.createElement(Checkbox, { "aria-label": "Select all rows", checked: all, indeterminate: some, onCheckedChange: (on) => setSel(on ? Array.from(/* @__PURE__ */ new Set([...sel, ...ids])) : sel.filter((x) => !ids.includes(x))) })), columns.map((c) => /* @__PURE__ */ React10.createElement(TableHead, { key: c.key, align: c.align, style: { width: c.width }, sort: srt?.key === c.key ? srt.direction : "none", onSort: c.sortValue ? () => cycle(c) : void 0 }, c.header)))),
3071
- /* @__PURE__ */ React10.createElement(TableBody, { ref: body }, !view.length && /* @__PURE__ */ React10.createElement("tr", null, /* @__PURE__ */ React10.createElement("td", { className: "alt-td alt-table-empty", colSpan: span }, empty)), view.map((r, i) => {
3205
+ /* @__PURE__ */ React11.createElement(TableHeader, null, /* @__PURE__ */ React11.createElement(TableRow, null, movable && /* @__PURE__ */ React11.createElement(TableHead, { className: "alt-th-grip" }, /* @__PURE__ */ React11.createElement("span", { className: "alt-sr" }, "Move")), selectable && /* @__PURE__ */ React11.createElement(TableHead, { className: "alt-th-check" }, /* @__PURE__ */ React11.createElement(Checkbox, { "aria-label": "Select all rows", checked: all, indeterminate: some, onCheckedChange: (on) => setSel(on ? Array.from(/* @__PURE__ */ new Set([...sel, ...ids])) : sel.filter((x) => !ids.includes(x))) })), columns.map((c) => /* @__PURE__ */ React11.createElement(TableHead, { key: c.key, align: c.align, style: { width: c.width }, sort: srt?.key === c.key ? srt.direction : "none", onSort: c.sortValue ? () => cycle(c) : void 0 }, c.header)))),
3206
+ /* @__PURE__ */ React11.createElement(TableBody, { ref: body }, !view.length && /* @__PURE__ */ React11.createElement("tr", null, /* @__PURE__ */ React11.createElement("td", { className: "alt-td alt-table-empty", colSpan: span }, empty)), view.map((r, i) => {
3072
3207
  const id = ids[i];
3073
3208
  let shift = 0;
3074
3209
  if (drag && id !== drag.id && h) {
@@ -3076,7 +3211,7 @@ function DataTable({
3076
3211
  if (drag.to < drag.from && i >= drag.to && i < drag.from) shift = h;
3077
3212
  }
3078
3213
  const following = drag?.id === id;
3079
- return /* @__PURE__ */ React10.createElement(
3214
+ return /* @__PURE__ */ React11.createElement(
3080
3215
  TableRow,
3081
3216
  {
3082
3217
  key: id,
@@ -3091,7 +3226,7 @@ function DataTable({
3091
3226
  if (!e.target.closest("button, a, input, label, select, textarea")) onRowClick(r);
3092
3227
  } : void 0
3093
3228
  },
3094
- movable && /* @__PURE__ */ React10.createElement(TableCell, { className: "alt-td-grip" }, /* @__PURE__ */ React10.createElement(
3229
+ movable && /* @__PURE__ */ React11.createElement(TableCell, { className: "alt-td-grip" }, /* @__PURE__ */ React11.createElement(
3095
3230
  "button",
3096
3231
  {
3097
3232
  type: "button",
@@ -3107,18 +3242,18 @@ function DataTable({
3107
3242
  if (held?.id === id && !drag) setTimeout(() => setHeld((x) => x?.id === id && document.activeElement?.closest(`tr[data-row="${CSS.escape(id)}"]`) === null ? null : x), 0);
3108
3243
  }
3109
3244
  },
3110
- /* @__PURE__ */ React10.createElement(GripIcon, null)
3245
+ /* @__PURE__ */ React11.createElement(GripIcon, null)
3111
3246
  )),
3112
- selectable && /* @__PURE__ */ React10.createElement(TableCell, { className: "alt-td-check" }, /* @__PURE__ */ React10.createElement(Checkbox, { "aria-label": `Select ${name(r)}`, checked: chosen.has(id), onCheckedChange: (on) => toggle(id, on) })),
3247
+ selectable && /* @__PURE__ */ React11.createElement(TableCell, { className: "alt-td-check" }, /* @__PURE__ */ React11.createElement(Checkbox, { "aria-label": `Select ${name(r)}`, checked: chosen.has(id), onCheckedChange: (on) => toggle(id, on) })),
3113
3248
  columns.map((c, ci) => {
3114
3249
  const content = c.cell ? c.cell(r) : String(r[c.key] ?? "");
3115
- return /* @__PURE__ */ React10.createElement(TableCell, { key: c.key, align: c.align }, onRowClick && ci === 0 ? /* @__PURE__ */ React10.createElement("button", { type: "button", className: "alt-table-rowbtn", onClick: () => onRowClick(r) }, content) : content);
3250
+ return /* @__PURE__ */ React11.createElement(TableCell, { key: c.key, align: c.align }, onRowClick && ci === 0 ? /* @__PURE__ */ React11.createElement("button", { type: "button", className: "alt-table-rowbtn", onClick: () => onRowClick(r) }, content) : content);
3116
3251
  })
3117
3252
  );
3118
3253
  }))
3119
- ), /* @__PURE__ */ React10.createElement("span", { className: "alt-sr", "aria-live": "polite" }, selectable && sel.length ? `${sel.length} selected. ` : "", said));
3254
+ ), /* @__PURE__ */ React11.createElement("span", { className: "alt-sr", "aria-live": "polite" }, selectable && sel.length ? `${sel.length} selected. ` : "", said));
3120
3255
  }
3121
- var BoardCtx = React10.createContext(null);
3256
+ var BoardCtx = React11.createContext(null);
3122
3257
  function readBoard(root) {
3123
3258
  return Array.from(root.querySelectorAll("[data-board-col]")).map((col) => {
3124
3259
  const list = col.querySelector(".alt-board-list");
@@ -3165,18 +3300,18 @@ function plan(cols, id, cx2, cy, h) {
3165
3300
  }
3166
3301
  return { to: { column: tc.id, index }, shifts, slotTop, gap: tc.gap };
3167
3302
  }
3168
- var Board = React10.forwardRef(function Board2({ onMove, className, children, ...rest }, ref) {
3169
- const root = React10.useRef(null);
3170
- React10.useImperativeHandle(ref, () => root.current);
3171
- const hintId = React10.useId();
3172
- const [carry, setCarry] = React10.useState(null);
3173
- const [kb, setKb] = React10.useState(null);
3174
- const [landing, setLanding] = React10.useState(null);
3175
- const [said, setSaid] = React10.useState("");
3176
- const start = React10.useRef(null);
3177
- const dragged = React10.useRef(false);
3303
+ var Board = React11.forwardRef(function Board2({ onMove, className, children, ...rest }, ref) {
3304
+ const root = React11.useRef(null);
3305
+ React11.useImperativeHandle(ref, () => root.current);
3306
+ const hintId = React11.useId();
3307
+ const [carry, setCarry] = React11.useState(null);
3308
+ const [kb, setKb] = React11.useState(null);
3309
+ const [landing, setLanding] = React11.useState(null);
3310
+ const [said, setSaid] = React11.useState("");
3311
+ const start = React11.useRef(null);
3312
+ const dragged = React11.useRef(false);
3178
3313
  useCarry(root, "[data-board-card]", "data-board-card");
3179
- React10.useEffect(() => {
3314
+ React11.useEffect(() => {
3180
3315
  if (!landing) return;
3181
3316
  const t = setTimeout(() => setLanding(null), 450);
3182
3317
  return () => clearTimeout(t);
@@ -3305,17 +3440,17 @@ var Board = React10.forwardRef(function Board2({ onMove, className, children, ..
3305
3440
  return d;
3306
3441
  };
3307
3442
  const ctx = { carry, kb, landing, hintId, onPointerDown, onPointerMove, onPointerUp, onKeyDown, onBlur, swallowClick };
3308
- return /* @__PURE__ */ React10.createElement(BoardCtx.Provider, { value: ctx }, /* @__PURE__ */ React10.createElement("div", { ref: root, role: "group", "aria-label": rest["aria-label"] ?? "Board", className: cx("alt-board", className), "data-carrying": carry || kb ? "" : void 0, ...rest }, children, /* @__PURE__ */ React10.createElement("span", { id: hintId, className: "alt-sr" }, "Press Space to pick up. While holding a card, use the arrow keys to move it, Space to drop it, Escape to cancel."), /* @__PURE__ */ React10.createElement("span", { className: "alt-sr", "aria-live": "assertive" }, said)));
3443
+ return /* @__PURE__ */ React11.createElement(BoardCtx.Provider, { value: ctx }, /* @__PURE__ */ React11.createElement("div", { ref: root, role: "group", "aria-label": rest["aria-label"] ?? "Board", className: cx("alt-board", className), "data-carrying": carry || kb ? "" : void 0, ...rest }, children, /* @__PURE__ */ React11.createElement("span", { id: hintId, className: "alt-sr" }, "Press Space to pick up. While holding a card, use the arrow keys to move it, Space to drop it, Escape to cancel."), /* @__PURE__ */ React11.createElement("span", { className: "alt-sr", "aria-live": "assertive" }, said)));
3309
3444
  });
3310
3445
  function BoardColumn({ id, title, collapsed, defaultCollapsed = false, onCollapsedChange, collapsible, className, children, ...rest }) {
3311
- const ctx = React10.useContext(BoardCtx);
3446
+ const ctx = React11.useContext(BoardCtx);
3312
3447
  const [isCollapsed, setCollapsed] = useControllable(collapsed, defaultCollapsed, onCollapsedChange);
3313
- const count = React10.Children.toArray(children).filter(React10.isValidElement).length;
3448
+ const count = React11.Children.toArray(children).filter(React11.isValidElement).length;
3314
3449
  const label = typeof title === "string" ? title : id;
3315
3450
  const carry = ctx?.carry;
3316
3451
  const over = carry ? carry.to.column === id : ctx?.kb?.at.column === id;
3317
3452
  const receiving = !!carry && carry.to.column === id && carry.from.column !== id && !isCollapsed;
3318
- return /* @__PURE__ */ React10.createElement(
3453
+ return /* @__PURE__ */ React11.createElement(
3319
3454
  "section",
3320
3455
  {
3321
3456
  className: cx("alt-board-col", className),
@@ -3326,7 +3461,7 @@ function BoardColumn({ id, title, collapsed, defaultCollapsed = false, onCollaps
3326
3461
  "data-over": over ? "" : void 0,
3327
3462
  ...rest
3328
3463
  },
3329
- /* @__PURE__ */ React10.createElement("header", { className: "alt-board-head" }, /* @__PURE__ */ React10.createElement("h3", { className: "alt-board-title" }, title), /* @__PURE__ */ React10.createElement("span", { className: "alt-board-count", "aria-label": `${count} ${count === 1 ? "card" : "cards"}` }, count), collapsible && /* @__PURE__ */ React10.createElement(
3464
+ /* @__PURE__ */ React11.createElement("header", { className: "alt-board-head" }, /* @__PURE__ */ React11.createElement("h3", { className: "alt-board-title" }, title), /* @__PURE__ */ React11.createElement("span", { className: "alt-board-count", "aria-label": `${count} ${count === 1 ? "card" : "cards"}` }, count), collapsible && /* @__PURE__ */ React11.createElement(
3330
3465
  Button,
3331
3466
  {
3332
3467
  variant: "ghost",
@@ -3338,20 +3473,20 @@ function BoardColumn({ id, title, collapsed, defaultCollapsed = false, onCollaps
3338
3473
  "data-collapsed": isCollapsed ? "" : void 0,
3339
3474
  onClick: () => setCollapsed(!isCollapsed)
3340
3475
  },
3341
- /* @__PURE__ */ React10.createElement(Icons.ChevronDown, null)
3476
+ /* @__PURE__ */ React11.createElement(Icons.ChevronDown, null)
3342
3477
  )),
3343
- isCollapsed ? /* @__PURE__ */ React10.createElement(Pile, { count: Math.max(count, 1), className: "alt-board-pile" }, /* @__PURE__ */ React10.createElement("div", { className: "alt-board-pile-face" }, /* @__PURE__ */ React10.createElement("b", null, title), /* @__PURE__ */ React10.createElement("span", null, count ? `${count} ${count === 1 ? "card" : "cards"}` : "No cards"))) : /* @__PURE__ */ React10.createElement("div", { className: "alt-board-list", style: receiving ? { paddingBottom: carry.h + carry.gap } : void 0 }, carry && carry.to.column === id && /* @__PURE__ */ React10.createElement("span", { className: "alt-board-slot", "aria-hidden": "true", style: { top: carry.slotTop, height: carry.h } }), children, !count && !carry && /* @__PURE__ */ React10.createElement("div", { className: "alt-board-empty" }, "No cards"))
3478
+ isCollapsed ? /* @__PURE__ */ React11.createElement(Pile, { count: Math.max(count, 1), className: "alt-board-pile" }, /* @__PURE__ */ React11.createElement("div", { className: "alt-board-pile-face" }, /* @__PURE__ */ React11.createElement("b", null, title), /* @__PURE__ */ React11.createElement("span", null, count ? `${count} ${count === 1 ? "card" : "cards"}` : "No cards"))) : /* @__PURE__ */ React11.createElement("div", { className: "alt-board-list", style: receiving ? { paddingBottom: carry.h + carry.gap } : void 0 }, carry && carry.to.column === id && /* @__PURE__ */ React11.createElement("span", { className: "alt-board-slot", "aria-hidden": "true", style: { top: carry.slotTop, height: carry.h } }), children, !count && !carry && /* @__PURE__ */ React11.createElement("div", { className: "alt-board-empty" }, "No cards"))
3344
3479
  );
3345
3480
  }
3346
- var BoardCard = React10.forwardRef(function BoardCard2({ id, label, heldBy, settle, className, style, onClickCapture, children, ...rest }, ref) {
3347
- const ctx = React10.useContext(BoardCtx);
3481
+ var BoardCard = React11.forwardRef(function BoardCard2({ id, label, heldBy, settle, className, style, onClickCapture, children, ...rest }, ref) {
3482
+ const ctx = React11.useContext(BoardCtx);
3348
3483
  const depth = useDepth({ heldBy, settle });
3349
3484
  const carry = ctx?.carry;
3350
3485
  const following = carry?.id === id;
3351
3486
  const carried = following || ctx?.kb?.id === id;
3352
3487
  const shift = carry?.shifts[id];
3353
3488
  const transform = following ? `translate(${carry.dx}px, ${carry.dy}px)` : shift ? `translateY(${shift}px)` : void 0;
3354
- return /* @__PURE__ */ React10.createElement(
3489
+ return /* @__PURE__ */ React11.createElement(
3355
3490
  "div",
3356
3491
  {
3357
3492
  ref,