wenay-react2 1.0.34 → 1.0.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +12 -56
  2. package/lib/common/api.d.ts +28 -28
  3. package/lib/common/api.js +31 -31
  4. package/lib/common/src/components/Buttons/MiniButton.d.ts +1 -3
  5. package/lib/common/src/components/Buttons/MiniButton.js +1 -3
  6. package/lib/common/src/components/Dnd/{RNDFunc.d.ts → DragArea.d.ts} +1 -1
  7. package/lib/common/src/components/Dnd/{RNDFunc.js → DragArea.js} +1 -1
  8. package/lib/common/src/components/Dnd/{RNDFunc3.d.ts → FloatingWindow.d.ts} +7 -7
  9. package/lib/common/src/components/Dnd/{RNDFunc3.js → FloatingWindow.js} +10 -10
  10. package/lib/common/src/components/Dnd/OutlineDragDemo.d.ts +1 -0
  11. package/lib/common/src/components/Dnd/{DraggableOutlineDiv.js → OutlineDragDemo.js} +1 -2
  12. package/lib/common/src/components/Dnd/Resizable.js +1 -1
  13. package/lib/common/src/components/Dnd/index.d.ts +3 -3
  14. package/lib/common/src/components/Dnd/index.js +3 -3
  15. package/lib/common/src/components/Input.d.ts +5 -5
  16. package/lib/common/src/components/Input.js +10 -10
  17. package/lib/common/src/components/Menu/RightMenu.d.ts +25 -5
  18. package/lib/common/src/components/Menu/RightMenu.js +27 -19
  19. package/lib/common/src/components/Menu/RightMenuStore.d.ts +2 -2
  20. package/lib/common/src/components/Menu/RightMenuStore.js +1 -1
  21. package/lib/common/src/components/Modal/LeftModal.js +4 -4
  22. package/lib/common/src/components/Modal/Modal.d.ts +11 -17
  23. package/lib/common/src/components/Modal/Modal.js +15 -21
  24. package/lib/common/src/components/Modal/ModalContextProvider.d.ts +0 -6
  25. package/lib/common/src/components/Modal/ModalContextProvider.js +2 -9
  26. package/lib/common/src/components/Other.d.ts +2 -2
  27. package/lib/common/src/components/Other.js +5 -5
  28. package/lib/common/src/components/Parameters.d.ts +3 -3
  29. package/lib/common/src/components/Parameters.js +3 -3
  30. package/lib/common/src/components/{ParametersEngine.d.ts → ParamsEditor.d.ts} +1 -1
  31. package/lib/common/src/components/{ParametersEngine.js → ParamsEditor.js} +10 -10
  32. package/lib/common/src/components/Settings/SettingsDialog.d.ts +4 -4
  33. package/lib/common/src/components/Toolbar/Toolbar.d.ts +50 -37
  34. package/lib/common/src/components/Toolbar/Toolbar.js +60 -23
  35. package/lib/common/src/components/UiSlot/UiSlot.d.ts +3 -3
  36. package/lib/common/src/components/UiSlot/UiSlot.js +6 -6
  37. package/lib/common/src/grid/agGrid4/agGrid4.d.ts +4 -4
  38. package/lib/common/src/grid/agGrid4/agGrid4.js +6 -6
  39. package/lib/common/src/grid/agGrid4/index.d.ts +3 -3
  40. package/lib/common/src/grid/agGrid4/index.js +1 -1
  41. package/lib/common/src/grid/agGrid4/theme.d.ts +3 -3
  42. package/lib/common/src/grid/columnState/CardList.d.ts +11 -0
  43. package/lib/common/src/grid/columnState/CardList.js +28 -0
  44. package/lib/common/src/grid/columnState/ColumnDots.d.ts +9 -0
  45. package/lib/common/src/grid/columnState/ColumnDots.js +121 -0
  46. package/lib/common/src/grid/columnState/ColumnsMenu.d.ts +65 -0
  47. package/lib/common/src/grid/columnState/ColumnsMenu.js +123 -0
  48. package/lib/common/src/grid/columnState/columnState.d.ts +113 -0
  49. package/lib/common/src/grid/columnState/columnState.js +284 -0
  50. package/lib/common/src/grid/columnState/index.d.ts +4 -0
  51. package/lib/common/src/grid/columnState/index.js +4 -0
  52. package/lib/common/src/hooks/index.d.ts +1 -1
  53. package/lib/common/src/hooks/index.js +1 -1
  54. package/lib/common/src/hooks/useKeyboard.d.ts +18 -0
  55. package/lib/common/src/hooks/{useAddDownAnyKey.js → useKeyboard.js} +16 -24
  56. package/lib/common/src/hooks/useObserveStore.d.ts +26 -11
  57. package/lib/common/src/hooks/useObserveStore.js +21 -2
  58. package/lib/common/src/hooks/useOutside.d.ts +12 -19
  59. package/lib/common/src/hooks/useOutside.js +9 -16
  60. package/lib/common/src/hooks/useReorder.d.ts +4 -4
  61. package/lib/common/src/hooks/useReorderBoard.d.ts +15 -15
  62. package/lib/common/src/hooks/useReplay.d.ts +33 -6
  63. package/lib/common/src/hooks/useReplay.js +32 -5
  64. package/lib/common/src/logs/logs.d.ts +3 -3
  65. package/lib/common/src/logs/logs.js +11 -13
  66. package/lib/common/src/logs/{logs3.d.ts → logsContext.d.ts} +4 -4
  67. package/lib/common/src/logs/{logs3.js → logsContext.js} +4 -4
  68. package/lib/common/src/menu/menu.d.ts +16 -16
  69. package/lib/common/src/menu/menu.js +12 -11
  70. package/lib/common/src/menu/menuMouse.d.ts +71 -13
  71. package/lib/common/src/menu/menuMouse.js +189 -21
  72. package/lib/common/src/menu/menuR.d.ts +3 -3
  73. package/lib/common/src/menu/menuR.js +5 -5
  74. package/lib/common/src/styles/styleGrid.d.ts +1 -1
  75. package/lib/common/src/styles/tokens.d.ts +1 -1
  76. package/lib/common/src/styles/tokens.js +1 -1
  77. package/lib/common/src/utils/cache.d.ts +11 -11
  78. package/lib/common/src/utils/cache.js +0 -0
  79. package/lib/common/src/utils/gridRows.d.ts +37 -0
  80. package/lib/common/src/utils/{applyTransactionAsyncUpdate.js → gridRows.js} +6 -38
  81. package/lib/common/src/utils/index.d.ts +2 -2
  82. package/lib/common/src/utils/index.js +2 -2
  83. package/lib/common/src/utils/inputAutoStep.d.ts +2 -2
  84. package/lib/common/src/utils/inputAutoStep.js +26 -37
  85. package/lib/common/src/utils/{mapMemory.d.ts → memoryStore.d.ts} +13 -13
  86. package/lib/common/src/utils/{mapMemory.js → memoryStore.js} +32 -32
  87. package/lib/common/src/utils/observableMap.d.ts +5 -5
  88. package/lib/common/src/utils/observableMap.js +3 -3
  89. package/lib/common/updateBy.js +2 -2
  90. package/lib/style/style.css +3 -3
  91. package/lib/style/tokens.css +1 -1
  92. package/package.json +49 -49
  93. package/lib/common/src/components/Dnd/DraggableOutlineDiv.d.ts +0 -2
  94. package/lib/common/src/hooks/useAddDownAnyKey.d.ts +0 -24
  95. package/lib/common/src/utils/applyTransactionAsyncUpdate.d.ts +0 -53
@@ -0,0 +1,121 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ // ColumnDots - the mobile column selector: a track of marks (one per column)
3
+ // with dots on the columns that are currently shown. Placing a dot IS showing
4
+ // the column; the card view (CardList) rebuilds from the same config live.
5
+ // Gestures (pointer events, mouse + touch):
6
+ // drag a dot along the track -> the dot slides to another (empty) mark: that
7
+ // column replaces the old one
8
+ // swipe a dot UP (flick off) -> the dot tears off the track: column hidden
9
+ // tap an empty mark -> a dot appears there: column shown
10
+ // tap a dot (no move) -> select the field (highlight); the sort
11
+ // button cycles asc -> desc -> off on the
12
+ // SELECTED field. Sort is sticky: selecting
13
+ // another dot does not touch it, and it may
14
+ // point at a hidden column.
15
+ // No ag-grid, no storage - only the columnState config.
16
+ import { useRef, useState } from 'react';
17
+ /** Dominant-axis thresholds: a gesture is a REMOVE only when it is clearly
18
+ * vertical and clearly upward - a horizontal drag or a page scroll never is. */
19
+ const REMOVE_DY = 32;
20
+ const MOVE_SLOP = 4;
21
+ export function ColumnDots(p) {
22
+ const cfg = p.state.api.useConfig();
23
+ const cols = p.state.columns;
24
+ const byKey = new Map(cols.map(c => [c.key, c]));
25
+ const max = p.max ?? 4;
26
+ const order = cfg.order;
27
+ const n = order.length;
28
+ const visibleKeys = order.filter(k => cfg.visible[k] != false);
29
+ const trackRef = useRef(null);
30
+ const gestureRef = useRef(null);
31
+ const [drag, setDrag] = useState(null);
32
+ const [selected, setSelected] = useState(null);
33
+ const pct = (i) => n <= 1 ? 50 : (i * 100) / (n - 1);
34
+ const short = (k) => k ? (byKey.get(k)?.short ?? byKey.get(k)?.title ?? k) : '';
35
+ function downDot(key, e) {
36
+ e.preventDefault();
37
+ e.currentTarget.setPointerCapture(e.pointerId);
38
+ gestureRef.current = { key, startX: e.clientX, startY: e.clientY, moved: false };
39
+ }
40
+ function moveDot(e) {
41
+ const g = gestureRef.current;
42
+ if (!g)
43
+ return;
44
+ const dx = e.clientX - g.startX;
45
+ const dy = e.clientY - g.startY;
46
+ if (!g.moved && Math.hypot(dx, dy) < MOVE_SLOP)
47
+ return;
48
+ g.moved = true;
49
+ const r = trackRef.current?.getBoundingClientRect();
50
+ if (!r)
51
+ return;
52
+ const step = n > 1 ? r.width / (n - 1) : r.width;
53
+ const from = order.indexOf(g.key);
54
+ const off = -dy > REMOVE_DY && -dy > Math.abs(dx);
55
+ const to = Math.max(0, Math.min(n - 1, Math.round(from + dx / step)));
56
+ setDrag({ key: g.key, dx, dy, to, off });
57
+ }
58
+ function upDot() {
59
+ const g = gestureRef.current;
60
+ gestureRef.current = null;
61
+ const st = drag;
62
+ setDrag(null);
63
+ if (!g)
64
+ return;
65
+ if (!g.moved) { // tap = select the field
66
+ setSelected(g.key);
67
+ return;
68
+ }
69
+ if (!st)
70
+ return;
71
+ const meta = byKey.get(g.key);
72
+ if (st.off) { // swipe up = tear the dot off (fixed and the last dot stay)
73
+ if (!meta?.fixed && visibleKeys.length > 1) {
74
+ p.state.api.show(g.key, false);
75
+ if (selected == g.key)
76
+ setSelected(null);
77
+ }
78
+ return;
79
+ }
80
+ const target = order[st.to];
81
+ if (target && target != g.key && cfg.visible[target] == false && !meta?.fixed) {
82
+ // the dot slides to another mark: that column takes this slot
83
+ p.state.api.setConfig({ ...cfg, visible: { ...cfg.visible, [g.key]: false, [target]: true } });
84
+ if (selected == g.key)
85
+ setSelected(target);
86
+ }
87
+ }
88
+ function tapMark(key) {
89
+ if (cfg.visible[key] != false)
90
+ return;
91
+ if (visibleKeys.length >= max)
92
+ return;
93
+ p.state.api.show(key, true);
94
+ setSelected(key);
95
+ }
96
+ const sortLabel = cfg.sort ? `${short(cfg.sort.key)} ${cfg.sort.dir == 'asc' ? '↑' : '↓'}` : 'off';
97
+ return _jsxs("div", { className: p.className, style: { userSelect: 'none', ...p.style }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 10, marginBottom: 6, fontSize: 12 }, children: [_jsxs("span", { style: { color: '#57606a' }, children: [visibleKeys.length, "/", max, " fields"] }), _jsx("span", { style: { flex: 1 } }), _jsxs("span", { style: { color: '#57606a' }, children: ["field: ", _jsx("b", { children: selected ? short(selected) : '—' })] }), _jsxs("button", { disabled: !selected, title: 'Sort by the selected field: asc -> desc -> off', onClick: () => selected && p.state.api.toggleSort(selected), style: { border: '1px solid #6e7781', borderRadius: 6, padding: '2px 8px', fontSize: 12, cursor: selected ? 'pointer' : 'default', opacity: selected ? 1 : 0.5, background: '#fff' }, children: ["\u21C5 sort: ", sortLabel] })] }), _jsxs("div", { ref: trackRef, style: { position: 'relative', height: 56, margin: '0 14px', touchAction: 'none' }, children: [_jsx("div", { style: { position: 'absolute', left: -8, right: -8, top: 27, height: 2, background: '#d0d7de', borderRadius: 1 } }), order.map((k, i) => {
98
+ const vis = cfg.visible[k] != false;
99
+ const isSorted = cfg.sort?.key == k;
100
+ return _jsxs("div", { onPointerUp: () => tapMark(k), style: { position: 'absolute', left: `${pct(i)}%`, top: 0, width: 44, height: 56, marginLeft: -22, cursor: !vis && visibleKeys.length < max ? 'pointer' : 'default' }, children: [isSorted && _jsx("div", { style: { position: 'absolute', left: 0, right: 0, top: 0, textAlign: 'center', fontSize: 11, color: '#0969da', fontWeight: 700 }, children: cfg.sort.dir == 'asc' ? '↑' : '↓' }), _jsx("div", { style: { position: 'absolute', left: '50%', top: 24, width: 8, height: 8, marginLeft: -4, borderRadius: 4, background: vis ? 'transparent' : '#afb8c1' } }), _jsx("div", { style: { position: 'absolute', left: 0, right: 0, top: 40, textAlign: 'center', fontSize: 10, color: vis ? '#24292f' : '#8c959f', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: short(k) })] }, 'm' + k);
101
+ }), order.map((k, i) => {
102
+ if (cfg.visible[k] == false)
103
+ return null;
104
+ const meta = byKey.get(k);
105
+ const d = drag?.key == k ? drag : null;
106
+ const removing = !!d?.off && !meta?.fixed && visibleKeys.length > 1;
107
+ return _jsx("div", { onPointerDown: e => downDot(k, e), onPointerMove: moveDot, onPointerUp: upDot, onPointerCancel: upDot, style: {
108
+ position: 'absolute', left: `${pct(i)}%`, top: 28, width: 44, height: 44,
109
+ margin: '-22px 0 0 -22px', display: 'flex', alignItems: 'center', justifyContent: 'center',
110
+ cursor: 'grab', zIndex: d ? 3 : 2,
111
+ transform: d ? `translate(${d.dx}px, ${removing ? d.dy : 0}px)` : undefined,
112
+ transition: d ? undefined : 'transform 0.15s ease',
113
+ opacity: removing ? 0.4 : 1,
114
+ }, children: _jsx("div", { style: {
115
+ width: 18, height: 18, borderRadius: 9,
116
+ background: selected == k ? '#0969da' : '#24292f',
117
+ border: meta?.fixed ? '2px solid #afb8c1' : selected == k ? '2px solid #b6d4fe' : '2px solid transparent',
118
+ boxShadow: d ? '0 3px 10px rgba(0,0,0,0.35)' : undefined,
119
+ } }) }, 'd' + k);
120
+ })] })] });
121
+ }
@@ -0,0 +1,65 @@
1
+ import React from 'react';
2
+ import type { ColumnStateController, ColumnsConfig } from './columnState';
3
+ export type MenuStripItem = {
4
+ /** stable id; reported back on click / in the reorder commit */
5
+ key: string;
6
+ /** full name - tooltip / accessible label */
7
+ title: string;
8
+ /** caption on the button (falls back to title) */
9
+ short?: string;
10
+ icon?: React.ReactNode;
11
+ /** 'disabled' = the slot exists but its target is gone (column removed
12
+ * from the live grid): the button stays in place, inert */
13
+ state: 'on' | 'off' | 'disabled';
14
+ /** adornment for "on with extras": sub-column dots, mode marks... */
15
+ marks?: React.ReactNode;
16
+ /** cannot be dragged (fixed columns) */
17
+ fixed?: boolean;
18
+ };
19
+ /** The presentation layer: renders the buttons in the given order, reports
20
+ * clicks (onItem) and drag-reorders (onMove) - never interprets either.
21
+ * Disabled buttons do not report clicks; fixed ones do not drag. */
22
+ export declare function MenuStrip(p: {
23
+ items: MenuStripItem[];
24
+ /** a click happened on this key - the MEANING lives a layer up */
25
+ onItem?: (key: string, e: React.MouseEvent) => void;
26
+ /** present = items are drag-reorderable (one commit per drop) */
27
+ onMove?: (order: string[]) => void;
28
+ /** preview rule for the drop (fixed pinning etc.) - same contract as
29
+ * useReorder.move, so the preview shows exactly what onMove receives */
30
+ move?: (order: string[], key: string, to: number) => string[];
31
+ /** trailing buttons after a divider, OUTSIDE the reorder list (mode
32
+ * cyclers, actions...); their clicks go to onItem like any other key */
33
+ tail?: MenuStripItem[];
34
+ /** hold before a touch drag starts, ms (default 150 - keeps page scroll usable) */
35
+ holdMs?: number;
36
+ /** icon-only buttons (letters stand in for a missing icon) */
37
+ compact?: boolean;
38
+ className?: string;
39
+ style?: React.CSSProperties;
40
+ }): import("react/jsx-runtime").JSX.Element;
41
+ /** The binding layer: MenuStrip fed by a ColumnStateController. Buttons follow
42
+ * config.order (the grid mirror is free: both sides subscribe to the same
43
+ * config); state per button = disabled (column absent from the live grid) /
44
+ * on / off. Default click toggles visibility; pass onItem for richer,
45
+ * multi-state semantics - the strip below stays untouched. */
46
+ export declare function ColumnsMenu(p: {
47
+ state: ColumnStateController;
48
+ /** what a click MEANS - a separate layer by design. Default: toggle the
49
+ * column's visibility (fixed columns ignore the default). */
50
+ onItem?: (key: string, e: React.MouseEvent) => void;
51
+ /** "on with extras" adornment per column (enabled sub-columns, strategies...) */
52
+ marks?: (key: string, cfg: ColumnsConfig) => React.ReactNode;
53
+ /** trailing non-column buttons (table standards cycler etc.) */
54
+ tail?: MenuStripItem[];
55
+ /** clicks on tail buttons */
56
+ onTail?: (key: string, e: React.MouseEvent) => void;
57
+ /** drag reorder of the column buttons, mirrored to the grid (default on) */
58
+ reorder?: boolean;
59
+ holdMs?: number;
60
+ /** icon-only buttons: the column's icon, or the first letters of its
61
+ * short/title as a text pseudo-icon (full title in the tooltip) */
62
+ compact?: boolean;
63
+ className?: string;
64
+ style?: React.CSSProperties;
65
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,123 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ // ColumnsMenu - the icon menu over a columnState config: one button per
3
+ // column, a 1:1 mirror of the grid. Reorder a column in the grid - the buttons
4
+ // reorder; drag a button - the grid reorders. Two DECOUPLED layers, because
5
+ // "what a click means" is deliberately not this strip's business:
6
+ //
7
+ // MenuStrip - pure presentation. Ordered buttons, each in one of three
8
+ // states ('on' / 'off' / 'disabled') plus an optional
9
+ // adornment (marks) for "on with extras" (sub-columns,
10
+ // naming strategies...). It reports clicks and drag-reorders;
11
+ // it never interprets them. Reusable for ANY button strip
12
+ // with multi-state buttons (table standards cyclers etc.).
13
+ //
14
+ // ColumnsMenu - the binding to a ColumnStateController: derives the items
15
+ // from order/visibility/presence, default click = toggle the
16
+ // column, drag = api.move. The click meaning is overridable
17
+ // (onItem) - multi-state columns plug in without touching the
18
+ // strip.
19
+ //
20
+ // A column REMOVED from the live grid (dynamic defs, "drop empty columns"
21
+ // standards) keeps its button: it renders 'disabled' (dashed, inert) and
22
+ // comes back to life when the column returns. No ag-grid, no storage here.
23
+ import { useRef } from 'react';
24
+ import { useReorder } from '../../hooks/useReorder';
25
+ function MenuButton(p) {
26
+ const it = p.item;
27
+ const disabled = it.state == 'disabled';
28
+ const on = it.state == 'on';
29
+ const abbr = p.compact && it.icon == null
30
+ ? _jsx("span", { style: { fontSize: 10, fontWeight: 700, letterSpacing: 0.5, textTransform: 'uppercase' }, children: (it.short ?? it.title).slice(0, 3) }) : null;
31
+ return _jsxs("div", { title: it.title, onClick: disabled ? undefined : e => p.onItem?.(it.key, e), onMouseDown: p.drag?.onMouseDown, onTouchStart: p.drag?.onTouchStart, style: {
32
+ position: 'relative', display: 'flex', alignItems: 'center', gap: 4,
33
+ padding: '3px 9px', borderRadius: 6, fontSize: 12, lineHeight: '16px',
34
+ whiteSpace: 'nowrap', cursor: disabled ? 'default' : 'pointer',
35
+ ...(disabled ? { border: '1px dashed #d0d7de', background: '#f6f8fa', color: '#c4ccd4' }
36
+ : on ? { border: '1px solid #24292f', background: '#24292f', color: '#fff' }
37
+ : { border: '1px solid #d0d7de', background: '#fff', color: '#8c959f', textDecoration: 'line-through' }),
38
+ ...(it.fixed ? { boxShadow: '0 0 0 2px #eaeef2' } : null),
39
+ ...p.style,
40
+ }, children: [it.icon != null && _jsx("span", { style: { display: 'inline-flex', alignItems: 'center' }, children: it.icon }), abbr, !p.compact && _jsx("span", { children: it.short ?? it.title }), it.marks != null && _jsx("span", { style: { fontSize: 9, opacity: 0.9 }, children: it.marks })] });
41
+ }
42
+ /** The presentation layer: renders the buttons in the given order, reports
43
+ * clicks (onItem) and drag-reorders (onMove) - never interprets either.
44
+ * Disabled buttons do not report clicks; fixed ones do not drag. */
45
+ export function MenuStrip(p) {
46
+ const reorder = useReorder({
47
+ order: p.items.map(i => i.key),
48
+ commit: next => p.onMove?.(next),
49
+ move: p.move,
50
+ canDrag: k => !!p.onMove && !p.items.find(i => i.key == k)?.fixed,
51
+ holdMs: p.holdMs ?? 150,
52
+ });
53
+ // A drag that ends over the button it started on still produces a browser
54
+ // click - without this guard every snapped-back drag would ALSO toggle the
55
+ // button. Track the press point; a click that travelled is not a click.
56
+ const down = useRef(null);
57
+ function onItem(key, e) {
58
+ const d = down.current;
59
+ down.current = null;
60
+ if (d && Math.hypot(e.clientX - d.x, e.clientY - d.y) > 4)
61
+ return;
62
+ p.onItem?.(key, e);
63
+ }
64
+ return _jsxs("div", { className: p.className, style: { display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap', userSelect: 'none', ...p.style }, children: [_jsx("div", { ref: reorder.listRef, style: { display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }, children: p.items.map(it => {
65
+ const r = reorder.item(it.key);
66
+ const drag = {
67
+ onMouseDown: (e) => {
68
+ down.current = { x: e.clientX, y: e.clientY };
69
+ r.props.onMouseDown(e);
70
+ },
71
+ onTouchStart: r.props.onTouchStart,
72
+ };
73
+ return _jsx(MenuButton, { item: it, onItem: onItem, drag: drag, compact: p.compact, style: {
74
+ ...r.style,
75
+ ...(r.dragging ? { zIndex: 3, boxShadow: '0 3px 10px rgba(0,0,0,0.35)' }
76
+ : r.active ? { transition: 'transform 0.15s ease' } : null),
77
+ } }, it.key);
78
+ }) }), !!p.tail?.length && _jsxs(_Fragment, { children: [_jsx("div", { style: { width: 1, alignSelf: 'stretch', margin: '1px 2px', background: '#d0d7de' } }), p.tail.map(it => _jsx(MenuButton, { item: it, onItem: p.onItem, compact: p.compact }, it.key))] })] });
79
+ }
80
+ /** The binding layer: MenuStrip fed by a ColumnStateController. Buttons follow
81
+ * config.order (the grid mirror is free: both sides subscribe to the same
82
+ * config); state per button = disabled (column absent from the live grid) /
83
+ * on / off. Default click toggles visibility; pass onItem for richer,
84
+ * multi-state semantics - the strip below stays untouched. */
85
+ export function ColumnsMenu(p) {
86
+ const cfg = p.state.api.useConfig();
87
+ const present = p.state.api.usePresent();
88
+ const byKey = new Map(p.state.columns.map(c => [c.key, c]));
89
+ const items = cfg.order.filter(k => byKey.has(k)).map(k => {
90
+ const c = byKey.get(k);
91
+ return {
92
+ key: k, title: c.title, short: c.short, icon: c.icon, fixed: c.fixed,
93
+ state: present && !present[k] ? 'disabled' : cfg.visible[k] != false ? 'on' : 'off',
94
+ marks: p.marks?.(k, cfg),
95
+ };
96
+ });
97
+ function onItem(key, e) {
98
+ if (!byKey.has(key))
99
+ return p.onTail?.(key, e);
100
+ if (p.onItem)
101
+ return p.onItem(key, e);
102
+ if (byKey.get(key)?.fixed)
103
+ return;
104
+ p.state.api.show(key, cfg.visible[key] == false);
105
+ }
106
+ /** Simulated drop with the same fixed pinning as the config's normalize():
107
+ * the drag preview never disagrees with where the button actually lands. */
108
+ function movedOrder(order, key, to) {
109
+ const next = order.slice();
110
+ const from = next.indexOf(key);
111
+ if (from == -1)
112
+ return next;
113
+ next.splice(from, 1);
114
+ next.splice(Math.max(0, Math.min(next.length, to)), 0, key);
115
+ const res = next.filter(k => !byKey.get(k)?.fixed);
116
+ p.state.columns.forEach(function pinFixed(c, i) {
117
+ if (c.fixed)
118
+ res.splice(Math.min(i, res.length), 0, c.key);
119
+ });
120
+ return res;
121
+ }
122
+ return _jsx(MenuStrip, { items: items, tail: p.tail, onItem: onItem, onMove: p.reorder == false ? undefined : order => p.state.api.move(order), move: movedOrder, holdMs: p.holdMs, compact: p.compact, className: p.className, style: p.style });
123
+ }
@@ -0,0 +1,113 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { GridApi } from 'ag-grid-community';
3
+ export type ColumnMeta = {
4
+ /** stable id (persist key; must equal the grid colId) */
5
+ key: string;
6
+ /** full human name - menus, card labels */
7
+ title: string;
8
+ /** short caption for icons/cards (falls back to title) */
9
+ short?: string;
10
+ /** glyph for icon menus */
11
+ icon?: ReactNode;
12
+ /** group key for columns with sub-columns ("versions" etc.) */
13
+ group?: string;
14
+ /** cannot be hidden or reordered (pinned to its descriptor index) */
15
+ fixed?: boolean;
16
+ /** default true */
17
+ defaultVisible?: boolean;
18
+ /** role in the mobile card view: 'title' = card header, 'accent' = badge */
19
+ cardRole?: 'title' | 'accent';
20
+ };
21
+ export type ColumnsSort = {
22
+ key: string;
23
+ dir: 'asc' | 'desc';
24
+ };
25
+ export type ColumnsConfig = {
26
+ /** schema version of the persisted shape */
27
+ v: number;
28
+ /** column keys, display order */
29
+ order: string[];
30
+ visible: {
31
+ [key: string]: boolean;
32
+ };
33
+ /** column widths - written by the grid adapter only */
34
+ width: {
35
+ [key: string]: number;
36
+ };
37
+ /** STICKY sort: independent of visibility and of any UI selection, may point
38
+ * at a hidden column; changes only by an explicit toggle or a header click */
39
+ sort: ColumnsSort | null;
40
+ /** grid filterModel - written by the grid adapter only */
41
+ filter: {
42
+ [key: string]: unknown;
43
+ };
44
+ /** group key -> enabled sub-column keys */
45
+ groups: {
46
+ [group: string]: string[];
47
+ };
48
+ };
49
+ export declare function createColumnState(opts: {
50
+ /** persistence key (memoryProps -> memoryCache), like createToolbar */
51
+ key: string;
52
+ /** column descriptors; the config only ever references them by key */
53
+ columns: ColumnMeta[];
54
+ /** defaults; missing fields are derived from columns */
55
+ def?: Partial<ColumnsConfig>;
56
+ /** grid->store save debounce, ms (default 300) */
57
+ saveMs?: number;
58
+ }): {
59
+ /** The column descriptors this state was created over - UI components
60
+ * (dots, cards, icon menus) render from these + the config. */
61
+ columns: readonly ColumnMeta[];
62
+ api: {
63
+ getConfig: () => ColumnsConfig;
64
+ setConfig: (next: ColumnsConfig) => void;
65
+ useConfig: () => ColumnsConfig;
66
+ onChange: import("wenay-common2").ListenApi<[ColumnsConfig]>;
67
+ reset: () => void;
68
+ show: (key: string, on: boolean) => void;
69
+ move: (order: string[]) => void;
70
+ setSort: (sort: ColumnsSort | null) => void;
71
+ toggleSort: (key: string) => void;
72
+ visibleKeys: () => string[];
73
+ getPresent: () => {
74
+ [key: string]: true;
75
+ } | null;
76
+ usePresent: () => {
77
+ [key: string]: true;
78
+ } | null;
79
+ isPresent: (key: string) => boolean;
80
+ setPresent: (keys: string[] | null) => void;
81
+ listSource: {
82
+ useConfig(): {
83
+ order: string[];
84
+ visible: {
85
+ [key: string]: boolean;
86
+ };
87
+ };
88
+ getConfig(): {
89
+ order: string[];
90
+ visible: {
91
+ [key: string]: boolean;
92
+ };
93
+ };
94
+ setConfig(next: {
95
+ order: string[];
96
+ visible: {
97
+ [k: string]: boolean;
98
+ };
99
+ }): void;
100
+ onChange: (cb: (cfg: {
101
+ order: string[];
102
+ visible: {
103
+ [k: string]: boolean;
104
+ };
105
+ }) => void) => import("wenay-common2").ListenOff;
106
+ };
107
+ };
108
+ grid: {
109
+ attach: (api: GridApi) => void;
110
+ detach: () => void;
111
+ };
112
+ };
113
+ export type ColumnStateController = ReturnType<typeof createColumnState>;