foldkit 0.42.0 → 0.43.1
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 +1 -0
- package/dist/devtools/overlay.d.ts.map +1 -1
- package/dist/devtools/overlay.js +7 -4
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +12 -2
- package/dist/runtime/subscription.d.ts +6 -8
- package/dist/runtime/subscription.d.ts.map +1 -1
- package/dist/runtime/subscription.js +5 -1
- package/dist/ui/anchor.d.ts +2 -1
- package/dist/ui/anchor.d.ts.map +1 -1
- package/dist/ui/anchor.js +4 -1
- package/dist/ui/dragAndDrop/index.d.ts +285 -0
- package/dist/ui/dragAndDrop/index.d.ts.map +1 -0
- package/dist/ui/dragAndDrop/index.js +511 -0
- package/dist/ui/dragAndDrop/public.d.ts +3 -0
- package/dist/ui/dragAndDrop/public.d.ts.map +1 -0
- package/dist/ui/dragAndDrop/public.js +1 -0
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +1 -0
- package/dist/ui/listbox/shared.d.ts.map +1 -1
- package/dist/ui/listbox/shared.js +21 -8
- package/dist/ui/menu/index.d.ts.map +1 -1
- package/dist/ui/menu/index.js +21 -8
- package/dist/ui/popover/index.d.ts.map +1 -1
- package/dist/ui/popover/index.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
import { Array, Effect, Equal, Equivalence, Function, Match as M, Option, Schema as S, Stream, pipe, } from 'effect';
|
|
2
|
+
import * as Command from '../../command';
|
|
3
|
+
import { html } from '../../html';
|
|
4
|
+
import { m } from '../../message';
|
|
5
|
+
import { makeSubscriptions } from '../../runtime/subscription';
|
|
6
|
+
import { ts } from '../../schema';
|
|
7
|
+
import { evo } from '../../struct';
|
|
8
|
+
import * as Task from '../../task';
|
|
9
|
+
// MODEL
|
|
10
|
+
const Orientation = S.Literal('Horizontal', 'Vertical');
|
|
11
|
+
const ScreenPoint = S.Struct({
|
|
12
|
+
screenX: S.Number,
|
|
13
|
+
screenY: S.Number,
|
|
14
|
+
});
|
|
15
|
+
const ClientPoint = S.Struct({
|
|
16
|
+
clientX: S.Number,
|
|
17
|
+
clientY: S.Number,
|
|
18
|
+
});
|
|
19
|
+
const DropTarget = S.Struct({
|
|
20
|
+
containerId: S.String,
|
|
21
|
+
index: S.Number,
|
|
22
|
+
});
|
|
23
|
+
const Idle = ts('Idle');
|
|
24
|
+
const Pending = ts('Pending', {
|
|
25
|
+
itemId: S.String,
|
|
26
|
+
containerId: S.String,
|
|
27
|
+
index: S.Number,
|
|
28
|
+
origin: ScreenPoint,
|
|
29
|
+
});
|
|
30
|
+
const Dragging = ts('Dragging', {
|
|
31
|
+
itemId: S.String,
|
|
32
|
+
sourceContainerId: S.String,
|
|
33
|
+
sourceIndex: S.Number,
|
|
34
|
+
origin: ScreenPoint,
|
|
35
|
+
current: ClientPoint,
|
|
36
|
+
maybeDropTarget: S.OptionFromSelf(DropTarget),
|
|
37
|
+
});
|
|
38
|
+
const KeyboardDragging = ts('KeyboardDragging', {
|
|
39
|
+
itemId: S.String,
|
|
40
|
+
sourceContainerId: S.String,
|
|
41
|
+
sourceIndex: S.Number,
|
|
42
|
+
targetContainerId: S.String,
|
|
43
|
+
targetIndex: S.Number,
|
|
44
|
+
});
|
|
45
|
+
const DragState = S.Union(Idle, Pending, Dragging, KeyboardDragging);
|
|
46
|
+
/** Schema for the drag-and-drop component's state, tracking its unique ID, orientation, and current drag phase. */
|
|
47
|
+
export const Model = S.Struct({
|
|
48
|
+
id: S.String,
|
|
49
|
+
orientation: Orientation,
|
|
50
|
+
activationThreshold: S.Number,
|
|
51
|
+
dragState: DragState,
|
|
52
|
+
});
|
|
53
|
+
// MESSAGE
|
|
54
|
+
/** The user pressed a pointer on a draggable item. */
|
|
55
|
+
export const PressedDraggable = m('PressedDraggable', {
|
|
56
|
+
itemId: S.String,
|
|
57
|
+
containerId: S.String,
|
|
58
|
+
index: S.Number,
|
|
59
|
+
screenX: S.Number,
|
|
60
|
+
screenY: S.Number,
|
|
61
|
+
});
|
|
62
|
+
/** The pointer moved during a drag, with collision detection results. */
|
|
63
|
+
export const MovedPointer = m('MovedPointer', {
|
|
64
|
+
screenX: S.Number,
|
|
65
|
+
screenY: S.Number,
|
|
66
|
+
clientX: S.Number,
|
|
67
|
+
clientY: S.Number,
|
|
68
|
+
maybeDropTarget: S.OptionFromSelf(DropTarget),
|
|
69
|
+
});
|
|
70
|
+
/** The pointer was released. */
|
|
71
|
+
export const ReleasedPointer = m('ReleasedPointer');
|
|
72
|
+
/** Escape was pressed during a drag. */
|
|
73
|
+
export const CancelledDrag = m('CancelledDrag');
|
|
74
|
+
/** The user activated keyboard drag with Space or Enter on a focused draggable. */
|
|
75
|
+
export const ActivatedKeyboardDrag = m('ActivatedKeyboardDrag', {
|
|
76
|
+
itemId: S.String,
|
|
77
|
+
containerId: S.String,
|
|
78
|
+
index: S.Number,
|
|
79
|
+
});
|
|
80
|
+
/** The ResolveKeyboardMove Command resolved the next keyboard drag position. */
|
|
81
|
+
export const ResolvedKeyboardMove = m('ResolvedKeyboardMove', {
|
|
82
|
+
targetContainerId: S.String,
|
|
83
|
+
targetIndex: S.Number,
|
|
84
|
+
});
|
|
85
|
+
/** The user confirmed a keyboard drop with Space or Enter. */
|
|
86
|
+
export const ConfirmedKeyboardDrop = m('ConfirmedKeyboardDrop');
|
|
87
|
+
/** The user pressed an arrow key during keyboard drag. */
|
|
88
|
+
export const PressedArrowKey = m('PressedArrowKey', {
|
|
89
|
+
direction: S.Literal('Up', 'Down', 'Left', 'Right', 'NextContainer', 'PreviousContainer'),
|
|
90
|
+
});
|
|
91
|
+
/** An animation frame fired during auto-scroll. */
|
|
92
|
+
export const CompletedAutoScroll = m('CompletedAutoScroll');
|
|
93
|
+
/** The FocusItem Command completed. */
|
|
94
|
+
export const CompletedFocusItem = m('CompletedFocusItem');
|
|
95
|
+
/** Union of all messages the drag-and-drop component can produce. */
|
|
96
|
+
export const Message = S.Union(PressedDraggable, MovedPointer, ReleasedPointer, CancelledDrag, ActivatedKeyboardDrag, ResolvedKeyboardMove, ConfirmedKeyboardDrop, PressedArrowKey, CompletedAutoScroll, CompletedFocusItem);
|
|
97
|
+
// OUT MESSAGE
|
|
98
|
+
/** Emitted when a drag completes with a valid drop target. The parent uses this to commit the reorder. */
|
|
99
|
+
export const Reordered = ts('Reordered', {
|
|
100
|
+
itemId: S.String,
|
|
101
|
+
fromContainerId: S.String,
|
|
102
|
+
fromIndex: S.Number,
|
|
103
|
+
toContainerId: S.String,
|
|
104
|
+
toIndex: S.Number,
|
|
105
|
+
});
|
|
106
|
+
/** Emitted when a drag is cancelled via Escape or pointer release without a drop target. */
|
|
107
|
+
export const Cancelled = ts('Cancelled');
|
|
108
|
+
/** Union of all out-messages the drag-and-drop component can emit to its parent. */
|
|
109
|
+
export const OutMessage = S.Union(Reordered, Cancelled);
|
|
110
|
+
// INIT
|
|
111
|
+
/** Configuration for creating a drag-and-drop model with `init`. */
|
|
112
|
+
const DEFAULT_ACTIVATION_THRESHOLD_PIXELS = 5;
|
|
113
|
+
/** Creates an initial drag-and-drop model. Starts in the Idle state with Vertical orientation and 5px activation threshold by default. */
|
|
114
|
+
export const init = (config) => ({
|
|
115
|
+
id: config.id,
|
|
116
|
+
orientation: config.orientation ?? 'Vertical',
|
|
117
|
+
activationThreshold: config.activationThreshold ?? DEFAULT_ACTIVATION_THRESHOLD_PIXELS,
|
|
118
|
+
dragState: Idle(),
|
|
119
|
+
});
|
|
120
|
+
/** Resolves the next keyboard drag position by querying the DOM for adjacent sortable items and containers. */
|
|
121
|
+
export const ResolveKeyboardMove = Command.define('ResolveKeyboardMove', ResolvedKeyboardMove);
|
|
122
|
+
/** Focuses a draggable item by ID after keyboard drop or cancel. */
|
|
123
|
+
export const FocusItem = Command.define('FocusItem', CompletedFocusItem);
|
|
124
|
+
const focusItem = (itemId) => FocusItem(Task.focus(`[data-draggable-id="${itemId}"]`).pipe(Effect.ignore, Effect.as(CompletedFocusItem())));
|
|
125
|
+
const resolveWithinContainer = (config) => {
|
|
126
|
+
const container = document.querySelector(`[data-droppable-id="${config.containerId}"]`);
|
|
127
|
+
if (!container) {
|
|
128
|
+
return ResolvedKeyboardMove({
|
|
129
|
+
targetContainerId: config.containerId,
|
|
130
|
+
targetIndex: config.currentIndex,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
const itemCount = pipe(container.querySelectorAll('[data-sortable-id]'), Array.fromIterable, Array.filter(({ dataset }) => dataset['sortableId'] !== config.itemId), Array.length);
|
|
134
|
+
const nextIndex = config.isForward
|
|
135
|
+
? Math.min(config.currentIndex + 1, itemCount)
|
|
136
|
+
: Math.max(config.currentIndex - 1, 0);
|
|
137
|
+
return ResolvedKeyboardMove({
|
|
138
|
+
targetContainerId: config.containerId,
|
|
139
|
+
targetIndex: nextIndex,
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
const resolveBetweenContainers = (config) => {
|
|
143
|
+
const allContainers = Array.fromIterable(document.querySelectorAll('[data-droppable-id]'));
|
|
144
|
+
const currentContainerIndex = pipe(allContainers, Array.findFirstIndex(({ dataset }) => dataset['droppableId'] === config.currentContainerId), Option.getOrElse(() => 0));
|
|
145
|
+
const nextContainerIndex = config.isForward
|
|
146
|
+
? Math.min(currentContainerIndex + 1, allContainers.length - 1)
|
|
147
|
+
: Math.max(currentContainerIndex - 1, 0);
|
|
148
|
+
const nextContainerId = allContainers[nextContainerIndex]?.dataset['droppableId'] ??
|
|
149
|
+
config.currentContainerId;
|
|
150
|
+
return ResolvedKeyboardMove({
|
|
151
|
+
targetContainerId: nextContainerId,
|
|
152
|
+
targetIndex: 0,
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
const resolveKeyboardMoveTarget = (config) => Effect.sync(() => M.value(config.direction).pipe(M.withReturnType(), M.whenOr('Down', 'Right', () => resolveWithinContainer({
|
|
156
|
+
itemId: config.itemId,
|
|
157
|
+
containerId: config.currentContainerId,
|
|
158
|
+
currentIndex: config.currentIndex,
|
|
159
|
+
isForward: true,
|
|
160
|
+
})), M.whenOr('Up', 'Left', () => resolveWithinContainer({
|
|
161
|
+
itemId: config.itemId,
|
|
162
|
+
containerId: config.currentContainerId,
|
|
163
|
+
currentIndex: config.currentIndex,
|
|
164
|
+
isForward: false,
|
|
165
|
+
})), M.when('NextContainer', () => resolveBetweenContainers({
|
|
166
|
+
currentContainerId: config.currentContainerId,
|
|
167
|
+
isForward: true,
|
|
168
|
+
})), M.when('PreviousContainer', () => resolveBetweenContainers({
|
|
169
|
+
currentContainerId: config.currentContainerId,
|
|
170
|
+
isForward: false,
|
|
171
|
+
})), M.exhaustive));
|
|
172
|
+
const withUpdateReturn = M.withReturnType();
|
|
173
|
+
/** Processes a drag-and-drop message and returns the next model, commands, and an optional out-message for the parent. */
|
|
174
|
+
export const update = (model, message) => M.value(message).pipe(withUpdateReturn, M.tagsExhaustive({
|
|
175
|
+
PressedDraggable: ({ itemId, containerId, index, screenX, screenY }) => [
|
|
176
|
+
evo(model, {
|
|
177
|
+
dragState: () => Pending({
|
|
178
|
+
itemId,
|
|
179
|
+
containerId,
|
|
180
|
+
index,
|
|
181
|
+
origin: { screenX, screenY },
|
|
182
|
+
}),
|
|
183
|
+
}),
|
|
184
|
+
[],
|
|
185
|
+
Option.none(),
|
|
186
|
+
],
|
|
187
|
+
MovedPointer: ({ screenX, screenY, clientX, clientY, maybeDropTarget }) => M.value(model.dragState).pipe(withUpdateReturn, M.tag('Pending', pending => {
|
|
188
|
+
const deltaX = screenX - pending.origin.screenX;
|
|
189
|
+
const deltaY = screenY - pending.origin.screenY;
|
|
190
|
+
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
191
|
+
if (distance < model.activationThreshold) {
|
|
192
|
+
return [model, [], Option.none()];
|
|
193
|
+
}
|
|
194
|
+
return [
|
|
195
|
+
evo(model, {
|
|
196
|
+
dragState: () => Dragging({
|
|
197
|
+
itemId: pending.itemId,
|
|
198
|
+
sourceContainerId: pending.containerId,
|
|
199
|
+
sourceIndex: pending.index,
|
|
200
|
+
origin: pending.origin,
|
|
201
|
+
current: { clientX, clientY },
|
|
202
|
+
maybeDropTarget,
|
|
203
|
+
}),
|
|
204
|
+
}),
|
|
205
|
+
[],
|
|
206
|
+
Option.none(),
|
|
207
|
+
];
|
|
208
|
+
}), M.tag('Dragging', dragging => [
|
|
209
|
+
evo(model, {
|
|
210
|
+
dragState: () => Dragging({
|
|
211
|
+
...dragging,
|
|
212
|
+
current: { clientX, clientY },
|
|
213
|
+
maybeDropTarget,
|
|
214
|
+
}),
|
|
215
|
+
}),
|
|
216
|
+
[],
|
|
217
|
+
Option.none(),
|
|
218
|
+
]), M.orElse(() => [model, [], Option.none()])),
|
|
219
|
+
ReleasedPointer: () => M.value(model.dragState).pipe(withUpdateReturn, M.tag('Pending', () => [
|
|
220
|
+
evo(model, { dragState: () => Idle() }),
|
|
221
|
+
[],
|
|
222
|
+
Option.none(),
|
|
223
|
+
]), M.tag('Dragging', dragging => Option.match(dragging.maybeDropTarget, {
|
|
224
|
+
onNone: () => [
|
|
225
|
+
evo(model, { dragState: () => Idle() }),
|
|
226
|
+
[],
|
|
227
|
+
Option.some(Cancelled()),
|
|
228
|
+
],
|
|
229
|
+
onSome: dropTarget => [
|
|
230
|
+
evo(model, { dragState: () => Idle() }),
|
|
231
|
+
[],
|
|
232
|
+
Option.some(Reordered({
|
|
233
|
+
itemId: dragging.itemId,
|
|
234
|
+
fromContainerId: dragging.sourceContainerId,
|
|
235
|
+
fromIndex: dragging.sourceIndex,
|
|
236
|
+
toContainerId: dropTarget.containerId,
|
|
237
|
+
toIndex: dropTarget.index,
|
|
238
|
+
})),
|
|
239
|
+
],
|
|
240
|
+
})), M.orElse(() => [model, [], Option.none()])),
|
|
241
|
+
CancelledDrag: () => {
|
|
242
|
+
const maybeFocusCommand = Option.liftPredicate(model.dragState, dragState => dragState._tag === 'KeyboardDragging').pipe(Option.map(({ itemId }) => focusItem(itemId)));
|
|
243
|
+
const maybeOutMessage = Option.liftPredicate(model.dragState._tag, _tag => _tag === 'Dragging' || _tag === 'KeyboardDragging').pipe(Option.map(() => Cancelled()));
|
|
244
|
+
return [
|
|
245
|
+
evo(model, { dragState: () => Idle() }),
|
|
246
|
+
Option.toArray(maybeFocusCommand),
|
|
247
|
+
maybeOutMessage,
|
|
248
|
+
];
|
|
249
|
+
},
|
|
250
|
+
ActivatedKeyboardDrag: ({ itemId, containerId, index }) => [
|
|
251
|
+
evo(model, {
|
|
252
|
+
dragState: () => KeyboardDragging({
|
|
253
|
+
itemId,
|
|
254
|
+
sourceContainerId: containerId,
|
|
255
|
+
sourceIndex: index,
|
|
256
|
+
targetContainerId: containerId,
|
|
257
|
+
targetIndex: index,
|
|
258
|
+
}),
|
|
259
|
+
}),
|
|
260
|
+
[],
|
|
261
|
+
Option.none(),
|
|
262
|
+
],
|
|
263
|
+
ResolvedKeyboardMove: ({ targetContainerId, targetIndex }) => M.value(model.dragState).pipe(withUpdateReturn, M.tag('KeyboardDragging', keyboardDragging => [
|
|
264
|
+
evo(model, {
|
|
265
|
+
dragState: () => KeyboardDragging({
|
|
266
|
+
...keyboardDragging,
|
|
267
|
+
targetContainerId,
|
|
268
|
+
targetIndex,
|
|
269
|
+
}),
|
|
270
|
+
}),
|
|
271
|
+
[],
|
|
272
|
+
Option.none(),
|
|
273
|
+
]), M.orElse(() => [model, [], Option.none()])),
|
|
274
|
+
ConfirmedKeyboardDrop: () => M.value(model.dragState).pipe(withUpdateReturn, M.tag('KeyboardDragging', keyboardDragging => [
|
|
275
|
+
evo(model, { dragState: () => Idle() }),
|
|
276
|
+
[focusItem(keyboardDragging.itemId)],
|
|
277
|
+
Option.some(Reordered({
|
|
278
|
+
itemId: keyboardDragging.itemId,
|
|
279
|
+
fromContainerId: keyboardDragging.sourceContainerId,
|
|
280
|
+
fromIndex: keyboardDragging.sourceIndex,
|
|
281
|
+
toContainerId: keyboardDragging.targetContainerId,
|
|
282
|
+
toIndex: keyboardDragging.targetIndex,
|
|
283
|
+
})),
|
|
284
|
+
]), M.orElse(() => [model, [], Option.none()])),
|
|
285
|
+
PressedArrowKey: ({ direction }) => M.value(model.dragState).pipe(withUpdateReturn, M.tag('KeyboardDragging', keyboardDragging => [
|
|
286
|
+
model,
|
|
287
|
+
[
|
|
288
|
+
ResolveKeyboardMove(resolveKeyboardMoveTarget({
|
|
289
|
+
itemId: keyboardDragging.itemId,
|
|
290
|
+
currentContainerId: keyboardDragging.targetContainerId,
|
|
291
|
+
currentIndex: keyboardDragging.targetIndex,
|
|
292
|
+
direction,
|
|
293
|
+
})),
|
|
294
|
+
],
|
|
295
|
+
Option.none(),
|
|
296
|
+
]), M.orElse(() => [model, [], Option.none()])),
|
|
297
|
+
CompletedAutoScroll: () => [model, [], Option.none()],
|
|
298
|
+
CompletedFocusItem: () => [model, [], Option.none()],
|
|
299
|
+
}));
|
|
300
|
+
// SUBSCRIPTION
|
|
301
|
+
const DragActivity = S.Literal('Idle', 'Active');
|
|
302
|
+
const PointerDragActivity = S.Literal('Idle', 'Active');
|
|
303
|
+
const KeyboardDragActivity = S.Literal('Idle', 'Active');
|
|
304
|
+
const resolveDropTarget = (clientX, clientY, orientation) => {
|
|
305
|
+
const maybeContainer = pipe(document.elementsFromPoint(clientX, clientY), Array.fromIterable, Array.findFirst(element => element.hasAttribute('data-droppable-id')));
|
|
306
|
+
return Option.flatMap(maybeContainer, container => {
|
|
307
|
+
const containerId = container.getAttribute('data-droppable-id');
|
|
308
|
+
if (!containerId) {
|
|
309
|
+
return Option.none();
|
|
310
|
+
}
|
|
311
|
+
const sortableItems = Array.fromIterable(container.querySelectorAll('[data-sortable-id]'));
|
|
312
|
+
const insertionIndex = pipe(sortableItems, Array.findFirstIndex(item => {
|
|
313
|
+
const rect = item.getBoundingClientRect();
|
|
314
|
+
return M.value(orientation).pipe(M.when('Vertical', () => clientY < rect.top + rect.height / 2), M.when('Horizontal', () => clientX < rect.left + rect.width / 2), M.exhaustive);
|
|
315
|
+
}), Option.getOrElse(() => sortableItems.length));
|
|
316
|
+
return Option.some({ containerId, index: insertionIndex });
|
|
317
|
+
});
|
|
318
|
+
};
|
|
319
|
+
const DEFAULT_AUTO_SCROLL_EDGE_PIXELS = 40;
|
|
320
|
+
const DEFAULT_AUTO_SCROLL_MAX_SPEED = 15;
|
|
321
|
+
const autoScroll = (clientY) => {
|
|
322
|
+
const viewportHeight = window.innerHeight;
|
|
323
|
+
const distanceFromTop = clientY;
|
|
324
|
+
const distanceFromBottom = viewportHeight - clientY;
|
|
325
|
+
if (distanceFromTop < DEFAULT_AUTO_SCROLL_EDGE_PIXELS) {
|
|
326
|
+
const speed = DEFAULT_AUTO_SCROLL_MAX_SPEED *
|
|
327
|
+
(1 - distanceFromTop / DEFAULT_AUTO_SCROLL_EDGE_PIXELS);
|
|
328
|
+
window.scrollBy(0, -speed);
|
|
329
|
+
}
|
|
330
|
+
else if (distanceFromBottom < DEFAULT_AUTO_SCROLL_EDGE_PIXELS) {
|
|
331
|
+
const speed = DEFAULT_AUTO_SCROLL_MAX_SPEED *
|
|
332
|
+
(1 - distanceFromBottom / DEFAULT_AUTO_SCROLL_EDGE_PIXELS);
|
|
333
|
+
window.scrollBy(0, speed);
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
const pointerDragActivityFromModel = (model) => M.value(model.dragState).pipe(M.withReturnType(), M.tag('Pending', 'Dragging', () => 'Active'), M.orElse(() => 'Idle'));
|
|
337
|
+
const dragActivityFromModel = (model) => M.value(model.dragState).pipe(M.withReturnType(), M.tag('Idle', () => 'Idle'), M.orElse(() => 'Active'));
|
|
338
|
+
const keyboardDragActivityFromModel = (model) => M.value(model.dragState).pipe(M.withReturnType(), M.tag('KeyboardDragging', () => 'Active'), M.orElse(() => 'Idle'));
|
|
339
|
+
/** Schema describing the subscription dependencies for document-level drag tracking. */
|
|
340
|
+
export const SubscriptionDeps = S.Struct({
|
|
341
|
+
documentPointer: S.Struct({
|
|
342
|
+
dragActivity: PointerDragActivity,
|
|
343
|
+
orientation: Orientation,
|
|
344
|
+
}),
|
|
345
|
+
documentEscape: S.Struct({ dragActivity: DragActivity }),
|
|
346
|
+
documentKeyboard: S.Struct({ dragActivity: KeyboardDragActivity }),
|
|
347
|
+
autoScroll: S.Struct({
|
|
348
|
+
isDragging: S.Boolean,
|
|
349
|
+
clientY: S.Number,
|
|
350
|
+
}),
|
|
351
|
+
});
|
|
352
|
+
/** Document-level subscriptions for pointer and keyboard events during drag operations. */
|
|
353
|
+
export const subscriptions = makeSubscriptions(SubscriptionDeps)({
|
|
354
|
+
documentPointer: {
|
|
355
|
+
modelToDependencies: model => ({
|
|
356
|
+
dragActivity: pointerDragActivityFromModel(model),
|
|
357
|
+
orientation: model.orientation,
|
|
358
|
+
}),
|
|
359
|
+
dependenciesToStream: ({ dragActivity, orientation }) => {
|
|
360
|
+
const pointerEvents = Stream.merge(Stream.fromEventListener(document, 'pointermove').pipe(Stream.mapEffect(event => Effect.sync(() => MovedPointer({
|
|
361
|
+
screenX: event.screenX,
|
|
362
|
+
screenY: event.screenY,
|
|
363
|
+
clientX: event.clientX,
|
|
364
|
+
clientY: event.clientY,
|
|
365
|
+
maybeDropTarget: resolveDropTarget(event.clientX, event.clientY, orientation),
|
|
366
|
+
})))), Stream.fromEventListener(document, 'pointerup').pipe(Stream.map(() => ReleasedPointer())));
|
|
367
|
+
// NOTE: prevents text selection and locks cursor to grabbing during
|
|
368
|
+
// pointer drag. Uses a <style> element for cursor because inline styles
|
|
369
|
+
// on <html> don't override descendant elements' cursor values.
|
|
370
|
+
const documentDragStyles = Stream.async(_emit => {
|
|
371
|
+
document.documentElement.style.setProperty('user-select', 'none');
|
|
372
|
+
document.documentElement.style.setProperty('-webkit-user-select', 'none');
|
|
373
|
+
const cursorStyle = document.createElement('style');
|
|
374
|
+
cursorStyle.textContent = '* { cursor: grabbing !important; }';
|
|
375
|
+
document.head.appendChild(cursorStyle);
|
|
376
|
+
return Effect.sync(() => {
|
|
377
|
+
document.documentElement.style.removeProperty('user-select');
|
|
378
|
+
document.documentElement.style.removeProperty('-webkit-user-select');
|
|
379
|
+
cursorStyle.remove();
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
return Stream.when(Stream.merge(pointerEvents, documentDragStyles), () => dragActivity === 'Active');
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
documentEscape: {
|
|
386
|
+
modelToDependencies: model => ({
|
|
387
|
+
dragActivity: dragActivityFromModel(model),
|
|
388
|
+
}),
|
|
389
|
+
dependenciesToStream: ({ dragActivity }) => Stream.when(Stream.fromEventListener(document, 'keydown').pipe(Stream.filter(({ key }) => key === 'Escape'), Stream.map(() => CancelledDrag())), () => dragActivity === 'Active'),
|
|
390
|
+
},
|
|
391
|
+
documentKeyboard: {
|
|
392
|
+
modelToDependencies: model => ({
|
|
393
|
+
dragActivity: keyboardDragActivityFromModel(model),
|
|
394
|
+
}),
|
|
395
|
+
dependenciesToStream: ({ dragActivity }) => Stream.when(Stream.fromEventListener(document, 'keydown').pipe(Stream.mapEffect((event) => Effect.sync(() => {
|
|
396
|
+
// NOTE: the draggable's OnKeyDownPreventDefault calls preventDefault on
|
|
397
|
+
// the Space that activates keyboard drag — skip it here so the same
|
|
398
|
+
// keypress doesn't also confirm the drop in the same tick.
|
|
399
|
+
if (event.defaultPrevented) {
|
|
400
|
+
return Option.none();
|
|
401
|
+
}
|
|
402
|
+
if (event.key === 'Tab') {
|
|
403
|
+
event.preventDefault();
|
|
404
|
+
return Option.some(PressedArrowKey({
|
|
405
|
+
direction: event.shiftKey
|
|
406
|
+
? 'PreviousContainer'
|
|
407
|
+
: 'NextContainer',
|
|
408
|
+
}));
|
|
409
|
+
}
|
|
410
|
+
if (event.key === ' ' || event.key === 'Enter') {
|
|
411
|
+
event.preventDefault();
|
|
412
|
+
return Option.some(ConfirmedKeyboardDrop());
|
|
413
|
+
}
|
|
414
|
+
return Option.map(arrowKeyToDirection(event.key), direction => {
|
|
415
|
+
event.preventDefault();
|
|
416
|
+
return PressedArrowKey({ direction });
|
|
417
|
+
});
|
|
418
|
+
})), Stream.filterMap(Function.identity)), () => dragActivity === 'Active'),
|
|
419
|
+
},
|
|
420
|
+
autoScroll: {
|
|
421
|
+
modelToDependencies: model => ({
|
|
422
|
+
isDragging: model.dragState._tag === 'Dragging',
|
|
423
|
+
clientY: model.dragState._tag === 'Dragging'
|
|
424
|
+
? model.dragState.current.clientY
|
|
425
|
+
: 0,
|
|
426
|
+
}),
|
|
427
|
+
equivalence: Equivalence.struct({ isDragging: Equivalence.boolean }),
|
|
428
|
+
dependenciesToStream: ({ isDragging }, readDependencies) => Stream.when(Stream.async(emit => {
|
|
429
|
+
let animationFrameId = 0;
|
|
430
|
+
const step = () => {
|
|
431
|
+
autoScroll(readDependencies().clientY);
|
|
432
|
+
emit.single(CompletedAutoScroll());
|
|
433
|
+
animationFrameId = requestAnimationFrame(step);
|
|
434
|
+
};
|
|
435
|
+
animationFrameId = requestAnimationFrame(step);
|
|
436
|
+
return Effect.sync(() => cancelAnimationFrame(animationFrameId));
|
|
437
|
+
}), () => isDragging),
|
|
438
|
+
},
|
|
439
|
+
});
|
|
440
|
+
// VIEW
|
|
441
|
+
const LEFT_MOUSE_BUTTON = 0;
|
|
442
|
+
const arrowKeyToDirection = (key) => M.value(key).pipe(M.withReturnType(), M.when('ArrowUp', () => 'Up'), M.when('ArrowDown', () => 'Down'), M.when('ArrowLeft', () => 'Left'), M.when('ArrowRight', () => 'Right'), M.option);
|
|
443
|
+
/** Returns attributes the parent attaches to a draggable element. Handles pointer-down, keyboard activation, and ARIA. */
|
|
444
|
+
export const draggable = (config) => {
|
|
445
|
+
const { AriaRoleDescription, DataAttribute, OnKeyDownPreventDefault, OnPointerDown, Role, Style, Tabindex, } = html();
|
|
446
|
+
const isKeyboardDragActivationKey = (key) => key === ' ' || key === 'Enter';
|
|
447
|
+
const handleKeyDown = (key) => {
|
|
448
|
+
if (isKeyboardDragActivationKey(key) &&
|
|
449
|
+
config.model.dragState._tag === 'Idle') {
|
|
450
|
+
return Option.some(config.toParentMessage(ActivatedKeyboardDrag({
|
|
451
|
+
itemId: config.itemId,
|
|
452
|
+
containerId: config.containerId,
|
|
453
|
+
index: config.index,
|
|
454
|
+
})));
|
|
455
|
+
}
|
|
456
|
+
return Option.none();
|
|
457
|
+
};
|
|
458
|
+
return [
|
|
459
|
+
DataAttribute('draggable-id', config.itemId),
|
|
460
|
+
DataAttribute('sortable-id', config.itemId),
|
|
461
|
+
Role('option'),
|
|
462
|
+
AriaRoleDescription('draggable'),
|
|
463
|
+
Tabindex(0),
|
|
464
|
+
OnPointerDown((_pointerType, button, screenX, screenY) => pipe(button, Option.liftPredicate(Equal.equals(LEFT_MOUSE_BUTTON)), Option.map(() => config.toParentMessage(PressedDraggable({
|
|
465
|
+
itemId: config.itemId,
|
|
466
|
+
containerId: config.containerId,
|
|
467
|
+
index: config.index,
|
|
468
|
+
screenX,
|
|
469
|
+
screenY,
|
|
470
|
+
}))))),
|
|
471
|
+
OnKeyDownPreventDefault(handleKeyDown),
|
|
472
|
+
Style({
|
|
473
|
+
'touch-action': 'none',
|
|
474
|
+
'user-select': 'none',
|
|
475
|
+
'-webkit-user-select': 'none',
|
|
476
|
+
}),
|
|
477
|
+
];
|
|
478
|
+
};
|
|
479
|
+
/** Returns attributes the parent attaches to a droppable container element. */
|
|
480
|
+
export const droppable = (containerId, label) => {
|
|
481
|
+
const { AriaLabel, DataAttribute, Role } = html();
|
|
482
|
+
return [
|
|
483
|
+
DataAttribute('droppable-id', containerId),
|
|
484
|
+
Role('listbox'),
|
|
485
|
+
...(label ? [AriaLabel(label)] : []),
|
|
486
|
+
];
|
|
487
|
+
};
|
|
488
|
+
/** Returns attributes the parent attaches to a sortable item element. Typically combined with `draggable`. */
|
|
489
|
+
export const sortable = (itemId) => {
|
|
490
|
+
const { DataAttribute } = html();
|
|
491
|
+
return [DataAttribute('sortable-id', itemId)];
|
|
492
|
+
};
|
|
493
|
+
const ghostTransform = (clientX, clientY) => `translate3d(${String(clientX)}px, ${String(clientY)}px, 0)`;
|
|
494
|
+
/** Returns positioning styles for the ghost element, or None when not dragging with a pointer. */
|
|
495
|
+
export const ghostStyle = (model) => M.value(model.dragState).pipe(M.tag('Dragging', dragging => ({
|
|
496
|
+
position: 'fixed',
|
|
497
|
+
top: '0',
|
|
498
|
+
left: '0',
|
|
499
|
+
transform: ghostTransform(dragging.current.clientX, dragging.current.clientY),
|
|
500
|
+
'pointer-events': 'none',
|
|
501
|
+
'z-index': '9999',
|
|
502
|
+
})), M.option);
|
|
503
|
+
/** Returns true when the component is actively dragging (pointer or keyboard). */
|
|
504
|
+
export const isDragging = ({ dragState: { _tag } }) => _tag === 'Dragging' || _tag === 'KeyboardDragging';
|
|
505
|
+
/** Returns the ID of the item currently being dragged or pending, if any. */
|
|
506
|
+
export const maybeDraggedItemId = (model) => M.value(model.dragState).pipe(M.tag('Pending', pending => pending.itemId), M.tag('Dragging', dragging => dragging.itemId), M.tag('KeyboardDragging', keyboardDragging => keyboardDragging.itemId), M.option);
|
|
507
|
+
/** Returns the current drop target, if any. Populated during pointer drag (from collision detection) and keyboard drag (from resolved position). */
|
|
508
|
+
export const maybeDropTarget = (model) => M.value(model.dragState).pipe(M.tag('Dragging', dragging => dragging.maybeDropTarget), M.tag('KeyboardDragging', keyboardDragging => Option.some({
|
|
509
|
+
containerId: keyboardDragging.targetContainerId,
|
|
510
|
+
index: keyboardDragging.targetIndex,
|
|
511
|
+
})), M.orElse(() => Option.none()));
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { init, update, draggable, droppable, sortable, ghostStyle, isDragging, maybeDraggedItemId, maybeDropTarget, subscriptions, SubscriptionDeps, Model, Message, OutMessage, PressedDraggable, MovedPointer, ReleasedPointer, CancelledDrag, ActivatedKeyboardDrag, ResolvedKeyboardMove, ConfirmedKeyboardDrop, PressedArrowKey, CompletedAutoScroll, CompletedFocusItem, FocusItem, ResolveKeyboardMove, Reordered, Cancelled, } from './index';
|
|
2
|
+
export type { InitConfig, DraggableConfig, DraggableMessage } from './index';
|
|
3
|
+
//# sourceMappingURL=public.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/ui/dragAndDrop/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,MAAM,EACN,SAAS,EACT,SAAS,EACT,QAAQ,EACR,UAAU,EACV,UAAU,EACV,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,KAAK,EACL,OAAO,EACP,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,eAAe,EACf,aAAa,EACb,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,SAAS,EACT,mBAAmB,EACnB,SAAS,EACT,SAAS,GACV,MAAM,SAAS,CAAA;AAEhB,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { init, update, draggable, droppable, sortable, ghostStyle, isDragging, maybeDraggedItemId, maybeDropTarget, subscriptions, SubscriptionDeps, Model, Message, OutMessage, PressedDraggable, MovedPointer, ReleasedPointer, CancelledDrag, ActivatedKeyboardDrag, ResolvedKeyboardMove, ConfirmedKeyboardDrop, PressedArrowKey, CompletedAutoScroll, CompletedFocusItem, FocusItem, ResolveKeyboardMove, Reordered, Cancelled, } from './index';
|
package/dist/ui/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * as Button from './button/public';
|
|
|
2
2
|
export * as Checkbox from './checkbox/public';
|
|
3
3
|
export * as Combobox from './combobox/public';
|
|
4
4
|
export * as Dialog from './dialog/public';
|
|
5
|
+
export * as DragAndDrop from './dragAndDrop/public';
|
|
5
6
|
export * as Disclosure from './disclosure/public';
|
|
6
7
|
export * as Fieldset from './fieldset/public';
|
|
7
8
|
export * as Input from './input/public';
|
package/dist/ui/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAA;AACzC,OAAO,KAAK,QAAQ,MAAM,mBAAmB,CAAA;AAC7C,OAAO,KAAK,QAAQ,MAAM,mBAAmB,CAAA;AAC7C,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAA;AACzC,OAAO,KAAK,UAAU,MAAM,qBAAqB,CAAA;AACjD,OAAO,KAAK,QAAQ,MAAM,mBAAmB,CAAA;AAC7C,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAA;AACvC,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAA;AAC3C,OAAO,KAAK,IAAI,MAAM,eAAe,CAAA;AACrC,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAA;AAC3C,OAAO,KAAK,UAAU,MAAM,qBAAqB,CAAA;AACjD,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAA;AACzC,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAA;AACzC,OAAO,KAAK,QAAQ,MAAM,mBAAmB,CAAA;AAC7C,OAAO,KAAK,IAAI,MAAM,eAAe,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAA;AACzC,OAAO,KAAK,QAAQ,MAAM,mBAAmB,CAAA;AAC7C,OAAO,KAAK,QAAQ,MAAM,mBAAmB,CAAA;AAC7C,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAA;AACzC,OAAO,KAAK,WAAW,MAAM,sBAAsB,CAAA;AACnD,OAAO,KAAK,UAAU,MAAM,qBAAqB,CAAA;AACjD,OAAO,KAAK,QAAQ,MAAM,mBAAmB,CAAA;AAC7C,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAA;AACvC,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAA;AAC3C,OAAO,KAAK,IAAI,MAAM,eAAe,CAAA;AACrC,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAA;AAC3C,OAAO,KAAK,UAAU,MAAM,qBAAqB,CAAA;AACjD,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAA;AACzC,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAA;AACzC,OAAO,KAAK,QAAQ,MAAM,mBAAmB,CAAA;AAC7C,OAAO,KAAK,IAAI,MAAM,eAAe,CAAA"}
|
package/dist/ui/index.js
CHANGED
|
@@ -2,6 +2,7 @@ export * as Button from './button/public';
|
|
|
2
2
|
export * as Checkbox from './checkbox/public';
|
|
3
3
|
export * as Combobox from './combobox/public';
|
|
4
4
|
export * as Dialog from './dialog/public';
|
|
5
|
+
export * as DragAndDrop from './dragAndDrop/public';
|
|
5
6
|
export * as Disclosure from './disclosure/public';
|
|
6
7
|
export * as Fieldset from './fieldset/public';
|
|
7
8
|
export * as Input from './input/public';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/ui/listbox/shared.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/ui/listbox/shared.ts"],"names":[],"mappings":"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"}
|
|
@@ -128,7 +128,6 @@ export const closedModel = (model) => constrainedEvo(model, {
|
|
|
128
128
|
isOpen: () => false,
|
|
129
129
|
transitionState: () => model.isAnimated ? 'LeaveStart' : 'Idle',
|
|
130
130
|
maybeActiveItemIndex: () => Option.none(),
|
|
131
|
-
activationTrigger: () => 'Keyboard',
|
|
132
131
|
searchQuery: () => '',
|
|
133
132
|
searchVersion: () => 0,
|
|
134
133
|
maybeLastPointerPosition: () => Option.none(),
|
|
@@ -387,11 +386,16 @@ export const makeView = (behavior) => (config) => {
|
|
|
387
386
|
const firstEnabledIndex = findFirstEnabledIndex(items.length, 0, isItemDisabledByIndex)(0, 1);
|
|
388
387
|
const lastEnabledIndex = findFirstEnabledIndex(items.length, 0, isItemDisabledByIndex)(items.length - 1, -1);
|
|
389
388
|
const selectedItemIndex = behavior.selectedItemIndex(config.model, items, itemToValue);
|
|
390
|
-
const handleButtonKeyDown = (key) =>
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
389
|
+
const handleButtonKeyDown = (key) => {
|
|
390
|
+
if (isOpen) {
|
|
391
|
+
return handleItemsKeyDown(key);
|
|
392
|
+
}
|
|
393
|
+
return M.value(key).pipe(M.whenOr('Enter', ' ', 'ArrowDown', () => Option.some(toParentMessage(Opened({
|
|
394
|
+
maybeActiveItemIndex: Option.orElse(selectedItemIndex, () => Option.some(firstEnabledIndex)),
|
|
395
|
+
})))), M.when('ArrowUp', () => Option.some(toParentMessage(Opened({
|
|
396
|
+
maybeActiveItemIndex: Option.orElse(selectedItemIndex, () => Option.some(lastEnabledIndex)),
|
|
397
|
+
})))), M.orElse(() => Option.none()));
|
|
398
|
+
};
|
|
395
399
|
const handleButtonPointerDown = (pointerType, button) => Option.some(toParentMessage(PressedPointerOnButton({
|
|
396
400
|
pointerType,
|
|
397
401
|
button,
|
|
@@ -448,15 +452,24 @@ export const makeView = (behavior) => (config) => {
|
|
|
448
452
|
onSome: index => [AriaActiveDescendant(itemId(id, index))],
|
|
449
453
|
});
|
|
450
454
|
const hooks = anchor
|
|
451
|
-
? anchorHooks({
|
|
455
|
+
? anchorHooks({
|
|
456
|
+
buttonId: `${id}-button`,
|
|
457
|
+
anchor,
|
|
458
|
+
focusAfterPosition: true,
|
|
459
|
+
})
|
|
452
460
|
: undefined;
|
|
461
|
+
const focusOnInsert = (element) => {
|
|
462
|
+
if (element instanceof HTMLElement) {
|
|
463
|
+
element.focus();
|
|
464
|
+
}
|
|
465
|
+
};
|
|
453
466
|
const anchorAttributes = hooks
|
|
454
467
|
? [
|
|
455
468
|
Style({ position: 'absolute', margin: '0', visibility: 'hidden' }),
|
|
456
469
|
OnInsert(hooks.onInsert),
|
|
457
470
|
OnDestroy(hooks.onDestroy),
|
|
458
471
|
]
|
|
459
|
-
: [];
|
|
472
|
+
: [OnInsert(focusOnInsert)];
|
|
460
473
|
const itemsContainerAttributes = [
|
|
461
474
|
Id(`${id}-items`),
|
|
462
475
|
Role('listbox'),
|