@llui/components 0.0.27 → 0.0.29
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.
|
@@ -58,10 +58,21 @@ export interface DragState {
|
|
|
58
58
|
*/
|
|
59
59
|
toContainer: string;
|
|
60
60
|
/**
|
|
61
|
-
* Pointer
|
|
62
|
-
*
|
|
61
|
+
* Pointer X at drag start (viewport coordinates). Used by 2D layouts
|
|
62
|
+
* to compute `deltaX = currentX - startX` alongside the Y axis. In 1D
|
|
63
|
+
* layouts X is tracked but ignored by the renderer.
|
|
64
|
+
*/
|
|
65
|
+
startX: number;
|
|
66
|
+
/**
|
|
67
|
+
* Pointer Y at drag start (viewport coordinates). Used by CSS / the
|
|
68
|
+
* library's `style.transform` binding to make the dragged item follow
|
|
69
|
+
* the pointer.
|
|
63
70
|
*/
|
|
64
71
|
startY: number;
|
|
72
|
+
/**
|
|
73
|
+
* Current pointer X (viewport coordinates). `deltaX = currentX - startX`.
|
|
74
|
+
*/
|
|
75
|
+
currentX: number;
|
|
65
76
|
/**
|
|
66
77
|
* Current pointer Y (viewport coordinates). `deltaY = currentY - startY`.
|
|
67
78
|
*/
|
|
@@ -75,11 +86,13 @@ export type SortableMsg = {
|
|
|
75
86
|
id: string;
|
|
76
87
|
index: number;
|
|
77
88
|
container: string;
|
|
89
|
+
x: number;
|
|
78
90
|
y: number;
|
|
79
91
|
} | {
|
|
80
92
|
type: 'move';
|
|
81
93
|
index: number;
|
|
82
94
|
container: string;
|
|
95
|
+
x: number;
|
|
83
96
|
y: number;
|
|
84
97
|
} | {
|
|
85
98
|
type: 'drop';
|
|
@@ -130,6 +143,32 @@ export interface SortableParts<S> {
|
|
|
130
143
|
}
|
|
131
144
|
export interface ConnectOptions {
|
|
132
145
|
id: string;
|
|
146
|
+
/**
|
|
147
|
+
* Drag-target selection + render strategy.
|
|
148
|
+
*
|
|
149
|
+
* - `'1d'` (default) — single-axis, Y-only. `findTargetAt` picks
|
|
150
|
+
* by vertical distance; `style.transform` on the dragged item
|
|
151
|
+
* is `translateY(deltaY)`; non-dragged items between source
|
|
152
|
+
* and target emit `data-shift: 'up' | 'down'` so CSS can
|
|
153
|
+
* animate them via `translateY(±var(--sortable-shift))`.
|
|
154
|
+
* Correct for vertical lists; fails for 2D layouts (flex-wrap,
|
|
155
|
+
* grid) because same-row items collapse to the same midpoint
|
|
156
|
+
* distance.
|
|
157
|
+
*
|
|
158
|
+
* - `'2d'` — Euclidean target selection against 2D midpoints;
|
|
159
|
+
* dragged item follows both X and Y (`translate(dx, dy)`);
|
|
160
|
+
* non-dragged items between source and target get a per-item
|
|
161
|
+
* `style.transform = translate(deltaFromSnapshot)` that opens
|
|
162
|
+
* the correct gap regardless of row boundaries. `data-shift`
|
|
163
|
+
* is always `undefined` in 2D so CSS `translateY(var(--...))`
|
|
164
|
+
* rules don't conflict with the per-item transform.
|
|
165
|
+
*
|
|
166
|
+
* Keyboard navigation (`moveBy`) stays linear-array in both modes —
|
|
167
|
+
* arrow keys step through the array indices regardless of visual
|
|
168
|
+
* row, because that's what screen readers announce and what the
|
|
169
|
+
* underlying data order actually is.
|
|
170
|
+
*/
|
|
171
|
+
layout?: '1d' | '2d';
|
|
133
172
|
}
|
|
134
173
|
export declare function connect<S>(get: (s: S) => SortableState, send: Send<SortableMsg>, opts: ConnectOptions): SortableParts<S>;
|
|
135
174
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sortable.d.ts","sourceRoot":"","sources":["../../src/components/sortable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAErC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB;;;OAGG;IACH,aAAa,EAAE,MAAM,CAAA;IACrB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB
|
|
1
|
+
{"version":3,"file":"sortable.d.ts","sourceRoot":"","sources":["../../src/components/sortable.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,7 +12,9 @@ export function update(state, msg) {
|
|
|
12
12
|
currentIndex: msg.index,
|
|
13
13
|
fromContainer: msg.container,
|
|
14
14
|
toContainer: msg.container,
|
|
15
|
+
startX: msg.x,
|
|
15
16
|
startY: msg.y,
|
|
17
|
+
currentX: msg.x,
|
|
16
18
|
currentY: msg.y,
|
|
17
19
|
},
|
|
18
20
|
},
|
|
@@ -23,6 +25,7 @@ export function update(state, msg) {
|
|
|
23
25
|
return [state, []];
|
|
24
26
|
if (state.dragging.currentIndex === msg.index &&
|
|
25
27
|
state.dragging.toContainer === msg.container &&
|
|
28
|
+
state.dragging.currentX === msg.x &&
|
|
26
29
|
state.dragging.currentY === msg.y) {
|
|
27
30
|
return [state, []];
|
|
28
31
|
}
|
|
@@ -32,6 +35,7 @@ export function update(state, msg) {
|
|
|
32
35
|
...state.dragging,
|
|
33
36
|
currentIndex: msg.index,
|
|
34
37
|
toContainer: msg.container,
|
|
38
|
+
currentX: msg.x,
|
|
35
39
|
currentY: msg.y,
|
|
36
40
|
},
|
|
37
41
|
},
|
|
@@ -56,7 +60,9 @@ export function update(state, msg) {
|
|
|
56
60
|
currentIndex: msg.index,
|
|
57
61
|
fromContainer: msg.container,
|
|
58
62
|
toContainer: msg.container,
|
|
63
|
+
startX: 0,
|
|
59
64
|
startY: 0,
|
|
65
|
+
currentX: 0,
|
|
60
66
|
currentY: 0,
|
|
61
67
|
},
|
|
62
68
|
},
|
|
@@ -75,6 +81,7 @@ export function update(state, msg) {
|
|
|
75
81
|
export function connect(get, send, opts) {
|
|
76
82
|
// The connect's `id` doubles as the cross-container identifier
|
|
77
83
|
const containerId = opts.id;
|
|
84
|
+
const layout = opts.layout ?? '1d';
|
|
78
85
|
const snapshots = new Map();
|
|
79
86
|
function snapshotContainer(rootEl, cid) {
|
|
80
87
|
const items = rootEl.querySelectorAll('[data-scope="sortable"][data-part="item"]');
|
|
@@ -83,7 +90,7 @@ export function connect(get, send, opts) {
|
|
|
83
90
|
const idToIndex = new Map();
|
|
84
91
|
items.forEach((item, i) => {
|
|
85
92
|
const r = item.getBoundingClientRect();
|
|
86
|
-
mids.push(r.top + r.height / 2);
|
|
93
|
+
mids.push({ x: r.left + r.width / 2, y: r.top + r.height / 2 });
|
|
87
94
|
const itemId = item.dataset.id;
|
|
88
95
|
if (itemId !== undefined)
|
|
89
96
|
idToIndex.set(itemId, i);
|
|
@@ -99,11 +106,12 @@ export function connect(get, send, opts) {
|
|
|
99
106
|
}
|
|
100
107
|
}
|
|
101
108
|
// Find the target index under the pointer using the drag-start snapshot.
|
|
102
|
-
//
|
|
103
|
-
//
|
|
109
|
+
// 1D mode: picks by Y-only distance (original behavior). 2D mode: picks
|
|
110
|
+
// by Euclidean distance over {x, y} midpoints — required for flex-wrap
|
|
111
|
+
// / grid layouts where multiple items share a row and collapse to the
|
|
112
|
+
// same Y value. Both modes are stable against items being visually
|
|
113
|
+
// transformed during the drag because midpoints are taken pre-transform.
|
|
104
114
|
function findTargetAt(e) {
|
|
105
|
-
// Find which sortable root the pointer is over (using getBoundingClientRect
|
|
106
|
-
// on roots, which are not transformed during drag).
|
|
107
115
|
const roots = document.querySelectorAll('[data-scope="sortable"][data-part="root"]');
|
|
108
116
|
for (const root of roots) {
|
|
109
117
|
const r = root.getBoundingClientRect();
|
|
@@ -118,14 +126,33 @@ export function connect(get, send, opts) {
|
|
|
118
126
|
if (!snap || snap.mids.length === 0)
|
|
119
127
|
return { container: cid, index: 0 };
|
|
120
128
|
const mids = snap.mids;
|
|
121
|
-
// Find the index whose midpoint is closest to clientY
|
|
122
129
|
let bestIdx = 0;
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
130
|
+
if (layout === '2d') {
|
|
131
|
+
// Euclidean (squared — monotonic with distance, saves a sqrt).
|
|
132
|
+
const dx0 = e.clientX - mids[0].x;
|
|
133
|
+
const dy0 = e.clientY - mids[0].y;
|
|
134
|
+
let bestDist = dx0 * dx0 + dy0 * dy0;
|
|
135
|
+
for (let i = 1; i < mids.length; i++) {
|
|
136
|
+
const dx = e.clientX - mids[i].x;
|
|
137
|
+
const dy = e.clientY - mids[i].y;
|
|
138
|
+
const d = dx * dx + dy * dy;
|
|
139
|
+
if (d < bestDist) {
|
|
140
|
+
bestDist = d;
|
|
141
|
+
bestIdx = i;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
// 1D — Y-only distance. Preserves the original behavior for
|
|
147
|
+
// vertical lists; same-row items in a flex-wrap would tie and
|
|
148
|
+
// the first match wins, which is the bug that motivates 2D.
|
|
149
|
+
let bestDist = Math.abs(e.clientY - mids[0].y);
|
|
150
|
+
for (let i = 1; i < mids.length; i++) {
|
|
151
|
+
const d = Math.abs(e.clientY - mids[i].y);
|
|
152
|
+
if (d < bestDist) {
|
|
153
|
+
bestDist = d;
|
|
154
|
+
bestIdx = i;
|
|
155
|
+
}
|
|
129
156
|
}
|
|
130
157
|
}
|
|
131
158
|
return { container: cid, index: bestIdx };
|
|
@@ -143,7 +170,13 @@ export function connect(get, send, opts) {
|
|
|
143
170
|
return;
|
|
144
171
|
const hit = findTargetAt(e);
|
|
145
172
|
if (hit !== null)
|
|
146
|
-
send({
|
|
173
|
+
send({
|
|
174
|
+
type: 'move',
|
|
175
|
+
index: hit.index,
|
|
176
|
+
container: hit.container,
|
|
177
|
+
x: e.clientX,
|
|
178
|
+
y: e.clientY,
|
|
179
|
+
});
|
|
147
180
|
},
|
|
148
181
|
onPointerUp: () => {
|
|
149
182
|
snapshots.clear();
|
|
@@ -177,7 +210,15 @@ export function connect(get, send, opts) {
|
|
|
177
210
|
// Shift direction for items BETWEEN the source and target (excluding the
|
|
178
211
|
// dragged item itself). 'down' = item should translate down to make room;
|
|
179
212
|
// 'up' = item should translate up. CSS controls the actual displacement.
|
|
213
|
+
//
|
|
214
|
+
// In 2D layout, `data-shift` is always undefined — the per-item
|
|
215
|
+
// `style.transform` below opens the correct gap directly. Keeping
|
|
216
|
+
// `data-shift` out of the 2D path prevents any author-provided
|
|
217
|
+
// CSS rule like `[data-shift] { translate: 0 var(--sortable-shift) }`
|
|
218
|
+
// from fighting with the computed transform.
|
|
180
219
|
'data-shift': (s) => {
|
|
220
|
+
if (layout === '2d')
|
|
221
|
+
return undefined;
|
|
181
222
|
const d = get(s).dragging;
|
|
182
223
|
if (!d || d.fromContainer !== containerId || d.toContainer !== containerId)
|
|
183
224
|
return undefined;
|
|
@@ -200,14 +241,65 @@ export function connect(get, send, opts) {
|
|
|
200
241
|
}
|
|
201
242
|
return undefined;
|
|
202
243
|
},
|
|
203
|
-
// The dragged item follows the pointer
|
|
204
|
-
//
|
|
244
|
+
// The dragged item follows the pointer. In 1D, translateY only; in
|
|
245
|
+
// 2D, both axes. Non-dragged items in 2D between source and target
|
|
246
|
+
// get a per-item translate computed from the snapshot — each item's
|
|
247
|
+
// vector is `snapshot[newSlot] - snapshot[ownSlot]` so the gap
|
|
248
|
+
// opens correctly regardless of row wrap. In 1D, non-dragged items
|
|
249
|
+
// emit `undefined` here and rely on the consumer's CSS `data-shift`
|
|
250
|
+
// rule.
|
|
205
251
|
'style.transform': (s) => {
|
|
206
252
|
const d = get(s).dragging;
|
|
207
|
-
if (!d
|
|
253
|
+
if (!d)
|
|
254
|
+
return undefined;
|
|
255
|
+
const isDragged = d.id === id && d.fromContainer === containerId;
|
|
256
|
+
if (isDragged) {
|
|
257
|
+
const deltaY = d.currentY - d.startY;
|
|
258
|
+
if (layout === '2d') {
|
|
259
|
+
const deltaX = d.currentX - d.startX;
|
|
260
|
+
return `translate(${deltaX}px, ${deltaY}px)`;
|
|
261
|
+
}
|
|
262
|
+
return `translateY(${deltaY}px)`;
|
|
263
|
+
}
|
|
264
|
+
// Non-dragged items: per-item displacement in 2D only.
|
|
265
|
+
if (layout !== '2d')
|
|
266
|
+
return undefined;
|
|
267
|
+
if (d.fromContainer !== containerId || d.toContainer !== containerId)
|
|
268
|
+
return undefined;
|
|
269
|
+
if (d.startIndex === d.currentIndex)
|
|
270
|
+
return undefined;
|
|
271
|
+
const snap = snapshots.get(containerId);
|
|
272
|
+
if (!snap)
|
|
273
|
+
return undefined;
|
|
274
|
+
const liveIndex = snap.idToIndex.get(id);
|
|
275
|
+
if (liveIndex === undefined)
|
|
276
|
+
return undefined;
|
|
277
|
+
// Which slot this item should visually occupy while the drag
|
|
278
|
+
// previews the reorder:
|
|
279
|
+
// drag-down (start < current): items at liveIndex in
|
|
280
|
+
// (start .. current] shift left-by-one in array order, so
|
|
281
|
+
// they take the slot at liveIndex - 1.
|
|
282
|
+
// drag-up (current < start): items at liveIndex in
|
|
283
|
+
// [current .. start) shift right-by-one, take slot
|
|
284
|
+
// liveIndex + 1.
|
|
285
|
+
let targetSlot;
|
|
286
|
+
if (d.startIndex < d.currentIndex) {
|
|
287
|
+
if (liveIndex <= d.startIndex || liveIndex > d.currentIndex)
|
|
288
|
+
return undefined;
|
|
289
|
+
targetSlot = liveIndex - 1;
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
if (liveIndex < d.currentIndex || liveIndex >= d.startIndex)
|
|
293
|
+
return undefined;
|
|
294
|
+
targetSlot = liveIndex + 1;
|
|
295
|
+
}
|
|
296
|
+
const own = snap.mids[liveIndex];
|
|
297
|
+
const target = snap.mids[targetSlot];
|
|
298
|
+
if (!own || !target)
|
|
208
299
|
return undefined;
|
|
209
|
-
const
|
|
210
|
-
|
|
300
|
+
const dx = target.x - own.x;
|
|
301
|
+
const dy = target.y - own.y;
|
|
302
|
+
return `translate(${dx}px, ${dy}px)`;
|
|
211
303
|
},
|
|
212
304
|
'style.zIndex': (s) => {
|
|
213
305
|
const d = get(s).dragging;
|
|
@@ -262,7 +354,14 @@ export function connect(get, send, opts) {
|
|
|
262
354
|
// positions. Otherwise items shifting via CSS would cause the target
|
|
263
355
|
// to oscillate as elementFromPoint hits different items.
|
|
264
356
|
snapshotAll();
|
|
265
|
-
send({
|
|
357
|
+
send({
|
|
358
|
+
type: 'start',
|
|
359
|
+
id,
|
|
360
|
+
index: currentIndex,
|
|
361
|
+
container: containerId,
|
|
362
|
+
x: e.clientX,
|
|
363
|
+
y: e.clientY,
|
|
364
|
+
});
|
|
266
365
|
},
|
|
267
366
|
onKeyDown: (e) => {
|
|
268
367
|
switch (e.key) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sortable.js","sourceRoot":"","sources":["../../src/components/sortable.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Send } from '@llui/dom'\n\n/**\n * Sortable — pointer-based reorderable list.\n *\n * State machine tracks the currently-dragged item and where it's hovering.\n * The app owns the actual array; listen for `drop` and use `reorder(arr, from, to)`\n * to compute the new order, or watch `currentIndex` during drag for live preview.\n *\n * ```ts\n * type State = { items: string[]; sort: SortableState }\n *\n * update: (state, msg) => {\n * switch (msg.type) {\n * case 'sort':\n * return [{ ...state, sort: sortable.update(state.sort, msg.msg)[0] }, []]\n * case 'drop': {\n * const d = state.sort.dragging\n * if (!d) return [state, []]\n * return [{ ...state, items: reorder(state.items, d.startIndex, d.currentIndex) }, []]\n * }\n * }\n * }\n *\n * view: ({ send, each, text }) => {\n * const s = sortable.connect<State>(s => s.sort, m => send({ type: 'sort', msg: m }), { id: 'list' })\n * return [\n * ul({ ...s.root, class: 'list' }, [\n * ...each({\n * items: (st) => st.items,\n * key: (x) => x,\n * render: ({ item, index }) => [\n * li({ ...s.item(item(), index()), class: 'item' }, [\n * div({ ...s.handle(item(), index()), class: 'handle' }, [text('⋮⋮')]),\n * text(item),\n * ]),\n * ],\n * }),\n * ]),\n * ]\n * }\n * ```\n *\n * Hook up pointermove/pointerup at the root (attachPointerHandlers) — or\n * wire them directly via `onPointerMove` / `onPointerUp` on the root part.\n */\n\nexport interface DragState {\n id: string\n startIndex: number\n currentIndex: number\n /**\n * Container the drag originated from. Defaults to the connect's `id` for\n * single-container sortables. Set when multiple sortables share state.\n */\n fromContainer: string\n /**\n * Container the pointer is currently over. Same as `fromContainer` for\n * single-container sortables. Differs when dragging across containers.\n */\n toContainer: string\n /**\n * Pointer Y at drag start (viewport coordinates). Used by CSS to make\n * the dragged item follow the pointer via translateY(deltaY).\n */\n startY: number\n /**\n * Current pointer Y (viewport coordinates). `deltaY = currentY - startY`.\n */\n currentY: number\n}\n\nexport interface SortableState {\n dragging: DragState | null\n}\n\nexport type SortableMsg =\n | { type: 'start'; id: string; index: number; container: string; y: number }\n | { type: 'move'; index: number; container: string; y: number }\n | { type: 'drop' }\n | { type: 'cancel' }\n // Keyboard: toggle between picking up and dropping at current position\n | { type: 'toggleGrab'; id: string; index: number; container: string }\n // Keyboard: shift currentIndex by delta (clamped ≥ 0)\n | { type: 'moveBy'; delta: number }\n\nexport function init(): SortableState {\n return { dragging: null }\n}\n\nexport function update(state: SortableState, msg: SortableMsg): [SortableState, never[]] {\n switch (msg.type) {\n case 'start':\n return [\n {\n dragging: {\n id: msg.id,\n startIndex: msg.index,\n currentIndex: msg.index,\n fromContainer: msg.container,\n toContainer: msg.container,\n startY: msg.y,\n currentY: msg.y,\n },\n },\n [],\n ]\n case 'move': {\n if (!state.dragging) return [state, []]\n if (\n state.dragging.currentIndex === msg.index &&\n state.dragging.toContainer === msg.container &&\n state.dragging.currentY === msg.y\n ) {\n return [state, []]\n }\n return [\n {\n dragging: {\n ...state.dragging,\n currentIndex: msg.index,\n toContainer: msg.container,\n currentY: msg.y,\n },\n },\n [],\n ]\n }\n case 'drop':\n return state.dragging ? [{ dragging: null }, []] : [state, []]\n case 'cancel':\n return state.dragging ? [{ dragging: null }, []] : [state, []]\n case 'toggleGrab':\n if (state.dragging) {\n // Already dragging — drop at current position\n return [{ dragging: null }, []]\n }\n // Pick up (keyboard — no pointer position)\n return [\n {\n dragging: {\n id: msg.id,\n startIndex: msg.index,\n currentIndex: msg.index,\n fromContainer: msg.container,\n toContainer: msg.container,\n startY: 0,\n currentY: 0,\n },\n },\n [],\n ]\n case 'moveBy': {\n if (!state.dragging) return [state, []]\n const next = Math.max(0, state.dragging.currentIndex + msg.delta)\n if (next === state.dragging.currentIndex) return [state, []]\n return [{ dragging: { ...state.dragging, currentIndex: next } }, []]\n }\n }\n}\n\nexport interface SortableParts<S> {\n root: {\n 'data-scope': 'sortable'\n 'data-part': 'root'\n 'data-container-id': string\n 'data-dragging': (s: S) => '' | undefined\n onPointerMove: (e: PointerEvent) => void\n onPointerUp: (e: PointerEvent) => void\n onPointerCancel: (e: PointerEvent) => void\n }\n item: (\n id: string,\n index: number,\n ) => {\n 'data-scope': 'sortable'\n 'data-part': 'item'\n 'data-index': string\n 'data-id': string\n 'data-dragging': (s: S) => '' | undefined\n 'data-over': (s: S) => '' | undefined\n 'data-shift': (s: S) => 'up' | 'down' | undefined\n 'style.transform': (s: S) => string | undefined\n 'style.zIndex': (s: S) => string | undefined\n }\n handle: (\n id: string,\n index: number,\n ) => {\n 'data-scope': 'sortable'\n 'data-part': 'handle'\n role: 'button'\n tabIndex: 0\n 'aria-grabbed': (s: S) => boolean\n 'aria-label': string\n onPointerDown: (e: PointerEvent) => void\n onKeyDown: (e: KeyboardEvent) => void\n }\n}\n\nexport interface ConnectOptions {\n id: string\n}\n\nexport function connect<S>(\n get: (s: S) => SortableState,\n send: Send<SortableMsg>,\n opts: ConnectOptions,\n): SortableParts<S> {\n // The connect's `id` doubles as the cross-container identifier\n const containerId = opts.id\n\n // Snapshots taken at drag start — stable throughout the drag so computing\n // the target index is not affected by items visually shifting via CSS.\n // Map: container-id → array of midpoint Y values for each item's original\n // bounding rect (sorted by index). The handler records this on pointerdown.\n interface Snapshot {\n mids: number[]\n // id → current DOM index at drag start. Used by data-shift to look up an\n // item's live position, since the `index` captured at render time is\n // frozen and goes stale after each() reconciles a reorder.\n idToIndex: Map<string, number>\n }\n const snapshots = new Map<string, Snapshot>()\n\n function snapshotContainer(rootEl: HTMLElement, cid: string): void {\n const items = rootEl.querySelectorAll<HTMLElement>('[data-scope=\"sortable\"][data-part=\"item\"]')\n // Read rects once — they're pre-transform (no drag shifts yet)\n const mids: number[] = []\n const idToIndex = new Map<string, number>()\n items.forEach((item, i) => {\n const r = item.getBoundingClientRect()\n mids.push(r.top + r.height / 2)\n const itemId = item.dataset.id\n if (itemId !== undefined) idToIndex.set(itemId, i)\n })\n snapshots.set(cid, { mids, idToIndex })\n }\n\n function snapshotAll(): void {\n const roots = document.querySelectorAll<HTMLElement>(\n '[data-scope=\"sortable\"][data-part=\"root\"]',\n )\n for (const root of roots) {\n const cid = root.dataset.containerId\n if (cid) snapshotContainer(root, cid)\n }\n }\n\n // Find the target index under the pointer using the drag-start snapshot.\n // Picks the index whose midpoint is closest to the pointer Y — stable\n // against items being visually transformed during the drag.\n function findTargetAt(e: PointerEvent): { container: string; index: number } | null {\n // Find which sortable root the pointer is over (using getBoundingClientRect\n // on roots, which are not transformed during drag).\n const roots = document.querySelectorAll<HTMLElement>(\n '[data-scope=\"sortable\"][data-part=\"root\"]',\n )\n for (const root of roots) {\n const r = root.getBoundingClientRect()\n if (e.clientX < r.left || e.clientX > r.right) continue\n if (e.clientY < r.top || e.clientY > r.bottom) continue\n const cid = root.dataset.containerId\n if (!cid) continue\n const snap = snapshots.get(cid)\n if (!snap || snap.mids.length === 0) return { container: cid, index: 0 }\n const mids = snap.mids\n // Find the index whose midpoint is closest to clientY\n let bestIdx = 0\n let bestDist = Math.abs(e.clientY - mids[0]!)\n for (let i = 1; i < mids.length; i++) {\n const d = Math.abs(e.clientY - mids[i]!)\n if (d < bestDist) {\n bestDist = d\n bestIdx = i\n }\n }\n return { container: cid, index: bestIdx }\n }\n return null\n }\n\n return {\n root: {\n 'data-scope': 'sortable',\n 'data-part': 'root',\n 'data-container-id': containerId,\n 'data-dragging': (s) => (get(s).dragging ? '' : undefined),\n onPointerMove: (e) => {\n if (!e.buttons) return\n const hit = findTargetAt(e)\n if (hit !== null)\n send({ type: 'move', index: hit.index, container: hit.container, y: e.clientY })\n },\n onPointerUp: () => {\n snapshots.clear()\n send({ type: 'drop' })\n },\n onPointerCancel: () => {\n snapshots.clear()\n send({ type: 'cancel' })\n },\n },\n item: (id, index) => ({\n 'data-scope': 'sortable',\n 'data-part': 'item',\n 'data-index': String(index),\n 'data-id': id,\n 'data-dragging': (s) => {\n const d = get(s).dragging\n return d?.id === id && d?.fromContainer === containerId ? '' : undefined\n },\n 'data-over': (s) => {\n const d = get(s).dragging\n if (!d || d.toContainer !== containerId) return undefined\n // Look up this item's CURRENT DOM index via the drag-start snapshot.\n // The `index` closed over here is frozen at initial render and goes\n // stale after each() reconciles a reorder.\n const snap = snapshots.get(containerId)\n const liveIndex = snap?.idToIndex.get(id) ?? index\n return d.currentIndex === liveIndex ? '' : undefined\n },\n // Shift direction for items BETWEEN the source and target (excluding the\n // dragged item itself). 'down' = item should translate down to make room;\n // 'up' = item should translate up. CSS controls the actual displacement.\n 'data-shift': (s) => {\n const d = get(s).dragging\n if (!d || d.fromContainer !== containerId || d.toContainer !== containerId) return undefined\n if (d.id === id) return undefined\n if (d.startIndex === d.currentIndex) return undefined\n // Look up this item's live DOM index — see note on data-over.\n const snap = snapshots.get(containerId)\n const liveIndex = snap?.idToIndex.get(id) ?? index\n if (d.startIndex < d.currentIndex) {\n // Dragging down: items between start+1 and current shift up\n if (liveIndex > d.startIndex && liveIndex <= d.currentIndex) return 'up'\n } else {\n // Dragging up: items between current and start-1 shift down\n if (liveIndex >= d.currentIndex && liveIndex < d.startIndex) return 'down'\n }\n return undefined\n },\n // The dragged item follows the pointer via translateY(deltaY). Other\n // items have no transform override — data-shift CSS handles them.\n 'style.transform': (s) => {\n const d = get(s).dragging\n if (!d || d.id !== id || d.fromContainer !== containerId) return undefined\n const deltaY = d.currentY - d.startY\n return `translateY(${deltaY}px)`\n },\n 'style.zIndex': (s) => {\n const d = get(s).dragging\n if (!d || d.id !== id || d.fromContainer !== containerId) return undefined\n return '10'\n },\n }),\n handle: (id, index) => ({\n 'data-scope': 'sortable',\n 'data-part': 'handle',\n role: 'button',\n tabIndex: 0,\n 'aria-grabbed': (s) => {\n const d = get(s).dragging\n return d?.id === id && d?.fromContainer === containerId\n },\n 'aria-label':\n 'Drag handle. Press space to pick up, arrow keys to move, space again to drop, escape to cancel.',\n onPointerDown: (e) => {\n e.preventDefault()\n const target = e.currentTarget as Element | null\n if (target && 'setPointerCapture' in target) {\n try {\n ;(target as Element & { setPointerCapture: (id: number) => void }).setPointerCapture(\n e.pointerId,\n )\n } catch {\n // Ignore — not all elements support pointer capture\n }\n }\n // Compute the CURRENT DOM index of this handle's item — the captured\n // `index` param is stale after a reorder (each() moves keyed nodes\n // without re-running render, so the closure's index is frozen at\n // initial mount). Walk up to find the containing item, then count its\n // position among sibling items.\n let currentIndex = index\n if (target) {\n const itemEl = (target as Element).closest<HTMLElement>(\n '[data-scope=\"sortable\"][data-part=\"item\"]',\n )\n const rootEl = (target as Element).closest<HTMLElement>(\n '[data-scope=\"sortable\"][data-part=\"root\"]',\n )\n if (itemEl && rootEl) {\n const items = rootEl.querySelectorAll<HTMLElement>(\n '[data-scope=\"sortable\"][data-part=\"item\"]',\n )\n for (let i = 0; i < items.length; i++) {\n if (items[i] === itemEl) {\n currentIndex = i\n break\n }\n }\n }\n }\n // Snapshot positions BEFORE the drag starts, so subsequent pointermove\n // events can resolve the target index against stable (pre-transform)\n // positions. Otherwise items shifting via CSS would cause the target\n // to oscillate as elementFromPoint hits different items.\n snapshotAll()\n send({ type: 'start', id, index: currentIndex, container: containerId, y: e.clientY })\n },\n onKeyDown: (e) => {\n switch (e.key) {\n case ' ':\n case 'Enter':\n e.preventDefault()\n send({ type: 'toggleGrab', id, index, container: containerId })\n return\n case 'Escape':\n e.preventDefault()\n send({ type: 'cancel' })\n return\n case 'ArrowDown':\n case 'ArrowRight':\n e.preventDefault()\n send({ type: 'moveBy', delta: 1 })\n return\n case 'ArrowUp':\n case 'ArrowLeft':\n e.preventDefault()\n send({ type: 'moveBy', delta: -1 })\n return\n }\n },\n }),\n }\n}\n\n// ── Reorder utility ────────────────────────────────────────────\n\n/**\n * Move an item in an array from one index to another, returning a new array.\n * Out-of-range indices are clamped to array bounds.\n */\nexport function reorder<T>(arr: readonly T[], from: number, to: number): T[] {\n const len = arr.length\n if (len === 0) return []\n const f = Math.max(0, Math.min(len - 1, from))\n const t = Math.max(0, Math.min(len - 1, to))\n if (f === t) return arr.slice()\n const result = arr.slice()\n const [item] = result.splice(f, 1)\n result.splice(t, 0, item)\n return result\n}\n\nexport const sortable = { init, update, connect, reorder }\n"]}
|
|
1
|
+
{"version":3,"file":"sortable.js","sourceRoot":"","sources":["../../src/components/sortable.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Send } from '@llui/dom'\n\n/**\n * Sortable — pointer-based reorderable list.\n *\n * State machine tracks the currently-dragged item and where it's hovering.\n * The app owns the actual array; listen for `drop` and use `reorder(arr, from, to)`\n * to compute the new order, or watch `currentIndex` during drag for live preview.\n *\n * ```ts\n * type State = { items: string[]; sort: SortableState }\n *\n * update: (state, msg) => {\n * switch (msg.type) {\n * case 'sort':\n * return [{ ...state, sort: sortable.update(state.sort, msg.msg)[0] }, []]\n * case 'drop': {\n * const d = state.sort.dragging\n * if (!d) return [state, []]\n * return [{ ...state, items: reorder(state.items, d.startIndex, d.currentIndex) }, []]\n * }\n * }\n * }\n *\n * view: ({ send, each, text }) => {\n * const s = sortable.connect<State>(s => s.sort, m => send({ type: 'sort', msg: m }), { id: 'list' })\n * return [\n * ul({ ...s.root, class: 'list' }, [\n * ...each({\n * items: (st) => st.items,\n * key: (x) => x,\n * render: ({ item, index }) => [\n * li({ ...s.item(item(), index()), class: 'item' }, [\n * div({ ...s.handle(item(), index()), class: 'handle' }, [text('⋮⋮')]),\n * text(item),\n * ]),\n * ],\n * }),\n * ]),\n * ]\n * }\n * ```\n *\n * Hook up pointermove/pointerup at the root (attachPointerHandlers) — or\n * wire them directly via `onPointerMove` / `onPointerUp` on the root part.\n */\n\nexport interface DragState {\n id: string\n startIndex: number\n currentIndex: number\n /**\n * Container the drag originated from. Defaults to the connect's `id` for\n * single-container sortables. Set when multiple sortables share state.\n */\n fromContainer: string\n /**\n * Container the pointer is currently over. Same as `fromContainer` for\n * single-container sortables. Differs when dragging across containers.\n */\n toContainer: string\n /**\n * Pointer X at drag start (viewport coordinates). Used by 2D layouts\n * to compute `deltaX = currentX - startX` alongside the Y axis. In 1D\n * layouts X is tracked but ignored by the renderer.\n */\n startX: number\n /**\n * Pointer Y at drag start (viewport coordinates). Used by CSS / the\n * library's `style.transform` binding to make the dragged item follow\n * the pointer.\n */\n startY: number\n /**\n * Current pointer X (viewport coordinates). `deltaX = currentX - startX`.\n */\n currentX: number\n /**\n * Current pointer Y (viewport coordinates). `deltaY = currentY - startY`.\n */\n currentY: number\n}\n\nexport interface SortableState {\n dragging: DragState | null\n}\n\nexport type SortableMsg =\n | { type: 'start'; id: string; index: number; container: string; x: number; y: number }\n | { type: 'move'; index: number; container: string; x: number; y: number }\n | { type: 'drop' }\n | { type: 'cancel' }\n // Keyboard: toggle between picking up and dropping at current position\n | { type: 'toggleGrab'; id: string; index: number; container: string }\n // Keyboard: shift currentIndex by delta (clamped ≥ 0)\n | { type: 'moveBy'; delta: number }\n\nexport function init(): SortableState {\n return { dragging: null }\n}\n\nexport function update(state: SortableState, msg: SortableMsg): [SortableState, never[]] {\n switch (msg.type) {\n case 'start':\n return [\n {\n dragging: {\n id: msg.id,\n startIndex: msg.index,\n currentIndex: msg.index,\n fromContainer: msg.container,\n toContainer: msg.container,\n startX: msg.x,\n startY: msg.y,\n currentX: msg.x,\n currentY: msg.y,\n },\n },\n [],\n ]\n case 'move': {\n if (!state.dragging) return [state, []]\n if (\n state.dragging.currentIndex === msg.index &&\n state.dragging.toContainer === msg.container &&\n state.dragging.currentX === msg.x &&\n state.dragging.currentY === msg.y\n ) {\n return [state, []]\n }\n return [\n {\n dragging: {\n ...state.dragging,\n currentIndex: msg.index,\n toContainer: msg.container,\n currentX: msg.x,\n currentY: msg.y,\n },\n },\n [],\n ]\n }\n case 'drop':\n return state.dragging ? [{ dragging: null }, []] : [state, []]\n case 'cancel':\n return state.dragging ? [{ dragging: null }, []] : [state, []]\n case 'toggleGrab':\n if (state.dragging) {\n // Already dragging — drop at current position\n return [{ dragging: null }, []]\n }\n // Pick up (keyboard — no pointer position)\n return [\n {\n dragging: {\n id: msg.id,\n startIndex: msg.index,\n currentIndex: msg.index,\n fromContainer: msg.container,\n toContainer: msg.container,\n startX: 0,\n startY: 0,\n currentX: 0,\n currentY: 0,\n },\n },\n [],\n ]\n case 'moveBy': {\n if (!state.dragging) return [state, []]\n const next = Math.max(0, state.dragging.currentIndex + msg.delta)\n if (next === state.dragging.currentIndex) return [state, []]\n return [{ dragging: { ...state.dragging, currentIndex: next } }, []]\n }\n }\n}\n\nexport interface SortableParts<S> {\n root: {\n 'data-scope': 'sortable'\n 'data-part': 'root'\n 'data-container-id': string\n 'data-dragging': (s: S) => '' | undefined\n onPointerMove: (e: PointerEvent) => void\n onPointerUp: (e: PointerEvent) => void\n onPointerCancel: (e: PointerEvent) => void\n }\n item: (\n id: string,\n index: number,\n ) => {\n 'data-scope': 'sortable'\n 'data-part': 'item'\n 'data-index': string\n 'data-id': string\n 'data-dragging': (s: S) => '' | undefined\n 'data-over': (s: S) => '' | undefined\n 'data-shift': (s: S) => 'up' | 'down' | undefined\n 'style.transform': (s: S) => string | undefined\n 'style.zIndex': (s: S) => string | undefined\n }\n handle: (\n id: string,\n index: number,\n ) => {\n 'data-scope': 'sortable'\n 'data-part': 'handle'\n role: 'button'\n tabIndex: 0\n 'aria-grabbed': (s: S) => boolean\n 'aria-label': string\n onPointerDown: (e: PointerEvent) => void\n onKeyDown: (e: KeyboardEvent) => void\n }\n}\n\nexport interface ConnectOptions {\n id: string\n /**\n * Drag-target selection + render strategy.\n *\n * - `'1d'` (default) — single-axis, Y-only. `findTargetAt` picks\n * by vertical distance; `style.transform` on the dragged item\n * is `translateY(deltaY)`; non-dragged items between source\n * and target emit `data-shift: 'up' | 'down'` so CSS can\n * animate them via `translateY(±var(--sortable-shift))`.\n * Correct for vertical lists; fails for 2D layouts (flex-wrap,\n * grid) because same-row items collapse to the same midpoint\n * distance.\n *\n * - `'2d'` — Euclidean target selection against 2D midpoints;\n * dragged item follows both X and Y (`translate(dx, dy)`);\n * non-dragged items between source and target get a per-item\n * `style.transform = translate(deltaFromSnapshot)` that opens\n * the correct gap regardless of row boundaries. `data-shift`\n * is always `undefined` in 2D so CSS `translateY(var(--...))`\n * rules don't conflict with the per-item transform.\n *\n * Keyboard navigation (`moveBy`) stays linear-array in both modes —\n * arrow keys step through the array indices regardless of visual\n * row, because that's what screen readers announce and what the\n * underlying data order actually is.\n */\n layout?: '1d' | '2d'\n}\n\nexport function connect<S>(\n get: (s: S) => SortableState,\n send: Send<SortableMsg>,\n opts: ConnectOptions,\n): SortableParts<S> {\n // The connect's `id` doubles as the cross-container identifier\n const containerId = opts.id\n const layout = opts.layout ?? '1d'\n\n // Snapshots taken at drag start — stable throughout the drag so computing\n // the target index is not affected by items visually shifting via CSS.\n // Map: container-id → array of midpoint {x, y} pairs for each item's\n // original bounding rect (sorted by index). The handler records this on\n // pointerdown. Always 2D internally; 1D layout's findTargetAt ignores X.\n interface Snapshot {\n mids: Array<{ x: number; y: number }>\n // id → current DOM index at drag start. Used by data-shift / per-item\n // transform to look up an item's live position, since the `index`\n // captured at render time is frozen and goes stale after each()\n // reconciles a reorder.\n idToIndex: Map<string, number>\n }\n const snapshots = new Map<string, Snapshot>()\n\n function snapshotContainer(rootEl: HTMLElement, cid: string): void {\n const items = rootEl.querySelectorAll<HTMLElement>('[data-scope=\"sortable\"][data-part=\"item\"]')\n // Read rects once — they're pre-transform (no drag shifts yet)\n const mids: Array<{ x: number; y: number }> = []\n const idToIndex = new Map<string, number>()\n items.forEach((item, i) => {\n const r = item.getBoundingClientRect()\n mids.push({ x: r.left + r.width / 2, y: r.top + r.height / 2 })\n const itemId = item.dataset.id\n if (itemId !== undefined) idToIndex.set(itemId, i)\n })\n snapshots.set(cid, { mids, idToIndex })\n }\n\n function snapshotAll(): void {\n const roots = document.querySelectorAll<HTMLElement>(\n '[data-scope=\"sortable\"][data-part=\"root\"]',\n )\n for (const root of roots) {\n const cid = root.dataset.containerId\n if (cid) snapshotContainer(root, cid)\n }\n }\n\n // Find the target index under the pointer using the drag-start snapshot.\n // 1D mode: picks by Y-only distance (original behavior). 2D mode: picks\n // by Euclidean distance over {x, y} midpoints — required for flex-wrap\n // / grid layouts where multiple items share a row and collapse to the\n // same Y value. Both modes are stable against items being visually\n // transformed during the drag because midpoints are taken pre-transform.\n function findTargetAt(e: PointerEvent): { container: string; index: number } | null {\n const roots = document.querySelectorAll<HTMLElement>(\n '[data-scope=\"sortable\"][data-part=\"root\"]',\n )\n for (const root of roots) {\n const r = root.getBoundingClientRect()\n if (e.clientX < r.left || e.clientX > r.right) continue\n if (e.clientY < r.top || e.clientY > r.bottom) continue\n const cid = root.dataset.containerId\n if (!cid) continue\n const snap = snapshots.get(cid)\n if (!snap || snap.mids.length === 0) return { container: cid, index: 0 }\n const mids = snap.mids\n let bestIdx = 0\n if (layout === '2d') {\n // Euclidean (squared — monotonic with distance, saves a sqrt).\n const dx0 = e.clientX - mids[0]!.x\n const dy0 = e.clientY - mids[0]!.y\n let bestDist = dx0 * dx0 + dy0 * dy0\n for (let i = 1; i < mids.length; i++) {\n const dx = e.clientX - mids[i]!.x\n const dy = e.clientY - mids[i]!.y\n const d = dx * dx + dy * dy\n if (d < bestDist) {\n bestDist = d\n bestIdx = i\n }\n }\n } else {\n // 1D — Y-only distance. Preserves the original behavior for\n // vertical lists; same-row items in a flex-wrap would tie and\n // the first match wins, which is the bug that motivates 2D.\n let bestDist = Math.abs(e.clientY - mids[0]!.y)\n for (let i = 1; i < mids.length; i++) {\n const d = Math.abs(e.clientY - mids[i]!.y)\n if (d < bestDist) {\n bestDist = d\n bestIdx = i\n }\n }\n }\n return { container: cid, index: bestIdx }\n }\n return null\n }\n\n return {\n root: {\n 'data-scope': 'sortable',\n 'data-part': 'root',\n 'data-container-id': containerId,\n 'data-dragging': (s) => (get(s).dragging ? '' : undefined),\n onPointerMove: (e) => {\n if (!e.buttons) return\n const hit = findTargetAt(e)\n if (hit !== null)\n send({\n type: 'move',\n index: hit.index,\n container: hit.container,\n x: e.clientX,\n y: e.clientY,\n })\n },\n onPointerUp: () => {\n snapshots.clear()\n send({ type: 'drop' })\n },\n onPointerCancel: () => {\n snapshots.clear()\n send({ type: 'cancel' })\n },\n },\n item: (id, index) => ({\n 'data-scope': 'sortable',\n 'data-part': 'item',\n 'data-index': String(index),\n 'data-id': id,\n 'data-dragging': (s) => {\n const d = get(s).dragging\n return d?.id === id && d?.fromContainer === containerId ? '' : undefined\n },\n 'data-over': (s) => {\n const d = get(s).dragging\n if (!d || d.toContainer !== containerId) return undefined\n // Look up this item's CURRENT DOM index via the drag-start snapshot.\n // The `index` closed over here is frozen at initial render and goes\n // stale after each() reconciles a reorder.\n const snap = snapshots.get(containerId)\n const liveIndex = snap?.idToIndex.get(id) ?? index\n return d.currentIndex === liveIndex ? '' : undefined\n },\n // Shift direction for items BETWEEN the source and target (excluding the\n // dragged item itself). 'down' = item should translate down to make room;\n // 'up' = item should translate up. CSS controls the actual displacement.\n //\n // In 2D layout, `data-shift` is always undefined — the per-item\n // `style.transform` below opens the correct gap directly. Keeping\n // `data-shift` out of the 2D path prevents any author-provided\n // CSS rule like `[data-shift] { translate: 0 var(--sortable-shift) }`\n // from fighting with the computed transform.\n 'data-shift': (s) => {\n if (layout === '2d') return undefined\n const d = get(s).dragging\n if (!d || d.fromContainer !== containerId || d.toContainer !== containerId) return undefined\n if (d.id === id) return undefined\n if (d.startIndex === d.currentIndex) return undefined\n // Look up this item's live DOM index — see note on data-over.\n const snap = snapshots.get(containerId)\n const liveIndex = snap?.idToIndex.get(id) ?? index\n if (d.startIndex < d.currentIndex) {\n // Dragging down: items between start+1 and current shift up\n if (liveIndex > d.startIndex && liveIndex <= d.currentIndex) return 'up'\n } else {\n // Dragging up: items between current and start-1 shift down\n if (liveIndex >= d.currentIndex && liveIndex < d.startIndex) return 'down'\n }\n return undefined\n },\n // The dragged item follows the pointer. In 1D, translateY only; in\n // 2D, both axes. Non-dragged items in 2D between source and target\n // get a per-item translate computed from the snapshot — each item's\n // vector is `snapshot[newSlot] - snapshot[ownSlot]` so the gap\n // opens correctly regardless of row wrap. In 1D, non-dragged items\n // emit `undefined` here and rely on the consumer's CSS `data-shift`\n // rule.\n 'style.transform': (s) => {\n const d = get(s).dragging\n if (!d) return undefined\n const isDragged = d.id === id && d.fromContainer === containerId\n if (isDragged) {\n const deltaY = d.currentY - d.startY\n if (layout === '2d') {\n const deltaX = d.currentX - d.startX\n return `translate(${deltaX}px, ${deltaY}px)`\n }\n return `translateY(${deltaY}px)`\n }\n // Non-dragged items: per-item displacement in 2D only.\n if (layout !== '2d') return undefined\n if (d.fromContainer !== containerId || d.toContainer !== containerId) return undefined\n if (d.startIndex === d.currentIndex) return undefined\n const snap = snapshots.get(containerId)\n if (!snap) return undefined\n const liveIndex = snap.idToIndex.get(id)\n if (liveIndex === undefined) return undefined\n // Which slot this item should visually occupy while the drag\n // previews the reorder:\n // drag-down (start < current): items at liveIndex in\n // (start .. current] shift left-by-one in array order, so\n // they take the slot at liveIndex - 1.\n // drag-up (current < start): items at liveIndex in\n // [current .. start) shift right-by-one, take slot\n // liveIndex + 1.\n let targetSlot: number\n if (d.startIndex < d.currentIndex) {\n if (liveIndex <= d.startIndex || liveIndex > d.currentIndex) return undefined\n targetSlot = liveIndex - 1\n } else {\n if (liveIndex < d.currentIndex || liveIndex >= d.startIndex) return undefined\n targetSlot = liveIndex + 1\n }\n const own = snap.mids[liveIndex]\n const target = snap.mids[targetSlot]\n if (!own || !target) return undefined\n const dx = target.x - own.x\n const dy = target.y - own.y\n return `translate(${dx}px, ${dy}px)`\n },\n 'style.zIndex': (s) => {\n const d = get(s).dragging\n if (!d || d.id !== id || d.fromContainer !== containerId) return undefined\n return '10'\n },\n }),\n handle: (id, index) => ({\n 'data-scope': 'sortable',\n 'data-part': 'handle',\n role: 'button',\n tabIndex: 0,\n 'aria-grabbed': (s) => {\n const d = get(s).dragging\n return d?.id === id && d?.fromContainer === containerId\n },\n 'aria-label':\n 'Drag handle. Press space to pick up, arrow keys to move, space again to drop, escape to cancel.',\n onPointerDown: (e) => {\n e.preventDefault()\n const target = e.currentTarget as Element | null\n if (target && 'setPointerCapture' in target) {\n try {\n ;(target as Element & { setPointerCapture: (id: number) => void }).setPointerCapture(\n e.pointerId,\n )\n } catch {\n // Ignore — not all elements support pointer capture\n }\n }\n // Compute the CURRENT DOM index of this handle's item — the captured\n // `index` param is stale after a reorder (each() moves keyed nodes\n // without re-running render, so the closure's index is frozen at\n // initial mount). Walk up to find the containing item, then count its\n // position among sibling items.\n let currentIndex = index\n if (target) {\n const itemEl = (target as Element).closest<HTMLElement>(\n '[data-scope=\"sortable\"][data-part=\"item\"]',\n )\n const rootEl = (target as Element).closest<HTMLElement>(\n '[data-scope=\"sortable\"][data-part=\"root\"]',\n )\n if (itemEl && rootEl) {\n const items = rootEl.querySelectorAll<HTMLElement>(\n '[data-scope=\"sortable\"][data-part=\"item\"]',\n )\n for (let i = 0; i < items.length; i++) {\n if (items[i] === itemEl) {\n currentIndex = i\n break\n }\n }\n }\n }\n // Snapshot positions BEFORE the drag starts, so subsequent pointermove\n // events can resolve the target index against stable (pre-transform)\n // positions. Otherwise items shifting via CSS would cause the target\n // to oscillate as elementFromPoint hits different items.\n snapshotAll()\n send({\n type: 'start',\n id,\n index: currentIndex,\n container: containerId,\n x: e.clientX,\n y: e.clientY,\n })\n },\n onKeyDown: (e) => {\n switch (e.key) {\n case ' ':\n case 'Enter':\n e.preventDefault()\n send({ type: 'toggleGrab', id, index, container: containerId })\n return\n case 'Escape':\n e.preventDefault()\n send({ type: 'cancel' })\n return\n case 'ArrowDown':\n case 'ArrowRight':\n e.preventDefault()\n send({ type: 'moveBy', delta: 1 })\n return\n case 'ArrowUp':\n case 'ArrowLeft':\n e.preventDefault()\n send({ type: 'moveBy', delta: -1 })\n return\n }\n },\n }),\n }\n}\n\n// ── Reorder utility ────────────────────────────────────────────\n\n/**\n * Move an item in an array from one index to another, returning a new array.\n * Out-of-range indices are clamped to array bounds.\n */\nexport function reorder<T>(arr: readonly T[], from: number, to: number): T[] {\n const len = arr.length\n if (len === 0) return []\n const f = Math.max(0, Math.min(len - 1, from))\n const t = Math.max(0, Math.min(len - 1, to))\n if (f === t) return arr.slice()\n const result = arr.slice()\n const [item] = result.splice(f, 1)\n result.splice(t, 0, item)\n return result\n}\n\nexport const sortable = { init, update, connect, reorder }\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llui/components",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.29",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -457,12 +457,12 @@
|
|
|
457
457
|
}
|
|
458
458
|
},
|
|
459
459
|
"peerDependencies": {
|
|
460
|
-
"@llui/dom": "^0.0.
|
|
460
|
+
"@llui/dom": "^0.0.29"
|
|
461
461
|
},
|
|
462
462
|
"devDependencies": {
|
|
463
463
|
"typescript": "^6.0.0",
|
|
464
464
|
"vitest": "^4.1.2",
|
|
465
|
-
"@llui/dom": "0.0.
|
|
465
|
+
"@llui/dom": "0.0.29"
|
|
466
466
|
},
|
|
467
467
|
"sideEffects": [
|
|
468
468
|
"./dist/styles/theme.css",
|