@nexub/react 7.6.0 → 7.7.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/accessibility.json +1 -1
- package/dist/components/DataTable/DataTable.d.ts +16 -2
- package/dist/components/DataTable/DataTable.js +131 -76
- package/dist/components/DataTable/ViewOptions.d.ts +15 -0
- package/dist/components/DataTable/ViewOptions.js +33 -0
- package/dist/components/DataTableVirtual/DataTableVirtual.d.ts +1 -1
- package/dist/components/FilterBuilder/FilterBuilder.js +33 -15
- package/dist/components/Kanban/Kanban.d.ts +1 -1
- package/dist/components/Sortable/Sortable.d.ts +2 -2
- package/dist/components/Sortable/reorder.d.ts +10 -11
- package/dist/components/Sortable/reorder.js +185 -27
- package/dist/styles.css +1 -1
- package/package.json +8 -7
|
@@ -11,18 +11,69 @@ function moveItem(lists, [fc, fi], [tc, ti]) {
|
|
|
11
11
|
next[tc].splice(Math.min(ti, next[tc].length), 0, item);
|
|
12
12
|
return next;
|
|
13
13
|
}
|
|
14
|
+
const THRESHOLD = 4;
|
|
15
|
+
const EDGE = 48;
|
|
16
|
+
const MAX_SPEED = 16;
|
|
17
|
+
const itemOf = (el) => el.closest("[data-nx-reorder-item]");
|
|
18
|
+
function scrollParent(el, axis) {
|
|
19
|
+
for (let p = el.parentElement; p; p = p.parentElement) {
|
|
20
|
+
const s = getComputedStyle(p);
|
|
21
|
+
const scrolls = axis === "y" ? /auto|scroll/.test(s.overflowY) && p.scrollHeight > p.clientHeight : /auto|scroll/.test(s.overflowX) && p.scrollWidth > p.clientWidth;
|
|
22
|
+
if (scrolls) return p;
|
|
23
|
+
}
|
|
24
|
+
return document.scrollingElement;
|
|
25
|
+
}
|
|
26
|
+
function edgeSpeed(at, start, end) {
|
|
27
|
+
if (at < start + EDGE) return -MAX_SPEED * Math.min(1, (start + EDGE - at) / EDGE);
|
|
28
|
+
if (at > end - EDGE) return MAX_SPEED * Math.min(1, (at - end + EDGE) / EDGE);
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
const sameMark = (a, b) => a === b || !!a && !!b && a.edge === b.edge && ("id" in a ? "id" in b && a.id === b.id : "list" in b && a.list === b.list);
|
|
14
32
|
function useReorder({ lists, getId, getLabel, listLabels, onMove }) {
|
|
15
33
|
const t = useNexubMessages();
|
|
16
34
|
const { dir } = useLocale();
|
|
17
|
-
const dragged = React.useRef(null);
|
|
18
35
|
const handles = React.useRef(/* @__PURE__ */ new Map());
|
|
36
|
+
const listEls = React.useRef(/* @__PURE__ */ new Map());
|
|
19
37
|
const refocus = React.useRef(null);
|
|
38
|
+
const drag = React.useRef(null);
|
|
39
|
+
const flipFrom = React.useRef(null);
|
|
20
40
|
const [announcement, setAnnouncement] = React.useState("");
|
|
41
|
+
const [dragging, setDragging] = React.useState(null);
|
|
42
|
+
const [mark, setMark] = React.useState(null);
|
|
43
|
+
const itemCache = React.useRef(/* @__PURE__ */ new Map());
|
|
21
44
|
const hintId = React.useId();
|
|
22
45
|
React.useEffect(() => {
|
|
23
46
|
if (refocus.current) handles.current.get(refocus.current)?.focus();
|
|
24
47
|
refocus.current = null;
|
|
25
48
|
});
|
|
49
|
+
React.useLayoutEffect(() => {
|
|
50
|
+
const from = flipFrom.current;
|
|
51
|
+
flipFrom.current = null;
|
|
52
|
+
if (!from || window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) return;
|
|
53
|
+
for (const [id, h] of handles.current) {
|
|
54
|
+
const el = itemOf(h);
|
|
55
|
+
const old = from.get(id);
|
|
56
|
+
if (!el || !old) continue;
|
|
57
|
+
const now = el.getBoundingClientRect();
|
|
58
|
+
const dx = old.left - now.left;
|
|
59
|
+
const dy = old.top - now.top;
|
|
60
|
+
if (!dx && !dy) continue;
|
|
61
|
+
el.style.transition = "none";
|
|
62
|
+
el.style.transform = `translate(${dx}px, ${dy}px)`;
|
|
63
|
+
el.getBoundingClientRect();
|
|
64
|
+
el.style.transition = "transform var(--motion-spring-spatial-default-duration) var(--motion-spring-spatial-default-easing)";
|
|
65
|
+
el.style.transform = "";
|
|
66
|
+
el.addEventListener("transitionend", () => el.style.transition = "", { once: true });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
const snapshot = () => {
|
|
70
|
+
const rects = /* @__PURE__ */ new Map();
|
|
71
|
+
for (const [id, h] of handles.current) {
|
|
72
|
+
const el = itemOf(h);
|
|
73
|
+
if (el) rects.set(id, el.getBoundingClientRect());
|
|
74
|
+
}
|
|
75
|
+
return rects;
|
|
76
|
+
};
|
|
26
77
|
const move = (from, to, keepFocus) => {
|
|
27
78
|
if (from[0] === to[0] && from[1] === to[1]) return;
|
|
28
79
|
const item = lists[from[0]][from[1]];
|
|
@@ -30,40 +81,102 @@ function useReorder({ lists, getId, getLabel, listLabels, onMove }) {
|
|
|
30
81
|
const [tc] = to;
|
|
31
82
|
const ti = next[tc].indexOf(item);
|
|
32
83
|
if (keepFocus) refocus.current = getId(item);
|
|
84
|
+
flipFrom.current ??= snapshot();
|
|
33
85
|
onMove(next);
|
|
34
86
|
const total = next[tc].length;
|
|
35
87
|
setAnnouncement(
|
|
36
88
|
from[0] !== tc && listLabels?.[tc] ? t.itemMovedToColumn(getLabel(item), listLabels[tc], ti + 1, total) : t.itemMoved(getLabel(item), ti + 1, total)
|
|
37
89
|
);
|
|
38
90
|
};
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
onDrop: (e) => {
|
|
49
|
-
e.preventDefault();
|
|
50
|
-
e.stopPropagation();
|
|
51
|
-
if (dragged.current) move(dragged.current, pos, false);
|
|
52
|
-
dragged.current = null;
|
|
53
|
-
},
|
|
54
|
-
onDragEnd: () => {
|
|
55
|
-
dragged.current = null;
|
|
91
|
+
const targetAt = (d) => {
|
|
92
|
+
let list = d.from[0];
|
|
93
|
+
if (lists.length > 1) {
|
|
94
|
+
let best = Infinity;
|
|
95
|
+
for (const [l, el] of listEls.current) {
|
|
96
|
+
const r = el.getBoundingClientRect();
|
|
97
|
+
const dist = Math.hypot(Math.max(r.left - d.x, 0, d.x - r.right), Math.max(r.top - d.y, 0, d.y - r.bottom));
|
|
98
|
+
if (dist < best) [best, list] = [dist, l];
|
|
99
|
+
}
|
|
56
100
|
}
|
|
57
|
-
|
|
101
|
+
const others = lists[list].filter((it) => getId(it) !== d.id);
|
|
102
|
+
let k = others.findIndex((it) => {
|
|
103
|
+
const h = handles.current.get(getId(it));
|
|
104
|
+
const r = h && itemOf(h)?.getBoundingClientRect();
|
|
105
|
+
return r && d.y < r.top + r.height / 2;
|
|
106
|
+
});
|
|
107
|
+
if (k < 0) k = others.length;
|
|
108
|
+
const to = [list, k];
|
|
109
|
+
if (list === d.from[0] && k === d.from[1]) return { to, mark: null };
|
|
110
|
+
const m = k < others.length ? { id: getId(others[k]), edge: "before" } : others.length ? { id: getId(others[others.length - 1]), edge: "after" } : { list, edge: "inside" };
|
|
111
|
+
return { to, mark: m };
|
|
112
|
+
};
|
|
113
|
+
const update = (d) => {
|
|
114
|
+
if (d.el && d.rect0) {
|
|
115
|
+
const r = d.el.getBoundingClientRect();
|
|
116
|
+
d.tx = d.x - d.x0 - (r.left - d.tx - d.rect0.left);
|
|
117
|
+
d.ty = d.y - d.y0 - (r.top - d.ty - d.rect0.top);
|
|
118
|
+
d.el.style.translate = `${d.tx}px ${d.ty}px`;
|
|
119
|
+
}
|
|
120
|
+
const { to, mark: m } = targetAt(d);
|
|
121
|
+
d.target = m ? to : null;
|
|
122
|
+
if (!sameMark(d.mark, m)) setMark(d.mark = m);
|
|
123
|
+
};
|
|
124
|
+
const tick = () => {
|
|
125
|
+
const d = drag.current;
|
|
126
|
+
if (!d?.active) return;
|
|
127
|
+
let scrolled = false;
|
|
128
|
+
d.scrollers.forEach((s, i) => {
|
|
129
|
+
if (!s) return;
|
|
130
|
+
const page = s === document.scrollingElement;
|
|
131
|
+
const r = page ? { left: 0, top: 0, right: window.innerWidth, bottom: window.innerHeight } : s.getBoundingClientRect();
|
|
132
|
+
const v = i === 0 ? edgeSpeed(d.x, r.left, r.right) : edgeSpeed(d.y, r.top, r.bottom);
|
|
133
|
+
if (!v) return;
|
|
134
|
+
const before = i === 0 ? s.scrollLeft : s.scrollTop;
|
|
135
|
+
if (i === 0) s.scrollLeft += v;
|
|
136
|
+
else s.scrollTop += v;
|
|
137
|
+
scrolled ||= (i === 0 ? s.scrollLeft : s.scrollTop) !== before;
|
|
138
|
+
});
|
|
139
|
+
if (scrolled) update(d);
|
|
140
|
+
d.raf = requestAnimationFrame(tick);
|
|
141
|
+
};
|
|
142
|
+
const finish = (commit) => {
|
|
143
|
+
const d = drag.current;
|
|
144
|
+
if (!d) return;
|
|
145
|
+
drag.current = null;
|
|
146
|
+
window.removeEventListener("keydown", d.onKey, true);
|
|
147
|
+
if (!d.active) return;
|
|
148
|
+
cancelAnimationFrame(d.raf);
|
|
149
|
+
flipFrom.current = snapshot();
|
|
150
|
+
if (d.el) d.el.style.translate = "";
|
|
151
|
+
if (commit && d.target) move(d.from, d.target, false);
|
|
152
|
+
setDragging(null);
|
|
153
|
+
setMark(null);
|
|
154
|
+
};
|
|
155
|
+
React.useEffect(() => () => finish(false), []);
|
|
156
|
+
const begin = (d) => {
|
|
157
|
+
d.active = true;
|
|
158
|
+
d.el = itemOf(d.handle) ?? d.handle;
|
|
159
|
+
d.rect0 = d.el.getBoundingClientRect();
|
|
160
|
+
d.scrollers = [scrollParent(d.el, "x"), scrollParent(d.el, "y")];
|
|
161
|
+
setDragging(d.id);
|
|
162
|
+
d.raf = requestAnimationFrame(tick);
|
|
163
|
+
};
|
|
164
|
+
const itemProps = (pos) => {
|
|
165
|
+
const id = getId(lists[pos[0]][pos[1]]);
|
|
166
|
+
const lifted = dragging === id ? "" : void 0;
|
|
167
|
+
const drop = mark && "id" in mark && mark.id === id ? mark.edge : void 0;
|
|
168
|
+
const prev = itemCache.current.get(id);
|
|
169
|
+
if (prev && prev["data-nx-dragging"] === lifted && prev["data-nx-drop"] === drop) return prev;
|
|
170
|
+
const next = { "data-nx-reorder-item": "", "data-nx-dragging": lifted, "data-nx-drop": drop };
|
|
171
|
+
itemCache.current.set(id, next);
|
|
172
|
+
return next;
|
|
173
|
+
};
|
|
58
174
|
const listProps = (list) => ({
|
|
59
|
-
|
|
60
|
-
if (
|
|
175
|
+
ref: (el) => {
|
|
176
|
+
if (el) listEls.current.set(list, el);
|
|
177
|
+
else listEls.current.delete(list);
|
|
61
178
|
},
|
|
62
|
-
|
|
63
|
-
e.preventDefault();
|
|
64
|
-
if (dragged.current) move(dragged.current, [list, lists[list].length], false);
|
|
65
|
-
dragged.current = null;
|
|
66
|
-
}
|
|
179
|
+
"data-nx-drop": mark && "list" in mark && mark.list === list ? "inside" : void 0
|
|
67
180
|
});
|
|
68
181
|
const handle = (pos, className) => {
|
|
69
182
|
const item = lists[pos[0]][pos[1]];
|
|
@@ -78,6 +191,51 @@ function useReorder({ lists, getId, getLabel, listLabels, onMove }) {
|
|
|
78
191
|
},
|
|
79
192
|
"aria-label": t.moveItem(getLabel(item)),
|
|
80
193
|
"aria-describedby": hintId,
|
|
194
|
+
onPointerDown: (e) => {
|
|
195
|
+
if (e.button !== 0 || drag.current) return;
|
|
196
|
+
const { clientX: x, clientY: y } = e;
|
|
197
|
+
const d = {
|
|
198
|
+
from: pos,
|
|
199
|
+
id,
|
|
200
|
+
handle: e.currentTarget,
|
|
201
|
+
x0: x,
|
|
202
|
+
y0: y,
|
|
203
|
+
x,
|
|
204
|
+
y,
|
|
205
|
+
active: false,
|
|
206
|
+
tx: 0,
|
|
207
|
+
ty: 0,
|
|
208
|
+
target: null,
|
|
209
|
+
mark: null,
|
|
210
|
+
scrollers: [null, null],
|
|
211
|
+
raf: 0,
|
|
212
|
+
onKey: (k) => {
|
|
213
|
+
if (k.key !== "Escape") return;
|
|
214
|
+
k.preventDefault();
|
|
215
|
+
k.stopPropagation();
|
|
216
|
+
finish(false);
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
drag.current = d;
|
|
220
|
+
window.addEventListener("keydown", d.onKey, true);
|
|
221
|
+
try {
|
|
222
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
223
|
+
} catch {
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
onPointerMove: (e) => {
|
|
227
|
+
const d = drag.current;
|
|
228
|
+
if (!d || d.id !== id) return;
|
|
229
|
+
d.x = e.clientX;
|
|
230
|
+
d.y = e.clientY;
|
|
231
|
+
if (!d.active) {
|
|
232
|
+
if (Math.hypot(d.x - d.x0, d.y - d.y0) < THRESHOLD) return;
|
|
233
|
+
begin(d);
|
|
234
|
+
}
|
|
235
|
+
update(d);
|
|
236
|
+
},
|
|
237
|
+
onPointerUp: () => finish(true),
|
|
238
|
+
onPointerCancel: () => finish(false),
|
|
81
239
|
onKeyDown: (e) => {
|
|
82
240
|
if (!e.altKey) return;
|
|
83
241
|
const [c, i] = pos;
|
|
@@ -94,7 +252,7 @@ function useReorder({ lists, getId, getLabel, listLabels, onMove }) {
|
|
|
94
252
|
move(pos, to, true);
|
|
95
253
|
},
|
|
96
254
|
className: cn(
|
|
97
|
-
"inline-flex size-7 shrink-0 cursor-grab items-center justify-center rounded-shape-min text-[var(--color-on-surface-variant)]",
|
|
255
|
+
"inline-flex size-7 shrink-0 cursor-grab touch-none select-none items-center justify-center rounded-shape-min text-[var(--color-on-surface-variant)]",
|
|
98
256
|
"hover:bg-[var(--color-surface-container-high)] active:cursor-grabbing",
|
|
99
257
|
className
|
|
100
258
|
),
|