@tensor_1/react 2.21.0 → 2.23.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.
@@ -4,7 +4,7 @@ import { HTMLAttributes, ReactNode } from 'react';
4
4
  interface AvatarProps extends HTMLAttributes<HTMLSpanElement> {
5
5
  /** TENSOR RX-125 (UIX-04): translatable; English default. */
6
6
  onlineLabel?: string;
7
- size?: 'sm' | 'md' | 'lg';
7
+ size?: 'sm' | 'md' | 'lg' | 'xl' | '2xl';
8
8
  src?: string;
9
9
  alt?: string;
10
10
  /** Initials / fallback shown when there is no `src`. */
@@ -30,6 +30,7 @@ declare const PATHS: {
30
30
  readonly sourceMode: "M10 12.5 8 15l2 2.5M14 12.5l2 2.5-2 2.5M14 2v4a2 2 0 0 0 2 2h4M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z";
31
31
  readonly richMode: "M14 2v4a2 2 0 0 0 2 2h4M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7ZM8 13h8M8 17h5";
32
32
  readonly clock: "M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20ZM12 6v6l4 2";
33
+ readonly grip: "M9 5h.01M9 12h.01M9 19h.01M15 5h.01M15 12h.01M15 19h.01";
33
34
  };
34
35
  type EditorIconName = keyof typeof PATHS;
35
36
  declare function EditorIcon({ name, ...props }: {
@@ -27,7 +27,8 @@ const PATHS = {
27
27
  redo: "m15 14 5-5-5-5M20 9H9.5a5.5 5.5 0 0 0 0 11H13",
28
28
  sourceMode: "M10 12.5 8 15l2 2.5M14 12.5l2 2.5-2 2.5M14 2v4a2 2 0 0 0 2 2h4M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",
29
29
  richMode: "M14 2v4a2 2 0 0 0 2 2h4M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7ZM8 13h8M8 17h5",
30
- clock: "M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20ZM12 6v6l4 2"
30
+ clock: "M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20ZM12 6v6l4 2",
31
+ grip: "M9 5h.01M9 12h.01M9 19h.01M15 5h.01M15 12h.01M15 19h.01"
31
32
  };
32
33
  function EditorIcon({ name, ...props }) {
33
34
  return /* @__PURE__ */ jsx(
@@ -55,14 +55,26 @@ interface SavedViewItem {
55
55
  active?: boolean;
56
56
  pinned?: boolean;
57
57
  }
58
- interface SavedViewMenuBaseProps {
58
+ /** A titled group of saved views, e.g. the user's custom views above the built-in presets. */
59
+ interface SavedViewSection {
60
+ id: string;
61
+ label: ReactNode;
59
62
  items: readonly SavedViewItem[];
63
+ }
64
+ interface SavedViewMenuBaseProps {
60
65
  onSelect: (id: string) => void;
61
66
  emptyLabel?: ReactNode;
67
+ /** The row's overflow slot (e.g. a ⋯ menu trigger). It stays quiet until the row is hovered or focused, or its menu is open. */
62
68
  actions?: (item: SavedViewItem) => ReactNode;
63
69
  footer?: ReactNode;
64
70
  }
65
71
  type SavedViewMenuProps = SavedViewMenuBaseProps & ({
72
+ items: readonly SavedViewItem[];
73
+ sections?: never;
74
+ } | {
75
+ sections: readonly SavedViewSection[];
76
+ items?: never;
77
+ }) & ({
66
78
  onPinChange: (id: string, pinned: boolean) => void;
67
79
  pinLabel: string;
68
80
  unpinLabel: string;
@@ -70,8 +82,24 @@ type SavedViewMenuProps = SavedViewMenuBaseProps & ({
70
82
  onPinChange?: undefined;
71
83
  pinLabel?: never;
72
84
  unpinLabel?: never;
85
+ }) & ({
86
+ /**
87
+ * Adds a drag grip to every row. Rows move only within their own section; the
88
+ * callback receives that section's full id order (and its id when `sections` is used).
89
+ * The grip also moves its row with ArrowUp / ArrowDown.
90
+ */
91
+ onReorder: (orderedIds: string[], sectionId: string | undefined) => void;
92
+ /** Accessible name of the grip; the row's name is attached as its description. */
93
+ reorderLabel: string;
94
+ } | {
95
+ onReorder?: undefined;
96
+ reorderLabel?: never;
73
97
  });
74
- /** Generic saved-view selector. Persistence, ownership and authorization remain application concerns. */
75
- declare function SavedViewMenu({ items, onSelect, onPinChange, pinLabel, unpinLabel, emptyLabel, actions, footer }: SavedViewMenuProps): react.JSX.Element;
98
+ /**
99
+ * Generic saved-view selector: optional titled sections inside one `.uix-menu`, rows of
100
+ * grip · name · overflow. The selected row is tinted as a whole (no check glyph). Persistence,
101
+ * ownership, permissions and labels remain application concerns.
102
+ */
103
+ declare function SavedViewMenu({ items, sections, onSelect, onPinChange, pinLabel, unpinLabel, onReorder, reorderLabel, emptyLabel, actions, footer }: SavedViewMenuProps): react.JSX.Element;
76
104
 
77
- export { type FilterOption, FilterPopover, type FilterPopoverProps, type SavedViewItem, SavedViewMenu, type SavedViewMenuProps, ViewMenu, type ViewMenuColumn, type ViewMenuProps };
105
+ export { type FilterOption, FilterPopover, type FilterPopoverProps, type SavedViewItem, SavedViewMenu, type SavedViewMenuProps, type SavedViewSection, ViewMenu, type ViewMenuColumn, type ViewMenuProps };
@@ -1,8 +1,9 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { useId } from "react";
3
+ import { useEffect, useId, useRef, useState } from "react";
4
4
  import { Button } from "./Button.js";
5
5
  import { Checkbox } from "./Checkbox.js";
6
+ import { EditorIcon } from "./EditorIcons.js";
6
7
  import { Input } from "./Input.js";
7
8
  import { Segmented, SegmentedOption } from "./Segmented.js";
8
9
  import { Switch } from "./Switch.js";
@@ -43,14 +44,103 @@ function FilterPopover({ label, type = "text", value, onValueChange, options, ap
43
44
  ] })
44
45
  ] });
45
46
  }
46
- function SavedViewMenu({ items, onSelect, onPinChange, pinLabel, unpinLabel, emptyLabel, actions, footer }) {
47
- return /* @__PURE__ */ jsxs("ul", { className: "uix-menu uix-saved-views", children: [
48
- items.length === 0 && emptyLabel != null && /* @__PURE__ */ jsx("li", { className: "uix-menu__label", children: emptyLabel }),
49
- items.map((item) => /* @__PURE__ */ jsxs("li", { className: "uix-saved-views__row", children: [
50
- /* @__PURE__ */ jsx("button", { type: "button", className: "uix-menu__item", "data-active": item.active || void 0, onClick: () => onSelect(item.id), children: item.label }),
51
- onPinChange && /* @__PURE__ */ jsx("button", { type: "button", className: "uix-saved-views__pin", "aria-label": item.pinned ? unpinLabel : pinLabel, "data-on": item.pinned || void 0, onClick: () => onPinChange(item.id, !item.pinned), children: "\u2605" }),
52
- actions?.(item)
53
- ] }, item.id)),
47
+ const sameOrder = (a, b) => a.length === b.length && a.every((id, i) => id === b[i]);
48
+ function placeId(ids, id, index) {
49
+ const from = ids.indexOf(id);
50
+ const to = Math.max(0, Math.min(index, ids.length - 1));
51
+ if (from < 0 || from === to) return ids;
52
+ const next = ids.filter((other) => other !== id);
53
+ next.splice(to, 0, id);
54
+ return next;
55
+ }
56
+ function SavedViewMenu({ items, sections, onSelect, onPinChange, pinLabel, unpinLabel, onReorder, reorderLabel, emptyLabel, actions, footer }) {
57
+ const baseId = useId();
58
+ const groups = sections ?? [{ id: void 0, items: items ?? [] }];
59
+ const [draft, setDraft] = useState(null);
60
+ const [dragging, setDragging] = useState(null);
61
+ const dragStart = useRef(null);
62
+ const orderKey = JSON.stringify(groups.map((group) => [group.id, group.items.map((item) => item.id)]));
63
+ useEffect(() => setDraft(null), [orderKey]);
64
+ const orderOf = (group) => draft && draft.group === group.id ? draft.ids : group.items.map((item) => item.id);
65
+ const move = (group, id, delta) => {
66
+ const current = orderOf(group);
67
+ const next = placeId(current, id, current.indexOf(id) + delta);
68
+ if (next === current) return;
69
+ setDraft({ group: group.id, ids: next });
70
+ onReorder?.([...next], group.id);
71
+ };
72
+ const dragOver = (event, group, id) => {
73
+ if (dragging !== id) return;
74
+ const rows = Array.from(event.currentTarget.closest("ul")?.querySelectorAll(":scope > .uix-saved-views__row") ?? []);
75
+ let index = rows.length - 1;
76
+ for (let i = 0; i < rows.length; i++) {
77
+ const box = rows[i].getBoundingClientRect();
78
+ if (event.clientY < box.top + box.height / 2) {
79
+ index = i;
80
+ break;
81
+ }
82
+ }
83
+ const current = orderOf(group);
84
+ const next = placeId(current, id, index);
85
+ if (next !== current) setDraft({ group: group.id, ids: next });
86
+ };
87
+ const drop = (group, id) => {
88
+ if (dragging !== id) return;
89
+ setDragging(null);
90
+ const start = dragStart.current;
91
+ dragStart.current = null;
92
+ const end = orderOf(group);
93
+ if (start && !sameOrder(start, end)) onReorder?.([...end], group.id);
94
+ };
95
+ const visible = groups.filter((group) => group.items.length > 0);
96
+ const renderRows = (group, groupIndex) => {
97
+ const byId = new Map(group.items.map((item) => [item.id, item]));
98
+ return orderOf(group).map((id, index) => {
99
+ const item = byId.get(id);
100
+ if (!item) return null;
101
+ const nameId = `${baseId}-${groupIndex}-${index}`;
102
+ const extra = actions?.(item);
103
+ return /* @__PURE__ */ jsxs("li", { className: "uix-saved-views__row", "data-active": item.active || void 0, "data-dragging": dragging === item.id || void 0, children: [
104
+ onReorder && /* @__PURE__ */ jsx(
105
+ "button",
106
+ {
107
+ type: "button",
108
+ className: "uix-saved-views__grip",
109
+ "aria-label": reorderLabel,
110
+ "aria-describedby": nameId,
111
+ onPointerDown: (event) => {
112
+ if (event.button !== 0) return;
113
+ event.preventDefault();
114
+ event.currentTarget.setPointerCapture?.(event.pointerId);
115
+ dragStart.current = orderOf(group);
116
+ setDragging(item.id);
117
+ },
118
+ onPointerMove: (event) => dragOver(event, group, item.id),
119
+ onPointerUp: () => drop(group, item.id),
120
+ onPointerCancel: () => drop(group, item.id),
121
+ onKeyDown: (event) => {
122
+ if (event.key !== "ArrowUp" && event.key !== "ArrowDown") return;
123
+ event.preventDefault();
124
+ move(group, item.id, event.key === "ArrowUp" ? -1 : 1);
125
+ },
126
+ children: /* @__PURE__ */ jsx(EditorIcon, { name: "grip" })
127
+ }
128
+ ),
129
+ /* @__PURE__ */ jsx("button", { type: "button", id: nameId, className: "uix-menu__item", "data-active": item.active || void 0, "aria-current": item.active || void 0, title: typeof item.label === "string" ? item.label : void 0, onClick: () => onSelect(item.id), children: /* @__PURE__ */ jsx("span", { className: "uix-saved-views__name", children: item.label }) }),
130
+ onPinChange && /* @__PURE__ */ jsx("button", { type: "button", className: "uix-saved-views__pin", "aria-label": item.pinned ? unpinLabel : pinLabel, "data-on": item.pinned || void 0, onClick: () => onPinChange(item.id, !item.pinned), children: "\u2605" }),
131
+ extra != null && extra !== false && /* @__PURE__ */ jsx("span", { className: "uix-saved-views__actions", children: extra })
132
+ ] }, item.id);
133
+ });
134
+ };
135
+ return /* @__PURE__ */ jsxs("ul", { className: "uix-menu uix-saved-views", "data-dragging": dragging ? "" : void 0, children: [
136
+ visible.length === 0 && emptyLabel != null && /* @__PURE__ */ jsx("li", { className: "uix-menu__label", children: emptyLabel }),
137
+ sections ? visible.map((group, groupIndex) => {
138
+ const labelId = `${baseId}-section-${groupIndex}`;
139
+ return /* @__PURE__ */ jsxs("li", { className: "uix-saved-views__section", children: [
140
+ /* @__PURE__ */ jsx("div", { id: labelId, className: "uix-menu__label uix-saved-views__section-label", children: group.label }),
141
+ /* @__PURE__ */ jsx("ul", { className: "uix-saved-views__group", "aria-labelledby": labelId, children: renderRows(group, groupIndex) })
142
+ ] }, group.id);
143
+ }) : visible[0] && renderRows(visible[0], 0),
54
144
  footer != null && /* @__PURE__ */ jsx("li", { className: "uix-saved-views__footer", children: footer })
55
145
  ] });
56
146
  }
package/dist/emoji.cjs CHANGED
@@ -664,7 +664,8 @@ var PATHS = {
664
664
  redo: "m15 14 5-5-5-5M20 9H9.5a5.5 5.5 0 0 0 0 11H13",
665
665
  sourceMode: "M10 12.5 8 15l2 2.5M14 12.5l2 2.5-2 2.5M14 2v4a2 2 0 0 0 2 2h4M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",
666
666
  richMode: "M14 2v4a2 2 0 0 0 2 2h4M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7ZM8 13h8M8 17h5",
667
- clock: "M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20ZM12 6v6l4 2"
667
+ clock: "M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20ZM12 6v6l4 2",
668
+ grip: "M9 5h.01M9 12h.01M9 19h.01M15 5h.01M15 12h.01M15 19h.01"
668
669
  };
669
670
  function EditorIcon({ name, ...props }) {
670
671
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(