foldkit 0.41.0 → 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/devtools/overlay.d.ts.map +1 -1
- package/dist/devtools/overlay.js +8 -5
- package/dist/runtime/runtime.d.ts +2 -0
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +17 -3
- 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/checkbox/index.d.ts +3 -3
- package/dist/ui/checkbox/index.d.ts.map +1 -1
- package/dist/ui/checkbox/index.js +11 -8
- package/dist/ui/combobox/multi.d.ts +4 -3
- package/dist/ui/combobox/multi.d.ts.map +1 -1
- package/dist/ui/combobox/multi.js +4 -4
- package/dist/ui/combobox/public.d.ts +2 -2
- package/dist/ui/combobox/public.d.ts.map +1 -1
- package/dist/ui/combobox/public.js +1 -1
- package/dist/ui/combobox/shared.d.ts +12 -1
- package/dist/ui/combobox/shared.d.ts.map +1 -1
- package/dist/ui/combobox/shared.js +32 -19
- package/dist/ui/combobox/single.d.ts +4 -3
- package/dist/ui/combobox/single.d.ts.map +1 -1
- package/dist/ui/combobox/single.js +4 -4
- package/dist/ui/dialog/index.d.ts +11 -3
- package/dist/ui/dialog/index.d.ts.map +1 -1
- package/dist/ui/dialog/index.js +15 -7
- package/dist/ui/dialog/public.d.ts +1 -1
- package/dist/ui/dialog/public.d.ts.map +1 -1
- package/dist/ui/dialog/public.js +1 -1
- package/dist/ui/disclosure/index.d.ts +8 -3
- package/dist/ui/disclosure/index.d.ts.map +1 -1
- package/dist/ui/disclosure/index.js +12 -7
- package/dist/ui/disclosure/public.d.ts +2 -2
- package/dist/ui/disclosure/public.d.ts.map +1 -1
- package/dist/ui/disclosure/public.js +1 -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/multi.d.ts +4 -3
- package/dist/ui/listbox/multi.d.ts.map +1 -1
- package/dist/ui/listbox/multi.js +4 -4
- package/dist/ui/listbox/public.d.ts +3 -3
- package/dist/ui/listbox/public.d.ts.map +1 -1
- package/dist/ui/listbox/public.js +2 -2
- package/dist/ui/listbox/shared.d.ts +14 -1
- package/dist/ui/listbox/shared.d.ts.map +1 -1
- package/dist/ui/listbox/shared.js +31 -16
- package/dist/ui/listbox/single.d.ts +10 -5
- package/dist/ui/listbox/single.d.ts.map +1 -1
- package/dist/ui/listbox/single.js +8 -5
- package/dist/ui/menu/index.d.ts +19 -3
- package/dist/ui/menu/index.d.ts.map +1 -1
- package/dist/ui/menu/index.js +39 -21
- package/dist/ui/menu/public.d.ts +2 -2
- package/dist/ui/menu/public.d.ts.map +1 -1
- package/dist/ui/menu/public.js +1 -1
- package/dist/ui/popover/index.d.ts +20 -3
- package/dist/ui/popover/index.d.ts.map +1 -1
- package/dist/ui/popover/index.js +31 -14
- package/dist/ui/popover/public.d.ts +2 -2
- package/dist/ui/popover/public.d.ts.map +1 -1
- package/dist/ui/popover/public.js +1 -1
- package/dist/ui/radioGroup/index.d.ts +10 -5
- package/dist/ui/radioGroup/index.d.ts.map +1 -1
- package/dist/ui/radioGroup/index.js +18 -20
- package/dist/ui/radioGroup/public.d.ts +2 -2
- package/dist/ui/radioGroup/public.d.ts.map +1 -1
- package/dist/ui/radioGroup/public.js +1 -1
- package/dist/ui/switch/index.d.ts +3 -3
- package/dist/ui/switch/index.d.ts.map +1 -1
- package/dist/ui/switch/index.js +11 -8
- package/dist/ui/tabs/index.d.ts +8 -3
- package/dist/ui/tabs/index.d.ts.map +1 -1
- package/dist/ui/tabs/index.js +15 -8
- package/dist/ui/tabs/public.d.ts +2 -2
- package/dist/ui/tabs/public.d.ts.map +1 -1
- package/dist/ui/tabs/public.js +1 -1
- 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';
|
|
@@ -153,7 +153,8 @@ export declare const view: <Message, Item>(config: Readonly<{
|
|
|
153
153
|
} & {
|
|
154
154
|
readonly selectedItems: readonly string[];
|
|
155
155
|
};
|
|
156
|
-
|
|
156
|
+
toParentMessage: (message: import("./shared").Opened | import("./shared").Closed | import("./shared").ClosedByTab | import("./shared").ActivatedItem | import("./shared").DeactivatedItem | import("./shared").SelectedItem | import("./shared").MovedPointerOverItem | import("./shared").RequestedItemClick | import("./shared").Searched | import("./shared").PressedPointerOnButton | import("./shared").IgnoredMouseClick | import("./shared").SuppressedSpaceScroll) => Message;
|
|
157
|
+
onSelectedItem?: (value: string) => Message;
|
|
157
158
|
items: readonly Item[];
|
|
158
159
|
itemToConfig: (item: Item, context: Readonly<{
|
|
159
160
|
isActive: boolean;
|
|
@@ -200,6 +201,6 @@ export declare const view: <Message, Item>(config: Readonly<{
|
|
|
200
201
|
isInvalid?: boolean;
|
|
201
202
|
}>) => Html;
|
|
202
203
|
/** Creates a memoized multi-select listbox view. Static config is captured in a closure;
|
|
203
|
-
* only `model` and `
|
|
204
|
-
export declare const lazy: <Message, Item>(staticConfig: Omit<ViewConfig<Message, Item>, "model" | "
|
|
204
|
+
* only `model` and `toParentMessage` are compared per render via `createLazy`. */
|
|
205
|
+
export declare const lazy: <Message, Item>(staticConfig: Omit<ViewConfig<Message, Item>, "model" | "toParentMessage" | "onSelectedItem">) => ((model: Model, toParentMessage: BaseViewConfig<Message, Item, Model>["toParentMessage"]) => Html);
|
|
205
206
|
//# sourceMappingURL=multi.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multi.d.ts","sourceRoot":"","sources":["../../../src/ui/listbox/multi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAE,MAAM,IAAI,CAAC,EAAQ,MAAM,QAAQ,CAAA;AAEzD,OAAO,EAAE,KAAK,IAAI,EAAc,MAAM,YAAY,CAAA;AAElD,OAAO,EACL,KAAK,cAAc,EAEnB,KAAK,cAAc,EAIpB,MAAM,UAAU,CAAA;AAIjB,iKAAiK;AACjK,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;GAEjB,CAAA;AAED,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAIrC,6RAA6R;AAC7R,MAAM,MAAM,UAAU,GAAG,cAAc,GACrC,QAAQ,CAAC;IACP,aAAa,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CACtC,CAAC,CAAA;AAEJ,4HAA4H;AAC5H,eAAO,MAAM,IAAI,GAAI,QAAQ,UAAU,KAAG,KAGxC,CAAA;AAIF,wJAAwJ;AACxJ,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAMjB,CAAA;AAIF,sEAAsE;AACtE,MAAM,MAAM,UAAU,CAAC,OAAO,EAAE,IAAI,IAAI,cAAc,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;AAE5E,0JAA0J;AAC1J,eAAO,MAAM,IAAI
|
|
1
|
+
{"version":3,"file":"multi.d.ts","sourceRoot":"","sources":["../../../src/ui/listbox/multi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAE,MAAM,IAAI,CAAC,EAAQ,MAAM,QAAQ,CAAA;AAEzD,OAAO,EAAE,KAAK,IAAI,EAAc,MAAM,YAAY,CAAA;AAElD,OAAO,EACL,KAAK,cAAc,EAEnB,KAAK,cAAc,EAIpB,MAAM,UAAU,CAAA;AAIjB,iKAAiK;AACjK,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;GAEjB,CAAA;AAED,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAIrC,6RAA6R;AAC7R,MAAM,MAAM,UAAU,GAAG,cAAc,GACrC,QAAQ,CAAC;IACP,aAAa,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CACtC,CAAC,CAAA;AAEJ,4HAA4H;AAC5H,eAAO,MAAM,IAAI,GAAI,QAAQ,UAAU,KAAG,KAGxC,CAAA;AAIF,wJAAwJ;AACxJ,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAMjB,CAAA;AAIF,sEAAsE;AACtE,MAAM,MAAM,UAAU,CAAC,OAAO,EAAE,IAAI,IAAI,cAAc,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;AAE5E,0JAA0J;AAC1J,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAYf,CAAA;AAEF;mFACmF;AACnF,eAAO,MAAM,IAAI,GAAI,OAAO,EAAE,IAAI,EAChC,cAAc,IAAI,CAChB,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,EACzB,OAAO,GAAG,iBAAiB,GAAG,gBAAgB,CAC/C,KACA,CAAC,CACF,KAAK,EAAE,KAAK,EACZ,eAAe,EAAE,cAAc,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,iBAAiB,CAAC,KACrE,IAAI,CAoBR,CAAA"}
|
package/dist/ui/listbox/multi.js
CHANGED
|
@@ -25,12 +25,12 @@ export const view = makeView({
|
|
|
25
25
|
ariaMultiSelectable: true,
|
|
26
26
|
});
|
|
27
27
|
/** Creates a memoized multi-select listbox view. Static config is captured in a closure;
|
|
28
|
-
* only `model` and `
|
|
28
|
+
* only `model` and `toParentMessage` are compared per render via `createLazy`. */
|
|
29
29
|
export const lazy = (staticConfig) => {
|
|
30
30
|
const lazyView = createLazy();
|
|
31
|
-
return (model,
|
|
31
|
+
return (model, toParentMessage) => lazyView((currentModel, currentToMessage) => view({
|
|
32
32
|
...staticConfig,
|
|
33
33
|
model: currentModel,
|
|
34
|
-
|
|
35
|
-
}), [model,
|
|
34
|
+
toParentMessage: currentToMessage,
|
|
35
|
+
}), [model, toParentMessage]);
|
|
36
36
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { init, update, view, lazy, Model } from './single';
|
|
2
|
-
export { Message, Orientation } from './shared';
|
|
1
|
+
export { init, update, selectItem, view, lazy, Model } from './single';
|
|
2
|
+
export { Message, Orientation, SelectedItem, CompletedLockScroll, CompletedUnlockScroll, CompletedSetupInert, CompletedTeardownInert, CompletedFocusButton, CompletedFocusItems, CompletedScrollIntoView, CompletedClickItem, ClearedSearch, AdvancedTransitionFrame, EndedTransition, DetectedButtonMovement, RequestFrame, LockScroll, UnlockScroll, InertOthers, RestoreInert, FocusButton, FocusItems, ScrollIntoView, ClickItem, DelayClearSearch, WaitForTransitions, DetectMovementOrTransitionEnd, } from './shared';
|
|
3
3
|
export { TransitionState } from '../transition';
|
|
4
|
-
export type { ActivationTrigger, Opened, Closed, ClosedByTab, ActivatedItem, DeactivatedItem,
|
|
4
|
+
export type { ActivationTrigger, Opened, Closed, ClosedByTab, ActivatedItem, DeactivatedItem, MovedPointerOverItem, RequestedItemClick, Searched, PressedPointerOnButton, IgnoredMouseClick, SuppressedSpaceScroll, ItemConfig, GroupHeading, } from './shared';
|
|
5
5
|
export type { InitConfig, ViewConfig } from './single';
|
|
6
6
|
export type { AnchorConfig } from '../anchor';
|
|
7
7
|
export * as Multi from './multiPublic';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/ui/listbox/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/ui/listbox/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,UAAU,CAAA;AAEtE,OAAO,EACL,OAAO,EACP,WAAW,EACX,YAAY,EACZ,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACnB,uBAAuB,EACvB,kBAAkB,EAClB,aAAa,EACb,uBAAuB,EACvB,eAAe,EACf,sBAAsB,EACtB,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,WAAW,EACX,UAAU,EACV,cAAc,EACd,SAAS,EACT,gBAAgB,EAChB,kBAAkB,EAClB,6BAA6B,GAC9B,MAAM,UAAU,CAAA;AAEjB,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAE/C,YAAY,EACV,iBAAiB,EACjB,MAAM,EACN,MAAM,EACN,WAAW,EACX,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,QAAQ,EACR,sBAAsB,EACtB,iBAAiB,EACjB,qBAAqB,EACrB,UAAU,EACV,YAAY,GACb,MAAM,UAAU,CAAA;AAEjB,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAEtD,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAE7C,OAAO,KAAK,KAAK,MAAM,eAAe,CAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { init, update, view, lazy, Model } from './single';
|
|
2
|
-
export { Message, Orientation } from './shared';
|
|
1
|
+
export { init, update, selectItem, view, lazy, Model } from './single';
|
|
2
|
+
export { Message, Orientation, SelectedItem, CompletedLockScroll, CompletedUnlockScroll, CompletedSetupInert, CompletedTeardownInert, CompletedFocusButton, CompletedFocusItems, CompletedScrollIntoView, CompletedClickItem, ClearedSearch, AdvancedTransitionFrame, EndedTransition, DetectedButtonMovement, RequestFrame, LockScroll, UnlockScroll, InertOthers, RestoreInert, FocusButton, FocusItems, ScrollIntoView, ClickItem, DelayClearSearch, WaitForTransitions, DetectMovementOrTransitionEnd, } from './shared';
|
|
3
3
|
export { TransitionState } from '../transition';
|
|
4
4
|
export * as Multi from './multiPublic';
|