@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.
Files changed (77) hide show
  1. package/README.md +29 -2
  2. package/agents/components.md +44 -1
  3. package/agents/workspace.md +77 -2
  4. package/dist/components/app/AppShell.svelte +121 -9
  5. package/dist/components/app/AppShell.svelte.d.ts +24 -2
  6. package/dist/components/app/AppShell.svelte.d.ts.map +1 -1
  7. package/dist/components/app/DockToggles.svelte +103 -0
  8. package/dist/components/app/DockToggles.svelte.d.ts +8 -0
  9. package/dist/components/app/DockToggles.svelte.d.ts.map +1 -0
  10. package/dist/components/app/dock-toggle.d.ts +14 -0
  11. package/dist/components/app/dock-toggle.d.ts.map +1 -0
  12. package/dist/components/app/dock-toggle.js +1 -0
  13. package/dist/components/app/index.d.ts +2 -0
  14. package/dist/components/app/index.d.ts.map +1 -1
  15. package/dist/components/app/index.js +1 -0
  16. package/dist/components/board/Board.svelte +92 -394
  17. package/dist/components/board/Board.svelte.d.ts.map +1 -1
  18. package/dist/components/forms/RelationInput.svelte +379 -0
  19. package/dist/components/forms/RelationInput.svelte.d.ts +75 -0
  20. package/dist/components/forms/RelationInput.svelte.d.ts.map +1 -0
  21. package/dist/components/forms/index.d.ts +2 -1
  22. package/dist/components/forms/index.d.ts.map +1 -1
  23. package/dist/components/forms/index.js +1 -0
  24. package/dist/components/forms/types.d.ts +9 -0
  25. package/dist/components/forms/types.d.ts.map +1 -1
  26. package/dist/components/sortable/Sortable.svelte +359 -0
  27. package/dist/components/sortable/Sortable.svelte.d.ts +26 -0
  28. package/dist/components/sortable/Sortable.svelte.d.ts.map +1 -0
  29. package/dist/components/sortable/controller.svelte.d.ts +152 -0
  30. package/dist/components/sortable/controller.svelte.d.ts.map +1 -0
  31. package/dist/components/sortable/controller.svelte.js +453 -0
  32. package/dist/components/sortable/index.d.ts +5 -0
  33. package/dist/components/sortable/index.d.ts.map +1 -0
  34. package/dist/components/sortable/index.js +2 -0
  35. package/dist/components/sortable/types.d.ts +80 -0
  36. package/dist/components/sortable/types.d.ts.map +1 -0
  37. package/dist/components/sortable/types.js +1 -0
  38. package/dist/components/workspace/admin-shell/AdminShell.svelte +49 -1
  39. package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts.map +1 -1
  40. package/dist/components/workspace/admin-shell/ShellLayoutEditor.svelte +196 -0
  41. package/dist/components/workspace/admin-shell/ShellLayoutEditor.svelte.d.ts +17 -0
  42. package/dist/components/workspace/admin-shell/ShellLayoutEditor.svelte.d.ts.map +1 -0
  43. package/dist/components/workspace/admin-shell/TenantNav.svelte +69 -3
  44. package/dist/components/workspace/admin-shell/TenantNav.svelte.d.ts.map +1 -1
  45. package/dist/components/workspace/admin-shell/dock.d.ts +37 -0
  46. package/dist/components/workspace/admin-shell/dock.d.ts.map +1 -0
  47. package/dist/components/workspace/admin-shell/dock.js +25 -0
  48. package/dist/components/workspace/admin-shell/layout-context.d.ts +7 -0
  49. package/dist/components/workspace/admin-shell/layout-context.d.ts.map +1 -0
  50. package/dist/components/workspace/admin-shell/layout-context.js +17 -0
  51. package/dist/components/workspace/admin-shell/layout-controller.svelte.d.ts +69 -0
  52. package/dist/components/workspace/admin-shell/layout-controller.svelte.d.ts.map +1 -0
  53. package/dist/components/workspace/admin-shell/layout-controller.svelte.js +91 -0
  54. package/dist/components/workspace/admin-shell/layout.d.ts +120 -0
  55. package/dist/components/workspace/admin-shell/layout.d.ts.map +1 -0
  56. package/dist/components/workspace/admin-shell/layout.js +402 -0
  57. package/dist/components/workspace/admin-shell/settings.d.ts +6 -4
  58. package/dist/components/workspace/admin-shell/settings.d.ts.map +1 -1
  59. package/dist/components/workspace/admin-shell/settings.js +14 -4
  60. package/dist/components/workspace/admin-shell/state.svelte.d.ts +59 -1
  61. package/dist/components/workspace/admin-shell/state.svelte.d.ts.map +1 -1
  62. package/dist/components/workspace/admin-shell/state.svelte.js +146 -2
  63. package/dist/components/workspace/admin-shell/types.d.ts +52 -0
  64. package/dist/components/workspace/admin-shell/types.d.ts.map +1 -1
  65. package/dist/components/workspace/index.d.ts +6 -1
  66. package/dist/components/workspace/index.d.ts.map +1 -1
  67. package/dist/components/workspace/index.js +5 -0
  68. package/dist/i18n/server.d.ts +1 -0
  69. package/dist/i18n/server.d.ts.map +1 -1
  70. package/dist/i18n/server.js +1 -0
  71. package/dist/i18n/strings.sortable.d.ts +13 -0
  72. package/dist/i18n/strings.sortable.d.ts.map +1 -0
  73. package/dist/i18n/strings.sortable.js +13 -0
  74. package/dist/i18n/strings.workspace.d.ts +13 -0
  75. package/dist/i18n/strings.workspace.d.ts.map +1 -1
  76. package/dist/i18n/strings.workspace.js +13 -0
  77. package/package.json +26 -15
@@ -0,0 +1,453 @@
1
+ /**
2
+ * Headless drag-and-drop engine shared by `Board` and `Sortable`: keyboard
3
+ * pick-up and move, pointer (mouse, touch, pen) and native HTML drag, a
4
+ * single in-flight move at a time, focus return, and screen-reader
5
+ * announcements. It knows nothing about cards, columns, or navigation: a host
6
+ * describes ordered containers of item ids and receives one typed move.
7
+ *
8
+ * Create it during component initialization (it registers an effect that
9
+ * returns focus to the moved item).
10
+ */
11
+ import { BROWSER } from 'esm-env';
12
+ import { tick } from 'svelte';
13
+ function clamp(index, max) {
14
+ return Math.max(0, Math.min(index, max));
15
+ }
16
+ export class SortableController {
17
+ options;
18
+ drag = $state();
19
+ announcement = $state('');
20
+ focusItemId = $state();
21
+ suppressedSelectionId = $state();
22
+ pending = $state(false);
23
+ pointerSession = $state();
24
+ constructor(options) {
25
+ this.options = options;
26
+ $effect(() => {
27
+ if (!BROWSER)
28
+ return;
29
+ if (!this.focusItemId)
30
+ return;
31
+ const itemId = this.focusItemId;
32
+ this.focusItemId = undefined;
33
+ tick().then(() => {
34
+ const root = this.options.root();
35
+ const element = root
36
+ ? Array.from(root.querySelectorAll(this.options.selectors.item)).find((candidate) => candidate.dataset[this.options.selectors.itemKey] === itemId)
37
+ : undefined;
38
+ const target = element
39
+ ? this.options.focusTarget
40
+ ? this.options.focusTarget(element)
41
+ : element
42
+ : undefined;
43
+ target?.focus();
44
+ });
45
+ });
46
+ }
47
+ /** Whether a new move may start now. */
48
+ get movable() {
49
+ return this.options.enabled() && !this.pending;
50
+ }
51
+ /** Whether native HTML drag should be offered (not while a pointer session runs). */
52
+ get nativeDraggable() {
53
+ return this.movable && this.pointerSession === undefined;
54
+ }
55
+ get containers() {
56
+ return this.options.containers();
57
+ }
58
+ itemsIn(containerId, withoutId) {
59
+ const ids = this.options.itemIds(containerId);
60
+ return withoutId ? ids.filter((id) => id !== withoutId) : ids;
61
+ }
62
+ findContainer(id) {
63
+ return this.containers.find((container) => container.id === id);
64
+ }
65
+ positionFor(itemId) {
66
+ if (this.options.itemLabel(itemId) === undefined)
67
+ return undefined;
68
+ for (const container of this.containers) {
69
+ const index = this.itemsIn(container.id).indexOf(itemId);
70
+ if (index >= 0)
71
+ return { containerId: container.id, index };
72
+ }
73
+ return undefined;
74
+ }
75
+ label(itemId) {
76
+ return this.options.itemLabel(itemId) ?? '';
77
+ }
78
+ say(announcement) {
79
+ this.announcement = this.options.announce(announcement);
80
+ }
81
+ pickup(itemId, mode) {
82
+ if (!this.movable)
83
+ return;
84
+ const source = this.positionFor(itemId);
85
+ if (!source)
86
+ return;
87
+ this.drag = { itemId, source, target: { ...source }, mode };
88
+ this.say({
89
+ type: 'pickup',
90
+ item: this.label(itemId),
91
+ destinations: this.containers
92
+ .filter((container) => !container.disabled)
93
+ .map((container) => container.label),
94
+ });
95
+ }
96
+ announcePosition(state) {
97
+ const container = this.findContainer(state.target.containerId);
98
+ if (!container || this.options.itemLabel(state.itemId) === undefined) {
99
+ return;
100
+ }
101
+ this.say({
102
+ type: 'position',
103
+ item: this.label(state.itemId),
104
+ container: container.label,
105
+ position: state.target.index + 1,
106
+ count: this.itemsIn(container.id, state.itemId).length + 1,
107
+ });
108
+ }
109
+ moveKeyboardTarget(direction, delta) {
110
+ const drag = this.drag;
111
+ if (drag?.mode !== 'keyboard')
112
+ return;
113
+ const vertical = this.options.orientation?.() === 'vertical';
114
+ const target = { ...drag.target };
115
+ // With same-container reordering off, the source container pins the
116
+ // position (as it does for pointer moves): a vertical step leaves it.
117
+ const pinned = !this.options.allowSameContainerReorder() &&
118
+ target.containerId === drag.source.containerId;
119
+ if (direction === 'vertical') {
120
+ if (pinned && !vertical)
121
+ return;
122
+ const max = this.itemsIn(target.containerId, drag.itemId).length;
123
+ const next = pinned ? (delta < 0 ? -1 : max + 1) : target.index + delta;
124
+ if (vertical && (next < 0 || next > max)) {
125
+ const currentIndex = this.containers.findIndex((container) => container.id === target.containerId);
126
+ const candidate = this.containers[currentIndex + delta];
127
+ if (!candidate)
128
+ return;
129
+ if (candidate.disabled) {
130
+ this.say({ type: 'unavailable', container: candidate.label });
131
+ return;
132
+ }
133
+ target.containerId = candidate.id;
134
+ target.index =
135
+ delta < 0 ? this.itemsIn(candidate.id, drag.itemId).length : 0;
136
+ }
137
+ else {
138
+ target.index = clamp(next, max);
139
+ }
140
+ }
141
+ else {
142
+ if (vertical)
143
+ return;
144
+ const currentIndex = this.containers.findIndex((container) => container.id === target.containerId);
145
+ const candidate = this.containers[currentIndex + delta];
146
+ if (!candidate)
147
+ return;
148
+ if (candidate.disabled) {
149
+ this.say({ type: 'unavailable', container: candidate.label });
150
+ return;
151
+ }
152
+ target.containerId = candidate.id;
153
+ target.index = clamp(target.index, this.itemsIn(candidate.id, drag.itemId).length);
154
+ }
155
+ // Coming back into a pinned source container lands on its own slot.
156
+ if (!this.options.allowSameContainerReorder() &&
157
+ target.containerId === drag.source.containerId) {
158
+ target.index = drag.source.index;
159
+ }
160
+ this.drag = { ...drag, target };
161
+ this.announcePosition(this.drag);
162
+ }
163
+ async drop() {
164
+ const state = this.drag;
165
+ if (!state)
166
+ return;
167
+ const itemLabel = this.options.itemLabel(state.itemId);
168
+ const target = this.findContainer(state.target.containerId);
169
+ const valid = itemLabel !== undefined &&
170
+ this.findContainer(state.source.containerId) !== undefined &&
171
+ target !== undefined &&
172
+ !target.disabled;
173
+ this.drag = undefined;
174
+ this.suppressSelection(state.itemId);
175
+ if (!valid || itemLabel === undefined || !target)
176
+ return;
177
+ const changed = state.source.containerId !== state.target.containerId ||
178
+ state.source.index !== state.target.index;
179
+ if (!changed) {
180
+ this.focusItemId = state.itemId;
181
+ return;
182
+ }
183
+ this.pending = true;
184
+ try {
185
+ await this.options.commit({
186
+ itemId: state.itemId,
187
+ source: state.source,
188
+ target: state.target,
189
+ });
190
+ }
191
+ catch {
192
+ this.say({ type: 'failed', item: itemLabel });
193
+ this.focusItemId = state.itemId;
194
+ return;
195
+ }
196
+ finally {
197
+ this.pending = false;
198
+ }
199
+ this.say({
200
+ type: 'drop',
201
+ item: itemLabel,
202
+ container: target.label,
203
+ position: state.target.index + 1,
204
+ count: this.itemsIn(target.id, state.itemId).length + 1,
205
+ });
206
+ this.focusItemId = state.itemId;
207
+ }
208
+ /** Cancel the move in flight. Focus returns to the item when `restoreFocus`. */
209
+ cancel(announce = true, restoreFocus = true) {
210
+ const drag = this.drag;
211
+ if (!drag)
212
+ return;
213
+ const label = this.options.itemLabel(drag.itemId);
214
+ if (label !== undefined && announce) {
215
+ this.say({ type: 'cancel', item: label });
216
+ }
217
+ if (label !== undefined && restoreFocus)
218
+ this.focusItemId = drag.itemId;
219
+ this.suppressSelection(drag.itemId);
220
+ this.drag = undefined;
221
+ }
222
+ suppressSelection(itemId) {
223
+ this.suppressedSelectionId = itemId;
224
+ window.setTimeout(() => {
225
+ if (this.suppressedSelectionId === itemId) {
226
+ this.suppressedSelectionId = undefined;
227
+ }
228
+ }, 0);
229
+ }
230
+ /**
231
+ * Keyboard pick-up and native drag gestures can both dispatch a click after
232
+ * their key/pointer sequence. Returns `true` when this click must be
233
+ * consumed so a move never doubles as an application-level activation.
234
+ */
235
+ consumeClick(itemId) {
236
+ if (this.suppressedSelectionId === itemId) {
237
+ this.suppressedSelectionId = undefined;
238
+ return true;
239
+ }
240
+ return false;
241
+ }
242
+ keydown(event, itemId) {
243
+ const drag = this.drag;
244
+ if (!drag) {
245
+ if (event.key === ' ' || event.key === 'Enter') {
246
+ if (!this.movable)
247
+ return;
248
+ event.preventDefault();
249
+ this.pickup(itemId, 'keyboard');
250
+ }
251
+ return;
252
+ }
253
+ if (drag.itemId !== itemId || drag.mode !== 'keyboard')
254
+ return;
255
+ if (event.key === 'Escape') {
256
+ event.preventDefault();
257
+ this.cancel();
258
+ }
259
+ else if (event.key === ' ' || event.key === 'Enter') {
260
+ event.preventDefault();
261
+ void this.drop();
262
+ }
263
+ else if (event.key === 'ArrowLeft') {
264
+ event.preventDefault();
265
+ this.moveKeyboardTarget('horizontal', -1);
266
+ }
267
+ else if (event.key === 'ArrowRight') {
268
+ event.preventDefault();
269
+ this.moveKeyboardTarget('horizontal', 1);
270
+ }
271
+ else if (event.key === 'ArrowUp') {
272
+ event.preventDefault();
273
+ this.moveKeyboardTarget('vertical', -1);
274
+ }
275
+ else if (event.key === 'ArrowDown') {
276
+ event.preventDefault();
277
+ this.moveKeyboardTarget('vertical', 1);
278
+ }
279
+ }
280
+ /**
281
+ * Focus left the item while a keyboard move was in flight: abandon it so
282
+ * the item is not left "picked up" with no way to steer it.
283
+ */
284
+ blur(event, itemId) {
285
+ const drag = this.drag;
286
+ if (drag?.mode !== 'keyboard' || drag.itemId !== itemId)
287
+ return;
288
+ const next = event.relatedTarget;
289
+ const current = event.currentTarget;
290
+ if (next instanceof Node &&
291
+ current instanceof Node &&
292
+ current.contains(next)) {
293
+ return;
294
+ }
295
+ this.cancel(true, false);
296
+ }
297
+ dragStart(event, itemId) {
298
+ if (!this.movable) {
299
+ event.preventDefault();
300
+ return;
301
+ }
302
+ this.pickup(itemId, 'pointer');
303
+ event.dataTransfer?.setData('text/plain', itemId);
304
+ if (event.dataTransfer)
305
+ event.dataTransfer.effectAllowed = 'move';
306
+ }
307
+ dragEnd() {
308
+ if (this.drag?.mode === 'pointer')
309
+ this.cancel();
310
+ }
311
+ pointerDown(event, itemId) {
312
+ if (!this.movable || event.button !== 0)
313
+ return;
314
+ this.pointerSession = {
315
+ itemId,
316
+ pointerId: event.pointerId,
317
+ startX: event.clientX,
318
+ startY: event.clientY,
319
+ active: false,
320
+ };
321
+ const target = event.currentTarget;
322
+ target.setPointerCapture?.(event.pointerId);
323
+ }
324
+ elementAtPointer(event) {
325
+ return (document.elementFromPoint?.(event.clientX, event.clientY) ??
326
+ event.target);
327
+ }
328
+ setPointerTargetFromElement(element, clientY) {
329
+ // Container ids belong to the host's domain and may repeat in another
330
+ // instance. Pointer hit-testing must never escape this one's root.
331
+ const root = this.options.root();
332
+ if (!element || !root?.contains(element))
333
+ return undefined;
334
+ const { selectors } = this.options;
335
+ const lane = element.closest(selectors.container);
336
+ const containerId = lane?.dataset[selectors.containerKey];
337
+ if (!containerId)
338
+ return undefined;
339
+ const container = this.findContainer(containerId);
340
+ if (!container || container.disabled)
341
+ return container;
342
+ const itemElement = element.closest(selectors.item);
343
+ if (!itemElement) {
344
+ this.pointerTarget(containerId, this.itemsIn(containerId, this.drag?.itemId).length);
345
+ return container;
346
+ }
347
+ const rawIndex = this.itemsIn(containerId).indexOf(itemElement.dataset[selectors.itemKey] ?? '');
348
+ if (rawIndex < 0)
349
+ return container;
350
+ this.pointerTarget(containerId, this.insertionIndex(containerId, rawIndex, itemElement, clientY));
351
+ return container;
352
+ }
353
+ /** Index to insert at when hovering item `rawIndex`, after removing the dragged item. */
354
+ insertionIndex(containerId, rawIndex, itemElement, clientY) {
355
+ const rect = itemElement.getBoundingClientRect();
356
+ const before = clientY < rect.top + rect.height / 2;
357
+ let targetIndex = rawIndex + (before ? 0 : 1);
358
+ const drag = this.drag;
359
+ if (drag?.source.containerId === containerId &&
360
+ targetIndex > drag.source.index) {
361
+ targetIndex -= 1;
362
+ }
363
+ return targetIndex;
364
+ }
365
+ finishPointerSession(event, cancelled = false) {
366
+ const session = this.pointerSession;
367
+ this.pointerSession = undefined;
368
+ const target = event.currentTarget;
369
+ if (target.hasPointerCapture?.(event.pointerId)) {
370
+ target.releasePointerCapture(event.pointerId);
371
+ }
372
+ if (!session?.active || this.drag?.itemId !== session.itemId)
373
+ return;
374
+ if (cancelled) {
375
+ this.cancel();
376
+ return;
377
+ }
378
+ const container = this.setPointerTargetFromElement(this.elementAtPointer(event), event.clientY);
379
+ if (container?.disabled) {
380
+ this.say({ type: 'unavailable', container: container.label });
381
+ this.cancel(false);
382
+ return;
383
+ }
384
+ if (!container) {
385
+ this.cancel();
386
+ return;
387
+ }
388
+ void this.drop();
389
+ }
390
+ pointerUp(event) {
391
+ this.finishPointerSession(event);
392
+ }
393
+ pointerCancel(event) {
394
+ this.finishPointerSession(event, true);
395
+ }
396
+ pointerMove(event) {
397
+ const session = this.pointerSession;
398
+ if (!session || session.pointerId !== event.pointerId)
399
+ return;
400
+ if (!session.active) {
401
+ const distance = Math.hypot(event.clientX - session.startX, event.clientY - session.startY);
402
+ if (distance < 6)
403
+ return;
404
+ this.pickup(session.itemId, 'pointer');
405
+ this.pointerSession = { ...session, active: true };
406
+ }
407
+ event.preventDefault();
408
+ this.setPointerTargetFromElement(this.elementAtPointer(event), event.clientY);
409
+ }
410
+ pointerTarget(containerId, index) {
411
+ const drag = this.drag;
412
+ if (drag?.mode !== 'pointer')
413
+ return;
414
+ if (!this.options.allowSameContainerReorder() &&
415
+ containerId === drag.source.containerId) {
416
+ this.drag = { ...drag, target: { ...drag.source } };
417
+ return;
418
+ }
419
+ const max = this.itemsIn(containerId, drag.itemId).length;
420
+ this.drag = { ...drag, target: { containerId, index: clamp(index, max) } };
421
+ }
422
+ /** Native drop on an item (`rawIndex` is its index in its container). */
423
+ dropOnItem(event, containerId, rawIndex) {
424
+ event.preventDefault();
425
+ const drag = this.drag;
426
+ if (drag?.mode !== 'pointer')
427
+ return;
428
+ const container = this.findContainer(containerId);
429
+ if (container?.disabled) {
430
+ this.say({ type: 'unavailable', container: container.label });
431
+ this.cancel(false);
432
+ return;
433
+ }
434
+ this.pointerTarget(containerId, this.insertionIndex(containerId, rawIndex, event.currentTarget, event.clientY));
435
+ void this.drop();
436
+ }
437
+ /** Native drop on a container's empty space: append. */
438
+ dropOnContainer(event, containerId) {
439
+ event.preventDefault();
440
+ const container = this.findContainer(containerId);
441
+ if (container?.disabled) {
442
+ this.say({ type: 'unavailable', container: container.label });
443
+ this.cancel(false);
444
+ return;
445
+ }
446
+ this.pointerTarget(containerId, this.itemsIn(containerId, this.drag?.itemId).length);
447
+ void this.drop();
448
+ }
449
+ }
450
+ /** Create a controller; call during component initialization. */
451
+ export function createSortable(options) {
452
+ return new SortableController(options);
453
+ }
@@ -0,0 +1,5 @@
1
+ export type { SortableAnnouncement, SortableContainerInfo, SortableControllerOptions, SortableMove, SortableSelectors, } from './controller.svelte.js';
2
+ export { createSortable, SortableController } from './controller.svelte.js';
3
+ export { default as Sortable } from './Sortable.svelte';
4
+ export type { SortableContainer, SortableContainerHeaderSnippetProps, SortableContainerMove, SortableItem, SortableItemMove, SortableItemSnippetProps, SortablePosition, SortableProps, } from './types.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/sortable/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,oBAAoB,EACpB,qBAAqB,EACrB,yBAAyB,EACzB,YAAY,EACZ,iBAAiB,GAClB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,YAAY,EACV,iBAAiB,EACjB,mCAAmC,EACnC,qBAAqB,EACrB,YAAY,EACZ,gBAAgB,EAChB,wBAAwB,EACxB,gBAAgB,EAChB,aAAa,GACd,MAAM,YAAY,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { createSortable, SortableController } from './controller.svelte.js';
2
+ export { default as Sortable } from './Sortable.svelte';
@@ -0,0 +1,80 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { SortablePosition } from './controller.svelte.js';
3
+ export type { SortablePosition } from './controller.svelte.js';
4
+ /** The minimum identity contract for a sortable item. */
5
+ export interface SortableItem {
6
+ id: string;
7
+ /** The container the item currently sits in. */
8
+ containerId: string;
9
+ /** Accessible text used in announcements and the move handle's name. */
10
+ label: string;
11
+ }
12
+ /** A list items are sorted within and moved between. */
13
+ export interface SortableContainer {
14
+ id: string;
15
+ label: string;
16
+ /** Items cannot be dropped into a disabled container. */
17
+ disabled?: boolean;
18
+ /**
19
+ * Keeps the container where it is when `reorderContainers` is on: fixed
20
+ * containers render first, in the order given, and have no move handle.
21
+ */
22
+ fixed?: boolean;
23
+ }
24
+ /** A drop of an item. `target.index` counts after removing it from `source`. */
25
+ export interface SortableItemMove<Item extends SortableItem = SortableItem, Container extends SortableContainer = SortableContainer> {
26
+ item: Item;
27
+ source: SortablePosition;
28
+ target: SortablePosition;
29
+ sourceContainer: Container;
30
+ targetContainer: Container;
31
+ }
32
+ /**
33
+ * A drop of a container. Indices count the movable (non-`fixed`) containers;
34
+ * `target` counts after removing the container from `source`.
35
+ */
36
+ export interface SortableContainerMove<Container extends SortableContainer = SortableContainer> {
37
+ container: Container;
38
+ source: number;
39
+ target: number;
40
+ }
41
+ export interface SortableItemSnippetProps<Item extends SortableItem, Container extends SortableContainer> {
42
+ item: Item;
43
+ container: Container;
44
+ /** Index within the container. */
45
+ index: number;
46
+ isDragging: boolean;
47
+ }
48
+ export interface SortableContainerHeaderSnippetProps<Container extends SortableContainer> {
49
+ container: Container;
50
+ count: number;
51
+ isDragging: boolean;
52
+ }
53
+ /** Public props for the generic Svelte 5 Sortable component. */
54
+ export interface SortableProps<Item extends SortableItem, Container extends SortableContainer> {
55
+ /** Containers in display order. The owner is authoritative. */
56
+ containers: readonly Container[];
57
+ /** Items; order within a container is their order here. */
58
+ items: readonly Item[];
59
+ /** Accessible name for the whole list. */
60
+ label?: string;
61
+ /** Let the user reorder the (non-`fixed`) containers too. */
62
+ reorderContainers?: boolean;
63
+ /**
64
+ * Whether items may be reordered within their container (default true);
65
+ * `false` only allows moving them between containers.
66
+ */
67
+ allowSameContainerReorder?: boolean;
68
+ /** Content beside each item's move handle. */
69
+ item: Snippet<[SortableItemSnippetProps<Item, Container>]>;
70
+ /** Content in a container's header, beside its move handle. */
71
+ containerHeader?: Snippet<[SortableContainerHeaderSnippetProps<Container>]>;
72
+ /**
73
+ * Receives a typed item move. May persist asynchronously; a rejection
74
+ * restores the presentation and is announced. Omit to make items read-only.
75
+ */
76
+ onmove?: (move: SortableItemMove<Item, Container>) => void | Promise<void>;
77
+ /** Receives a container move (requires `reorderContainers`). */
78
+ oncontainermove?: (move: SortableContainerMove<Container>) => void | Promise<void>;
79
+ }
80
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/sortable/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,yDAAyD;AACzD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAC;IACpB,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wDAAwD;AACxD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,gFAAgF;AAChF,MAAM,WAAW,gBAAgB,CAC/B,IAAI,SAAS,YAAY,GAAG,YAAY,EACxC,SAAS,SAAS,iBAAiB,GAAG,iBAAiB;IAEvD,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,gBAAgB,CAAC;IACzB,eAAe,EAAE,SAAS,CAAC;IAC3B,eAAe,EAAE,SAAS,CAAC;CAC5B;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB,CACpC,SAAS,SAAS,iBAAiB,GAAG,iBAAiB;IAEvD,SAAS,EAAE,SAAS,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,wBAAwB,CACvC,IAAI,SAAS,YAAY,EACzB,SAAS,SAAS,iBAAiB;IAEnC,IAAI,EAAE,IAAI,CAAC;IACX,SAAS,EAAE,SAAS,CAAC;IACrB,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,mCAAmC,CAClD,SAAS,SAAS,iBAAiB;IAEnC,SAAS,EAAE,SAAS,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,gEAAgE;AAChE,MAAM,WAAW,aAAa,CAC5B,IAAI,SAAS,YAAY,EACzB,SAAS,SAAS,iBAAiB;IAEnC,+DAA+D;IAC/D,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IACjC,2DAA2D;IAC3D,KAAK,EAAE,SAAS,IAAI,EAAE,CAAC;IACvB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;OAGG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,8CAA8C;IAC9C,IAAI,EAAE,OAAO,CAAC,CAAC,wBAAwB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IAC3D,+DAA+D;IAC/D,eAAe,CAAC,EAAE,OAAO,CAAC,CAAC,mCAAmC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAC5E;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,CAAC,IAAI,EAAE,SAAS,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3E,gEAAgE;IAChE,eAAe,CAAC,EAAE,CAChB,IAAI,EAAE,qBAAqB,CAAC,SAAS,CAAC,KACnC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B"}
@@ -0,0 +1 @@
1
+ export {};
@@ -349,6 +349,53 @@ function buildLayoutStyle(shell: ModuleShellState): string {
349
349
  });
350
350
  });
351
351
 
352
+ // Dock opened from code (`useShellDock`, header dock toggles): focus moves
353
+ // into the dock once it is shown.
354
+ let lastDockFocusRequest = 0;
355
+ $effect(() => {
356
+ const request = shell.dockFocusRequest;
357
+ untrack(() => {
358
+ if (request === lastDockFocusRequest) return;
359
+ lastDockFocusRequest = request;
360
+ void tick().then(focusDock);
361
+ });
362
+ });
363
+
364
+ function focusDock(): void {
365
+ const aside = sideElements.right;
366
+ const panel = aside?.querySelector<HTMLElement>(
367
+ '.smrt-admin-shell__panel--right',
368
+ );
369
+ if (!panel || panel.hidden || !edgeExpanded('right')) return;
370
+ if (panel.contains(document.activeElement)) return;
371
+ const target =
372
+ panel.querySelector<HTMLElement>(
373
+ 'a[href], button:not([disabled]), input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), [contenteditable="true"], [tabindex]:not([tabindex="-1"])',
374
+ ) ?? panel;
375
+ target.focus({ preventScroll: true });
376
+ }
377
+
378
+ // When the dock closes (Escape, a toggle, or code), focus goes back to
379
+ // where it was opened from if it was left inside the dock or lost.
380
+ let dockWasOpen = false;
381
+ $effect(() => {
382
+ const open = edgeExpanded('right');
383
+ untrack(() => {
384
+ const wasOpen = dockWasOpen;
385
+ dockWasOpen = open;
386
+ if (!wasOpen || open) return;
387
+ const target = shell.dockReturnFocus;
388
+ shell.dockReturnFocus = null;
389
+ if (!target?.isConnected || typeof document === 'undefined') return;
390
+ const active = document.activeElement;
391
+ const lost =
392
+ !active ||
393
+ active === document.body ||
394
+ Boolean(sideElements.right?.contains(active));
395
+ if (lost) target.focus({ preventScroll: true });
396
+ });
397
+ });
398
+
352
399
  function onEdgeAnimationEnd(event: AnimationEvent, edge: SideEdge): void {
353
400
  if (closingEdge === edge && event.target === event.currentTarget) {
354
401
  endClosing();
@@ -777,7 +824,7 @@ function buildLayoutStyle(shell: ModuleShellState): string {
777
824
  {@render shellBrand()}
778
825
  {@render edgeToggle('top')}
779
826
  {/if}
780
- {#if shell.config.panels.left.initial !== 'hidden' && (!isPhone || shell.phonePresentation('left') !== 'hidden')}
827
+ {#if shell.config.panels.left.initial !== 'hidden' && shell.layoutPanels.left?.visible !== false && (!isPhone || shell.phonePresentation('left') !== 'hidden')}
781
828
  <div class="smrt-admin-shell__tenant-opener" class:restore-hidden={panelState('left') === 'hidden'}>
782
829
  <Button variant="ghost" size="sm" aria-label={t(M['ui.admin_shell.menu'])} aria-expanded={edgeExpanded('left')} aria-controls="smrt-admin-shell-left-panel" onclick={() => shell.setPanelState('left', edgeExpanded('left') ? 'collapsed' : 'expanded')}>{t(M['ui.admin_shell.menu'])}</Button>
783
830
  </div>
@@ -988,6 +1035,7 @@ function buildLayoutStyle(shell: ModuleShellState): string {
988
1035
  {#if panelMounted('right')}
989
1036
  <div
990
1037
  class="smrt-admin-shell__panel smrt-admin-shell__panel--right"
1038
+ tabindex="-1"
991
1039
  hidden={!shownOpen('right')}
992
1040
  >
993
1041
  {#key shell.activeFocusToolId}
@@ -1 +1 @@
1
- {"version":3,"file":"AdminShell.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/workspace/admin-shell/AdminShell.svelte.ts"],"names":[],"mappings":"AA+CA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAQtC,OAAO,EAAE,KAAK,aAAa,EAAiB,MAAM,mBAAmB,CAAC;AAStE,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EACR,sBAAsB,EACtB,eAAe,EAEf,cAAc,EACd,aAAa,EACd,MAAM,YAAY,CAAC;AAGpB,UAAU,KAAM,SAAQ,eAAe;IACrC,sEAAsE;IACtE,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,mCAAmC;IACnC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,qEAAqE;IACrE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yCAAyC;IACzC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,2CAA2C;IAC3C,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,wCAAwC;IACxC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,mEAAmE;IACnE,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,IAAI,EAAE,cAAc,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC,CAAC;IACxD;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,oCAAoC;IACpC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uCAAuC;IACvC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,wCAAwC;IACxC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,0CAA0C;IAC1C,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,2CAA2C;IAC3C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,kDAAkD;IAClD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,QAAQ,EAAE,aAAa,CAAA;KAAE,CAAC,CAAC,CAAC;IAChD;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC,CAAC;IAC7C;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,QAAQ,EAAE,aAAa,CAAC;QAAC,SAAS,EAAE,aAAa,CAAA;KAAE,CAAC,CAAC,CAAC;IAC5E,uEAAuE;IACvE,KAAK,CAAC,EAAE,sBAAsB,CAAC;IAC/B,qEAAqE;IACrE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;CACnB;AA+zBH,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"AdminShell.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/workspace/admin-shell/AdminShell.svelte.ts"],"names":[],"mappings":"AA+CA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAQtC,OAAO,EAAE,KAAK,aAAa,EAAiB,MAAM,mBAAmB,CAAC;AAStE,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EACR,sBAAsB,EACtB,eAAe,EAEf,cAAc,EACd,aAAa,EACd,MAAM,YAAY,CAAC;AAGpB,UAAU,KAAM,SAAQ,eAAe;IACrC,sEAAsE;IACtE,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,mCAAmC;IACnC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,qEAAqE;IACrE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yCAAyC;IACzC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,2CAA2C;IAC3C,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,wCAAwC;IACxC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,mEAAmE;IACnE,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,IAAI,EAAE,cAAc,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC,CAAC;IACxD;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,oCAAoC;IACpC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uCAAuC;IACvC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,wCAAwC;IACxC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,0CAA0C;IAC1C,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,2CAA2C;IAC3C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,kDAAkD;IAClD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,QAAQ,EAAE,aAAa,CAAA;KAAE,CAAC,CAAC,CAAC;IAChD;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC,CAAC;IAC7C;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,QAAQ,EAAE,aAAa,CAAC;QAAC,SAAS,EAAE,aAAa,CAAA;KAAE,CAAC,CAAC,CAAC;IAC5E,uEAAuE;IACvE,KAAK,CAAC,EAAE,sBAAsB,CAAC;IAC/B,qEAAqE;IACrE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;CACnB;AA82BH,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}