@happyvertical/smrt-svelte 0.55.1 → 0.55.3
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/README.md +29 -2
- package/agents/components.md +44 -1
- package/agents/workspace.md +77 -2
- package/dist/components/app/AppShell.svelte +121 -9
- package/dist/components/app/AppShell.svelte.d.ts +24 -2
- package/dist/components/app/AppShell.svelte.d.ts.map +1 -1
- package/dist/components/app/DockToggles.svelte +103 -0
- package/dist/components/app/DockToggles.svelte.d.ts +8 -0
- package/dist/components/app/DockToggles.svelte.d.ts.map +1 -0
- package/dist/components/app/dock-toggle.d.ts +14 -0
- package/dist/components/app/dock-toggle.d.ts.map +1 -0
- package/dist/components/app/dock-toggle.js +1 -0
- package/dist/components/app/index.d.ts +2 -0
- package/dist/components/app/index.d.ts.map +1 -1
- package/dist/components/app/index.js +1 -0
- package/dist/components/board/Board.svelte +92 -394
- package/dist/components/board/Board.svelte.d.ts.map +1 -1
- package/dist/components/forms/RelationInput.svelte +379 -0
- package/dist/components/forms/RelationInput.svelte.d.ts +75 -0
- package/dist/components/forms/RelationInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/index.d.ts +2 -1
- package/dist/components/forms/index.d.ts.map +1 -1
- package/dist/components/forms/index.js +1 -0
- package/dist/components/forms/types.d.ts +9 -0
- package/dist/components/forms/types.d.ts.map +1 -1
- package/dist/components/sortable/Sortable.svelte +359 -0
- package/dist/components/sortable/Sortable.svelte.d.ts +26 -0
- package/dist/components/sortable/Sortable.svelte.d.ts.map +1 -0
- package/dist/components/sortable/controller.svelte.d.ts +152 -0
- package/dist/components/sortable/controller.svelte.d.ts.map +1 -0
- package/dist/components/sortable/controller.svelte.js +453 -0
- package/dist/components/sortable/index.d.ts +5 -0
- package/dist/components/sortable/index.d.ts.map +1 -0
- package/dist/components/sortable/index.js +2 -0
- package/dist/components/sortable/types.d.ts +80 -0
- package/dist/components/sortable/types.d.ts.map +1 -0
- package/dist/components/sortable/types.js +1 -0
- package/dist/components/workspace/admin-shell/AdminShell.svelte +49 -1
- package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts.map +1 -1
- package/dist/components/workspace/admin-shell/ShellLayoutEditor.svelte +196 -0
- package/dist/components/workspace/admin-shell/ShellLayoutEditor.svelte.d.ts +17 -0
- package/dist/components/workspace/admin-shell/ShellLayoutEditor.svelte.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/TenantNav.svelte +69 -3
- package/dist/components/workspace/admin-shell/TenantNav.svelte.d.ts.map +1 -1
- package/dist/components/workspace/admin-shell/dock.d.ts +37 -0
- package/dist/components/workspace/admin-shell/dock.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/dock.js +25 -0
- package/dist/components/workspace/admin-shell/layout-context.d.ts +7 -0
- package/dist/components/workspace/admin-shell/layout-context.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/layout-context.js +17 -0
- package/dist/components/workspace/admin-shell/layout-controller.svelte.d.ts +69 -0
- package/dist/components/workspace/admin-shell/layout-controller.svelte.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/layout-controller.svelte.js +91 -0
- package/dist/components/workspace/admin-shell/layout.d.ts +120 -0
- package/dist/components/workspace/admin-shell/layout.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/layout.js +402 -0
- package/dist/components/workspace/admin-shell/settings.d.ts +6 -4
- package/dist/components/workspace/admin-shell/settings.d.ts.map +1 -1
- package/dist/components/workspace/admin-shell/settings.js +14 -4
- package/dist/components/workspace/admin-shell/state.svelte.d.ts +59 -1
- package/dist/components/workspace/admin-shell/state.svelte.d.ts.map +1 -1
- package/dist/components/workspace/admin-shell/state.svelte.js +146 -2
- package/dist/components/workspace/admin-shell/types.d.ts +52 -0
- package/dist/components/workspace/admin-shell/types.d.ts.map +1 -1
- package/dist/components/workspace/index.d.ts +6 -1
- package/dist/components/workspace/index.d.ts.map +1 -1
- package/dist/components/workspace/index.js +5 -0
- package/dist/i18n/server.d.ts +1 -0
- package/dist/i18n/server.d.ts.map +1 -1
- package/dist/i18n/server.js +1 -0
- package/dist/i18n/strings.sortable.d.ts +13 -0
- package/dist/i18n/strings.sortable.d.ts.map +1 -0
- package/dist/i18n/strings.sortable.js +13 -0
- package/dist/i18n/strings.workspace.d.ts +13 -0
- package/dist/i18n/strings.workspace.d.ts.map +1 -1
- package/dist/i18n/strings.workspace.js +13 -0
- package/package.json +26 -15
|
@@ -1,31 +1,20 @@
|
|
|
1
1
|
<script lang="ts" generics="Card extends BoardCard, Column extends BoardColumn">
|
|
2
2
|
import { useI18n } from '@happyvertical/smrt-ui/i18n';
|
|
3
3
|
import { BROWSER } from 'esm-env';
|
|
4
|
-
import {
|
|
4
|
+
import { untrack } from 'svelte';
|
|
5
5
|
import { M } from '../../i18n/strings.board.js';
|
|
6
|
+
import {
|
|
7
|
+
createSortable,
|
|
8
|
+
type SortableAnnouncement,
|
|
9
|
+
type SortableMove,
|
|
10
|
+
} from '../sortable/controller.svelte.js';
|
|
6
11
|
import type {
|
|
7
12
|
BoardCard,
|
|
8
13
|
BoardColumn,
|
|
9
14
|
BoardMoveIntent,
|
|
10
|
-
BoardPosition,
|
|
11
15
|
BoardProps,
|
|
12
16
|
} from './types.js';
|
|
13
17
|
|
|
14
|
-
interface DragState {
|
|
15
|
-
cardId: string;
|
|
16
|
-
source: BoardPosition;
|
|
17
|
-
target: BoardPosition;
|
|
18
|
-
mode: 'keyboard' | 'pointer';
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface PointerSession {
|
|
22
|
-
cardId: string;
|
|
23
|
-
pointerId: number;
|
|
24
|
-
startX: number;
|
|
25
|
-
startY: number;
|
|
26
|
-
active: boolean;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
18
|
let {
|
|
30
19
|
columns,
|
|
31
20
|
cards,
|
|
@@ -50,13 +39,7 @@ let root: HTMLElement;
|
|
|
50
39
|
let localCards = $state<Card[]>(untrack(() => [...defaultCards]));
|
|
51
40
|
let optimisticCards = $state<Card[] | undefined>();
|
|
52
41
|
let lastAuthoritativeCards = untrack(() => cards);
|
|
53
|
-
let drag = $state<DragState | undefined>();
|
|
54
42
|
let collapsed = $state<Set<string>>(new Set());
|
|
55
|
-
let announcement = $state('');
|
|
56
|
-
let focusCardId = $state<string | undefined>();
|
|
57
|
-
let suppressedSelectionCardId = $state<string | undefined>();
|
|
58
|
-
let movePending = $state(false);
|
|
59
|
-
let pointerSession = $state<PointerSession | undefined>();
|
|
60
43
|
|
|
61
44
|
// A new owner array is an explicit reconciliation point. Do not clear an
|
|
62
45
|
// optimistic move merely because this component has re-rendered.
|
|
@@ -80,23 +63,6 @@ const cardsByColumn = $derived.by(() => {
|
|
|
80
63
|
}
|
|
81
64
|
return next;
|
|
82
65
|
});
|
|
83
|
-
const movable = $derived(
|
|
84
|
-
(cards === undefined || onmove !== undefined) && !movePending,
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
$effect(() => {
|
|
88
|
-
if (!BROWSER) return;
|
|
89
|
-
if (!focusCardId) return;
|
|
90
|
-
const cardId = focusCardId;
|
|
91
|
-
focusCardId = undefined;
|
|
92
|
-
tick().then(() => {
|
|
93
|
-
Array.from(
|
|
94
|
-
root.querySelectorAll<HTMLButtonElement>('[data-smrt-board-card-id]'),
|
|
95
|
-
)
|
|
96
|
-
.find((element) => element.dataset.smrtBoardCardId === cardId)
|
|
97
|
-
?.focus();
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
66
|
|
|
101
67
|
function laneListId(index: number): string {
|
|
102
68
|
return `${instanceId}-lane-${index}`;
|
|
@@ -117,32 +83,17 @@ function findColumn(columnId: string): Column | undefined {
|
|
|
117
83
|
return columns.find((column) => column.id === columnId);
|
|
118
84
|
}
|
|
119
85
|
|
|
120
|
-
function
|
|
121
|
-
|
|
122
|
-
if (!item) return undefined;
|
|
123
|
-
const columnId = getCardColumnId(item);
|
|
124
|
-
const index = cardsInColumn(columnId).findIndex(
|
|
125
|
-
(candidate) => candidate.id === cardId,
|
|
126
|
-
);
|
|
127
|
-
return index < 0 ? undefined : { columnId, index };
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function clamp(index: number, max: number): number {
|
|
131
|
-
return Math.max(0, Math.min(index, max));
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function createIntent(
|
|
135
|
-
state: DragState,
|
|
86
|
+
function intentFor(
|
|
87
|
+
move: SortableMove,
|
|
136
88
|
): BoardMoveIntent<Card, Column> | undefined {
|
|
137
|
-
const movingCard = findCard(
|
|
138
|
-
const sourceColumn = findColumn(
|
|
139
|
-
const targetColumn = findColumn(
|
|
140
|
-
if (!movingCard || !sourceColumn || !targetColumn
|
|
141
|
-
return undefined;
|
|
89
|
+
const movingCard = findCard(move.itemId);
|
|
90
|
+
const sourceColumn = findColumn(move.source.containerId);
|
|
91
|
+
const targetColumn = findColumn(move.target.containerId);
|
|
92
|
+
if (!movingCard || !sourceColumn || !targetColumn) return undefined;
|
|
142
93
|
return {
|
|
143
94
|
card: movingCard,
|
|
144
|
-
source:
|
|
145
|
-
target:
|
|
95
|
+
source: { columnId: move.source.containerId, index: move.source.index },
|
|
96
|
+
target: { columnId: move.target.containerId, index: move.target.index },
|
|
146
97
|
sourceColumn,
|
|
147
98
|
targetColumn,
|
|
148
99
|
};
|
|
@@ -176,327 +127,74 @@ function moveCards(intent: BoardMoveIntent<Card, Column>): Card[] {
|
|
|
176
127
|
];
|
|
177
128
|
}
|
|
178
129
|
|
|
179
|
-
function
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
announcement = t(M['ui.board.pickup'], {
|
|
186
|
-
card: getCardLabel(movingCard),
|
|
187
|
-
destinations: columns
|
|
188
|
-
.filter((column) => !column.disabled)
|
|
189
|
-
.map((column) => column.label)
|
|
190
|
-
.join(', '),
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function announcePosition(state: DragState): void {
|
|
195
|
-
const movingCard = findCard(state.cardId);
|
|
196
|
-
const column = findColumn(state.target.columnId);
|
|
197
|
-
if (!movingCard || !column) return;
|
|
198
|
-
const count = cardsInColumn(column.id, state.cardId).length + 1;
|
|
199
|
-
announcement = t(M['ui.board.position'], {
|
|
200
|
-
card: getCardLabel(movingCard),
|
|
201
|
-
column: column.label,
|
|
202
|
-
position: state.target.index + 1,
|
|
203
|
-
count,
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
function moveKeyboardTarget(
|
|
208
|
-
direction: 'horizontal' | 'vertical',
|
|
209
|
-
delta: number,
|
|
210
|
-
): void {
|
|
211
|
-
if (drag?.mode !== 'keyboard') return;
|
|
212
|
-
const target = { ...drag.target };
|
|
213
|
-
if (direction === 'vertical') {
|
|
214
|
-
if (!allowSameColumnReorder) return;
|
|
215
|
-
target.index = clamp(
|
|
216
|
-
target.index + delta,
|
|
217
|
-
cardsInColumn(target.columnId, drag.cardId).length,
|
|
218
|
-
);
|
|
219
|
-
} else {
|
|
220
|
-
const currentIndex = columns.findIndex(
|
|
221
|
-
(column) => column.id === target.columnId,
|
|
222
|
-
);
|
|
223
|
-
const candidate = columns[currentIndex + delta];
|
|
224
|
-
if (!candidate) return;
|
|
225
|
-
if (candidate.disabled) {
|
|
226
|
-
announcement = t(M['ui.board.unavailable_column'], {
|
|
227
|
-
column: candidate.label,
|
|
130
|
+
function message(announcement: SortableAnnouncement): string {
|
|
131
|
+
switch (announcement.type) {
|
|
132
|
+
case 'pickup':
|
|
133
|
+
return t(M['ui.board.pickup'], {
|
|
134
|
+
card: announcement.item,
|
|
135
|
+
destinations: announcement.destinations.join(', '),
|
|
228
136
|
});
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
if (!changed) {
|
|
252
|
-
focusCardId = intent.card.id;
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
const reordered = moveCards(intent);
|
|
256
|
-
expandColumn(intent.target.columnId);
|
|
257
|
-
if (cards !== undefined && optimistic) optimisticCards = reordered;
|
|
258
|
-
movePending = true;
|
|
259
|
-
try {
|
|
260
|
-
await onmove?.(intent);
|
|
261
|
-
} catch {
|
|
262
|
-
optimisticCards = undefined;
|
|
263
|
-
announcement = t(M['ui.board.move_failed'], {
|
|
264
|
-
card: getCardLabel(intent.card),
|
|
265
|
-
});
|
|
266
|
-
focusCardId = intent.card.id;
|
|
267
|
-
return;
|
|
268
|
-
} finally {
|
|
269
|
-
movePending = false;
|
|
270
|
-
}
|
|
271
|
-
if (cards === undefined) localCards = reordered;
|
|
272
|
-
const count =
|
|
273
|
-
cardsInColumn(intent.target.columnId, intent.card.id).length + 1;
|
|
274
|
-
announcement = t(M['ui.board.drop'], {
|
|
275
|
-
card: getCardLabel(intent.card),
|
|
276
|
-
column: intent.targetColumn.label,
|
|
277
|
-
position: intent.target.index + 1,
|
|
278
|
-
count,
|
|
279
|
-
});
|
|
280
|
-
focusCardId = intent.card.id;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
function cancel(announce = true): void {
|
|
284
|
-
if (!drag) return;
|
|
285
|
-
const movingCard = findCard(drag.cardId);
|
|
286
|
-
if (movingCard && announce) {
|
|
287
|
-
announcement = t(M['ui.board.cancel'], { card: getCardLabel(movingCard) });
|
|
288
|
-
}
|
|
289
|
-
if (movingCard) {
|
|
290
|
-
focusCardId = movingCard.id;
|
|
291
|
-
}
|
|
292
|
-
suppressSelection(drag.cardId);
|
|
293
|
-
drag = undefined;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
function suppressSelection(cardId: string): void {
|
|
297
|
-
suppressedSelectionCardId = cardId;
|
|
298
|
-
window.setTimeout(() => {
|
|
299
|
-
if (suppressedSelectionCardId === cardId)
|
|
300
|
-
suppressedSelectionCardId = undefined;
|
|
301
|
-
}, 0);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
function selectCard(item: Card): void {
|
|
305
|
-
// Keyboard pickup/drop and native drag gestures can both dispatch a click
|
|
306
|
-
// after their key/pointer sequence. Consume it so a move never doubles as
|
|
307
|
-
// an application-level selection.
|
|
308
|
-
if (suppressedSelectionCardId === item.id) {
|
|
309
|
-
suppressedSelectionCardId = undefined;
|
|
310
|
-
return;
|
|
137
|
+
case 'position':
|
|
138
|
+
return t(M['ui.board.position'], {
|
|
139
|
+
card: announcement.item,
|
|
140
|
+
column: announcement.container,
|
|
141
|
+
position: announcement.position,
|
|
142
|
+
count: announcement.count,
|
|
143
|
+
});
|
|
144
|
+
case 'drop':
|
|
145
|
+
return t(M['ui.board.drop'], {
|
|
146
|
+
card: announcement.item,
|
|
147
|
+
column: announcement.container,
|
|
148
|
+
position: announcement.position,
|
|
149
|
+
count: announcement.count,
|
|
150
|
+
});
|
|
151
|
+
case 'unavailable':
|
|
152
|
+
return t(M['ui.board.unavailable_column'], {
|
|
153
|
+
column: announcement.container,
|
|
154
|
+
});
|
|
155
|
+
case 'failed':
|
|
156
|
+
return t(M['ui.board.move_failed'], { card: announcement.item });
|
|
157
|
+
case 'cancel':
|
|
158
|
+
return t(M['ui.board.cancel'], { card: announcement.item });
|
|
311
159
|
}
|
|
312
|
-
if (!drag) onselect?.(item);
|
|
313
160
|
}
|
|
314
161
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
162
|
+
const sortable = createSortable({
|
|
163
|
+
root: () => root,
|
|
164
|
+
selectors: {
|
|
165
|
+
container: '[data-smrt-board-column-id]',
|
|
166
|
+
containerKey: 'smrtBoardColumnId',
|
|
167
|
+
item: '[data-smrt-board-card-id]',
|
|
168
|
+
itemKey: 'smrtBoardCardId',
|
|
169
|
+
},
|
|
170
|
+
containers: () => columns,
|
|
171
|
+
itemIds: (columnId) => cardsInColumn(columnId).map((item) => item.id),
|
|
172
|
+
itemLabel: (cardId) => {
|
|
173
|
+
const found = findCard(cardId);
|
|
174
|
+
return found ? getCardLabel(found) : undefined;
|
|
175
|
+
},
|
|
176
|
+
allowSameContainerReorder: () => allowSameColumnReorder,
|
|
177
|
+
enabled: () => cards === undefined || onmove !== undefined,
|
|
178
|
+
announce: message,
|
|
179
|
+
async commit(move) {
|
|
180
|
+
const intent = intentFor(move);
|
|
181
|
+
if (!intent) return;
|
|
182
|
+
const reordered = moveCards(intent);
|
|
183
|
+
expandColumn(intent.target.columnId);
|
|
184
|
+
if (cards !== undefined && optimistic) optimisticCards = reordered;
|
|
185
|
+
try {
|
|
186
|
+
await onmove?.(intent);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
optimisticCards = undefined;
|
|
189
|
+
throw error;
|
|
321
190
|
}
|
|
322
|
-
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
if (event.key === 'Escape') {
|
|
326
|
-
event.preventDefault();
|
|
327
|
-
cancel();
|
|
328
|
-
} else if (event.key === ' ' || event.key === 'Enter') {
|
|
329
|
-
event.preventDefault();
|
|
330
|
-
void drop();
|
|
331
|
-
} else if (event.key === 'ArrowLeft') {
|
|
332
|
-
event.preventDefault();
|
|
333
|
-
moveKeyboardTarget('horizontal', -1);
|
|
334
|
-
} else if (event.key === 'ArrowRight') {
|
|
335
|
-
event.preventDefault();
|
|
336
|
-
moveKeyboardTarget('horizontal', 1);
|
|
337
|
-
} else if (event.key === 'ArrowUp') {
|
|
338
|
-
event.preventDefault();
|
|
339
|
-
moveKeyboardTarget('vertical', -1);
|
|
340
|
-
} else if (event.key === 'ArrowDown') {
|
|
341
|
-
event.preventDefault();
|
|
342
|
-
moveKeyboardTarget('vertical', 1);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
function handleDragStart(event: DragEvent, cardId: string): void {
|
|
347
|
-
if (!movable) {
|
|
348
|
-
event.preventDefault();
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
pickup(cardId, 'pointer');
|
|
352
|
-
event.dataTransfer?.setData('text/plain', cardId);
|
|
353
|
-
if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move';
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
function handlePointerDown(event: PointerEvent, cardId: string): void {
|
|
357
|
-
if (!movable || event.button !== 0) return;
|
|
358
|
-
pointerSession = {
|
|
359
|
-
cardId,
|
|
360
|
-
pointerId: event.pointerId,
|
|
361
|
-
startX: event.clientX,
|
|
362
|
-
startY: event.clientY,
|
|
363
|
-
active: false,
|
|
364
|
-
};
|
|
365
|
-
const target = event.currentTarget as HTMLElement;
|
|
366
|
-
target.setPointerCapture?.(event.pointerId);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
function elementAtPointer(event: PointerEvent): Element | null {
|
|
370
|
-
return (
|
|
371
|
-
document.elementFromPoint?.(event.clientX, event.clientY) ??
|
|
372
|
-
(event.target as Element | null)
|
|
373
|
-
);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
function setPointerTargetFromElement(
|
|
377
|
-
element: Element | null,
|
|
378
|
-
clientY: number,
|
|
379
|
-
): Column | undefined {
|
|
380
|
-
// Column ids belong to the consumer's domain and may repeat in another
|
|
381
|
-
// Board instance. Pointer hit-testing must never escape this Board's root.
|
|
382
|
-
if (!element || !root.contains(element)) return undefined;
|
|
383
|
-
const lane = element?.closest<HTMLElement>('[data-smrt-board-column-id]');
|
|
384
|
-
const columnId = lane?.dataset.smrtBoardColumnId;
|
|
385
|
-
if (!columnId) return undefined;
|
|
386
|
-
const column = findColumn(columnId);
|
|
387
|
-
if (!column || column.disabled) return column;
|
|
388
|
-
const cardElement = element?.closest<HTMLElement>(
|
|
389
|
-
'[data-smrt-board-card-id]',
|
|
390
|
-
);
|
|
391
|
-
if (!cardElement) {
|
|
392
|
-
pointerTarget(columnId, cardsInColumn(columnId, drag?.cardId).length);
|
|
393
|
-
return column;
|
|
394
|
-
}
|
|
395
|
-
const rawIndex = cardsInColumn(columnId).findIndex(
|
|
396
|
-
(item) => item.id === cardElement.dataset.smrtBoardCardId,
|
|
397
|
-
);
|
|
398
|
-
if (rawIndex < 0) return column;
|
|
399
|
-
const rect = cardElement.getBoundingClientRect();
|
|
400
|
-
const before = clientY < rect.top + rect.height / 2;
|
|
401
|
-
let targetIndex = rawIndex + (before ? 0 : 1);
|
|
402
|
-
if (drag?.source.columnId === columnId && targetIndex > drag.source.index) {
|
|
403
|
-
targetIndex -= 1;
|
|
404
|
-
}
|
|
405
|
-
pointerTarget(columnId, targetIndex);
|
|
406
|
-
return column;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
function finishPointerSession(event: PointerEvent, cancelled = false): void {
|
|
410
|
-
const session = pointerSession;
|
|
411
|
-
pointerSession = undefined;
|
|
412
|
-
const target = event.currentTarget as HTMLElement;
|
|
413
|
-
if (target.hasPointerCapture?.(event.pointerId))
|
|
414
|
-
target.releasePointerCapture(event.pointerId);
|
|
415
|
-
if (!session?.active || drag?.cardId !== session.cardId) return;
|
|
416
|
-
if (cancelled) {
|
|
417
|
-
cancel();
|
|
418
|
-
return;
|
|
419
|
-
}
|
|
420
|
-
const column = setPointerTargetFromElement(
|
|
421
|
-
elementAtPointer(event),
|
|
422
|
-
event.clientY,
|
|
423
|
-
);
|
|
424
|
-
if (column?.disabled) {
|
|
425
|
-
announcement = t(M['ui.board.unavailable_column'], {
|
|
426
|
-
column: column.label,
|
|
427
|
-
});
|
|
428
|
-
cancel(false);
|
|
429
|
-
return;
|
|
430
|
-
}
|
|
431
|
-
if (!column) {
|
|
432
|
-
cancel();
|
|
433
|
-
return;
|
|
434
|
-
}
|
|
435
|
-
void drop();
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
function handlePointerMove(event: PointerEvent): void {
|
|
439
|
-
const session = pointerSession;
|
|
440
|
-
if (!session || session.pointerId !== event.pointerId) return;
|
|
441
|
-
if (!session.active) {
|
|
442
|
-
const distance = Math.hypot(
|
|
443
|
-
event.clientX - session.startX,
|
|
444
|
-
event.clientY - session.startY,
|
|
445
|
-
);
|
|
446
|
-
if (distance < 6) return;
|
|
447
|
-
pickup(session.cardId, 'pointer');
|
|
448
|
-
pointerSession = { ...session, active: true };
|
|
449
|
-
}
|
|
450
|
-
event.preventDefault();
|
|
451
|
-
setPointerTargetFromElement(elementAtPointer(event), event.clientY);
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
function pointerTarget(columnId: string, index: number): void {
|
|
455
|
-
if (drag?.mode !== 'pointer') return;
|
|
456
|
-
if (!allowSameColumnReorder && columnId === drag.source.columnId) {
|
|
457
|
-
drag = { ...drag, target: { ...drag.source } };
|
|
458
|
-
return;
|
|
459
|
-
}
|
|
460
|
-
const max = cardsInColumn(columnId, drag.cardId).length;
|
|
461
|
-
drag = { ...drag, target: { columnId, index: clamp(index, max) } };
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
function handleCardDrop(
|
|
465
|
-
event: DragEvent,
|
|
466
|
-
columnId: string,
|
|
467
|
-
rawIndex: number,
|
|
468
|
-
): void {
|
|
469
|
-
event.preventDefault();
|
|
470
|
-
if (drag?.mode !== 'pointer') return;
|
|
471
|
-
const targetColumn = findColumn(columnId);
|
|
472
|
-
if (targetColumn?.disabled) {
|
|
473
|
-
announcement = t(M['ui.board.unavailable_column'], {
|
|
474
|
-
column: targetColumn.label,
|
|
475
|
-
});
|
|
476
|
-
cancel(false);
|
|
477
|
-
return;
|
|
478
|
-
}
|
|
479
|
-
const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();
|
|
480
|
-
const before = event.clientY < rect.top + rect.height / 2;
|
|
481
|
-
let targetIndex = rawIndex + (before ? 0 : 1);
|
|
482
|
-
if (drag.source.columnId === columnId && targetIndex > drag.source.index) {
|
|
483
|
-
targetIndex -= 1;
|
|
484
|
-
}
|
|
485
|
-
pointerTarget(columnId, targetIndex);
|
|
486
|
-
void drop();
|
|
487
|
-
}
|
|
191
|
+
if (cards === undefined) localCards = reordered;
|
|
192
|
+
},
|
|
193
|
+
});
|
|
488
194
|
|
|
489
|
-
function
|
|
490
|
-
|
|
491
|
-
if (
|
|
492
|
-
announcement = t(M['ui.board.unavailable_column'], {
|
|
493
|
-
column: column.label,
|
|
494
|
-
});
|
|
495
|
-
cancel(false);
|
|
496
|
-
return;
|
|
497
|
-
}
|
|
498
|
-
pointerTarget(column.id, cardsInColumn(column.id, drag?.cardId).length);
|
|
499
|
-
void drop();
|
|
195
|
+
function selectCard(item: Card): void {
|
|
196
|
+
if (sortable.consumeClick(item.id)) return;
|
|
197
|
+
if (!sortable.drag) onselect?.(item);
|
|
500
198
|
}
|
|
501
199
|
|
|
502
200
|
function expandColumn(columnId: string): void {
|
|
@@ -551,32 +249,32 @@ function toggleColumn(columnId: string): void {
|
|
|
551
249
|
role="list"
|
|
552
250
|
data-smrt-board-column-id={column.id}
|
|
553
251
|
ondragover={(event) => event.preventDefault()}
|
|
554
|
-
ondrop={(event) =>
|
|
252
|
+
ondrop={(event) => sortable.dropOnContainer(event, column.id)}
|
|
555
253
|
>
|
|
556
254
|
{#each items as item, index (item.id)}
|
|
557
255
|
<div role="listitem">
|
|
558
256
|
<!-- raw-primitive-allow: native button owns keyboard pickup and HTML drag/drop -->
|
|
559
257
|
<button
|
|
560
258
|
type="button"
|
|
561
|
-
class:smrt-board__card--dragging={drag?.
|
|
562
|
-
class:smrt-board__card--touch-drag={movable}
|
|
259
|
+
class:smrt-board__card--dragging={sortable.drag?.itemId === item.id}
|
|
260
|
+
class:smrt-board__card--touch-drag={sortable.movable}
|
|
563
261
|
class="smrt-board__card"
|
|
564
262
|
data-smrt-board-card-id={item.id}
|
|
565
|
-
draggable={
|
|
566
|
-
aria-pressed={drag?.
|
|
567
|
-
aria-describedby={drag?.
|
|
263
|
+
draggable={sortable.nativeDraggable}
|
|
264
|
+
aria-pressed={sortable.drag?.itemId === item.id}
|
|
265
|
+
aria-describedby={sortable.drag?.itemId === item.id ? liveId : undefined}
|
|
568
266
|
onclick={() => selectCard(item)}
|
|
569
|
-
onkeydown={(event) =>
|
|
570
|
-
onpointerdown={(event) =>
|
|
571
|
-
onpointermove={
|
|
572
|
-
onpointerup={(event) =>
|
|
573
|
-
onpointercancel={(event) =>
|
|
574
|
-
ondragstart={(event) =>
|
|
575
|
-
ondragend={() =>
|
|
267
|
+
onkeydown={(event) => sortable.keydown(event, item.id)}
|
|
268
|
+
onpointerdown={(event) => sortable.pointerDown(event, item.id)}
|
|
269
|
+
onpointermove={(event) => sortable.pointerMove(event)}
|
|
270
|
+
onpointerup={(event) => sortable.pointerUp(event)}
|
|
271
|
+
onpointercancel={(event) => sortable.pointerCancel(event)}
|
|
272
|
+
ondragstart={(event) => sortable.dragStart(event, item.id)}
|
|
273
|
+
ondragend={() => sortable.dragEnd()}
|
|
576
274
|
ondragover={(event) => event.preventDefault()}
|
|
577
|
-
ondrop={(event) =>
|
|
275
|
+
ondrop={(event) => sortable.dropOnItem(event, column.id, index)}
|
|
578
276
|
>
|
|
579
|
-
{@render card({ card: item, column, index, isDragging: drag?.
|
|
277
|
+
{@render card({ card: item, column, index, isDragging: sortable.drag?.itemId === item.id })}
|
|
580
278
|
</button>
|
|
581
279
|
</div>
|
|
582
280
|
{:else}
|
|
@@ -589,7 +287,7 @@ function toggleColumn(columnId: string): void {
|
|
|
589
287
|
</section>
|
|
590
288
|
{/each}
|
|
591
289
|
</div>
|
|
592
|
-
<div id={liveId} class="smrt-board__live" aria-live="assertive" aria-atomic="true">{announcement}</div>
|
|
290
|
+
<div id={liveId} class="smrt-board__live" aria-live="assertive" aria-atomic="true">{sortable.announcement}</div>
|
|
593
291
|
</section>
|
|
594
292
|
|
|
595
293
|
<style>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Board.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/board/Board.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Board.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/board/Board.svelte.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,SAAS,EACT,WAAW,EAEX,UAAU,EACX,MAAM,YAAY,CAAC;AACpB,iBAAS,QAAQ,CAAC,IAAI,SAAS,SAAS,EAAE,MAAM,SAAS,WAAW;WA0PvC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC;;;;;EAA4E;AACjI,cAAM,iBAAiB,CAAC,IAAI,SAAS,SAAS,EAAC,MAAM,SAAS,WAAW;IACrE,KAAK,IAAI,UAAU,CAAC,OAAO,QAAQ,CAAC,IAAI,EAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IAC1D,MAAM,IAAI,UAAU,CAAC,OAAO,QAAQ,CAAC,IAAI,EAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;IAC5D,KAAK,IAAI,UAAU,CAAC,OAAO,QAAQ,CAAC,IAAI,EAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IAC1D,QAAQ;IACR,OAAO;CACV;AAED,UAAU,qBAAqB;IAC3B,KAAK,IAAI,SAAS,SAAS,EAAC,MAAM,SAAS,WAAW,EAAE,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,UAAU,CAAC,iBAAiB,CAAC,IAAI,EAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,UAAU,CAAC,iBAAiB,CAAC,IAAI,EAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,UAAU,CAAC,iBAAiB,CAAC,IAAI,EAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,UAAU,CAAC,iBAAiB,CAAC,IAAI,EAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,UAAU,CAAC,iBAAiB,CAAC,IAAI,EAAC,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC,CAAA;KAAE,GAAG,UAAU,CAAC,iBAAiB,CAAC,IAAI,EAAC,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IACxe,CAAC,IAAI,SAAS,SAAS,EAAC,MAAM,SAAS,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,iBAAiB,CAAC,IAAI,EAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,iBAAiB,CAAC,IAAI,EAAC,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC/L,YAAY,CAAC,EAAE,UAAU,CAAC,iBAAiB,CAAC,GAAG,EAAC,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;CACrE;AACD,QAAA,MAAM,KAAK,EAAE,qBAAmC,CAAC;AAC/B,KAAK,KAAK,CAAC,IAAI,SAAS,SAAS,EAAC,MAAM,SAAS,WAAW,IAAI,YAAY,CAAC,OAAO,KAAK,CAAC,IAAI,EAAC,MAAM,CAAC,CAAC,CAAC;AAC1G,eAAe,KAAK,CAAC"}
|