foldkit-primitives 0.3.0 → 0.5.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 +144 -723
- package/dist/device/index.d.mts.map +1 -1
- package/dist/device/index.mjs +95 -87
- package/dist/device/index.mjs.map +1 -1
- package/dist/dom/index.d.mts +71 -3
- package/dist/dom/index.d.mts.map +1 -1
- package/dist/dom/index.mjs +303 -6
- package/dist/dom/index.mjs.map +1 -1
- package/dist/editable-text-CKkS_mWt.d.mts +235 -0
- package/dist/editable-text-CKkS_mWt.d.mts.map +1 -0
- package/dist/events/index.d.mts +8 -1
- package/dist/events/index.d.mts.map +1 -1
- package/dist/events/index.mjs +32 -17
- package/dist/events/index.mjs.map +1 -1
- package/dist/follow-tab-stop-BSZsUrrk.mjs +805 -0
- package/dist/follow-tab-stop-BSZsUrrk.mjs.map +1 -0
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +7 -7
- package/dist/interaction/index.d.mts +247 -92
- package/dist/interaction/index.d.mts.map +1 -1
- package/dist/interaction/index.mjs +507 -224
- package/dist/interaction/index.mjs.map +1 -1
- package/dist/internal-DP-xLKbT.mjs +37 -0
- package/dist/internal-DP-xLKbT.mjs.map +1 -0
- package/dist/media/index.d.mts.map +1 -1
- package/dist/media/index.mjs +3 -3
- package/dist/media/index.mjs.map +1 -1
- package/dist/motion/index.mjs +1 -1
- package/dist/{motion-LfJYAMnP.mjs → motion-DGGBZ0wt.mjs} +14 -13
- package/dist/motion-DGGBZ0wt.mjs.map +1 -0
- package/dist/net/index.d.mts +69 -5
- package/dist/net/index.d.mts.map +1 -1
- package/dist/net/index.mjs +139 -56
- package/dist/net/index.mjs.map +1 -1
- package/dist/observers/index.d.mts +1 -1
- package/dist/observers/index.mjs +3 -0
- package/dist/observers/index.mjs.map +1 -1
- package/dist/state/index.d.mts +54 -12
- package/dist/state/index.d.mts.map +1 -1
- package/dist/state/index.mjs +198 -90
- package/dist/state/index.mjs.map +1 -1
- package/dist/time/index.d.mts +21 -1
- package/dist/time/index.d.mts.map +1 -1
- package/dist/time/index.mjs +48 -19
- package/dist/time/index.mjs.map +1 -1
- package/package.json +20 -9
- package/dist/motion-LfJYAMnP.mjs.map +0 -1
- package/dist/move-CZwHyTGk.mjs +0 -215
- package/dist/move-CZwHyTGk.mjs.map +0 -1
- package/dist/move-DNiMPcuf.d.mts +0 -58
- package/dist/move-DNiMPcuf.d.mts.map +0 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "../rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { C as MoveEnded, D as ScrollLock, E as HideOutside, O as FocusScope, S as Move, T as Moved, _ as zoneOf, a as TextCommitted, b as Targets, d as DragPlace, f as DragStarted, g as boxOf, h as PointerDrag, i as TextCancelled, k as tabbableWithin, l as DragCancelled, m as DraggedOver, n as EditAsked, o as TextEdited, p as DragZone, r as EditableText, s as textOf, t as FollowTabStop, u as DragDropped, v as TargetHovered, w as MoveStarted, x as targetOf, y as TargetPressed } from "../follow-tab-stop-BSZsUrrk.mjs";
|
|
3
|
+
import { n as perInput, r as sameIds, t as clearQuery } from "../internal-DP-xLKbT.mjs";
|
|
3
4
|
import { a as modalityOfKey, i as Modality, t as InputModality } from "../input-modality-m6v7__Xs.mjs";
|
|
4
5
|
import { Effect, Option, Queue, Schema, Stream } from "effect";
|
|
5
6
|
import * as Mount from "foldkit/mount";
|
|
@@ -23,11 +24,11 @@ var dismiss_layer_exports = /* @__PURE__ */ __exportAll({
|
|
|
23
24
|
Dismiss: () => Dismiss,
|
|
24
25
|
LAYER_ATTRIBUTE: () => LAYER_ATTRIBUTE,
|
|
25
26
|
Layer: () => Layer$1,
|
|
26
|
-
Message: () => Message$
|
|
27
|
-
Model: () => Model$
|
|
27
|
+
Message: () => Message$9,
|
|
28
|
+
Model: () => Model$9,
|
|
28
29
|
TRIGGER_ATTRIBUTE: () => TRIGGER_ATTRIBUTE,
|
|
29
|
-
behavior: () => behavior$
|
|
30
|
-
bundle: () => bundle$
|
|
30
|
+
behavior: () => behavior$14,
|
|
31
|
+
bundle: () => bundle$9,
|
|
31
32
|
layersOnPath: () => layersOnPath,
|
|
32
33
|
toDismiss: () => toDismiss
|
|
33
34
|
});
|
|
@@ -43,10 +44,10 @@ const Layer$1 = Schema.Struct({
|
|
|
43
44
|
/** Dismiss on Escape when topmost. */
|
|
44
45
|
escape: Schema.Boolean
|
|
45
46
|
});
|
|
46
|
-
const Model$
|
|
47
|
+
const Model$9 = Schema.Struct({
|
|
47
48
|
/** The open layers at the last event, bottom to top. */
|
|
48
49
|
layers: Schema.Array(Layer$1) });
|
|
49
|
-
const Message$
|
|
50
|
+
const Message$9 = defineMessageUnion({
|
|
50
51
|
/** A pointer went down; `inside` are the layers whose element or trigger was on the event's path. */
|
|
51
52
|
PressedAt: {
|
|
52
53
|
layers: Schema.Array(Layer$1),
|
|
@@ -63,26 +64,30 @@ const toDismiss = (layers, inside) => {
|
|
|
63
64
|
});
|
|
64
65
|
return layers.slice(topmostHit + 1).filter((layer) => layer.outside).map((layer) => layer.id);
|
|
65
66
|
};
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
const sameLayers = (a, b) => a.length === b.length && a.every((layer, index) => layer.id === b[index].id && layer.outside === b[index].outside && layer.escape === b[index].escape);
|
|
68
|
+
const bundle$9 = Bundle.make("DismissLayer", {
|
|
69
|
+
Model: Model$9,
|
|
70
|
+
Message: Message$9,
|
|
69
71
|
init: () => ({ model: { layers: [] } }),
|
|
70
|
-
update: (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
72
|
+
update: (model, message) => {
|
|
73
|
+
const next = sameLayers(model.layers, message.layers) ? model : { layers: message.layers };
|
|
74
|
+
return Message$9.match(message, {
|
|
75
|
+
PressedAt: ({ layers, inside }) => {
|
|
76
|
+
const ids = toDismiss(layers, inside);
|
|
77
|
+
return ids.length === 0 ? { model: next } : {
|
|
78
|
+
model: next,
|
|
79
|
+
outMessage: Dismiss.make({ ids })
|
|
80
|
+
};
|
|
81
|
+
},
|
|
82
|
+
PressedEscape: ({ layers }) => {
|
|
83
|
+
const top = layers[layers.length - 1];
|
|
84
|
+
return top !== void 0 && top.escape ? {
|
|
85
|
+
model: next,
|
|
86
|
+
outMessage: Dismiss.make({ ids: [top.id] })
|
|
87
|
+
} : { model: next };
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
},
|
|
86
91
|
subscriptions: () => Subscription.make()(() => ({ document: Subscription.persistent(documentEvents()) }))
|
|
87
92
|
});
|
|
88
93
|
const openLayers = (root) => Array.from(root.querySelectorAll(`[${LAYER_ATTRIBUTE}]`)).map((element) => ({
|
|
@@ -108,13 +113,13 @@ const layersOnPath = (path) => {
|
|
|
108
113
|
const documentEvents = () => {
|
|
109
114
|
if (typeof document === "undefined") return Stream.empty;
|
|
110
115
|
return Stream.callback((queue) => Effect.acquireRelease(Effect.sync(() => {
|
|
111
|
-
const onPointerDown = (event) => Queue.offerUnsafe(queue, Message$
|
|
116
|
+
const onPointerDown = (event) => Queue.offerUnsafe(queue, Message$9.PressedAt({
|
|
112
117
|
layers: openLayers(document),
|
|
113
118
|
inside: layersOnPath(event.composedPath())
|
|
114
119
|
}));
|
|
115
120
|
const onKeyDown = (event) => {
|
|
116
121
|
if (event.key !== "Escape") return;
|
|
117
|
-
Queue.offerUnsafe(queue, Message$
|
|
122
|
+
Queue.offerUnsafe(queue, Message$9.PressedEscape({ layers: openLayers(document) }));
|
|
118
123
|
};
|
|
119
124
|
document.addEventListener("pointerdown", onPointerDown, { capture: true });
|
|
120
125
|
document.addEventListener("keydown", onKeyDown, { capture: true });
|
|
@@ -133,7 +138,7 @@ const documentEvents = () => {
|
|
|
133
138
|
* parent Model is the whole lifecycle. Not for an element with `popover`,
|
|
134
139
|
* which the browser dismisses itself.
|
|
135
140
|
*/
|
|
136
|
-
const behavior$
|
|
141
|
+
const behavior$14 = (_declared) => (slots) => (options) => Behavior.forSlots(slots)({
|
|
137
142
|
[options.layer]: Behavior.slot({
|
|
138
143
|
requires: { capability: Capability.Container },
|
|
139
144
|
attributes: ({ input, h }) => [
|
|
@@ -148,13 +153,34 @@ const behavior$10 = (_declared) => (slots) => (options) => Behavior.forSlots(slo
|
|
|
148
153
|
}) }
|
|
149
154
|
}, { name: "DismissLayer" });
|
|
150
155
|
//#endregion
|
|
156
|
+
//#region src/interaction/editable-text.ts
|
|
157
|
+
/**
|
|
158
|
+
* `EditableText` as a Behavior: attaches the `foldkit-primitives/dom` Mount to
|
|
159
|
+
* a container slot and maps what it reports into the view's Messages. No
|
|
160
|
+
* Bundle: the text typed is a fact, and what it changes is the parent's
|
|
161
|
+
* `update`.
|
|
162
|
+
*/
|
|
163
|
+
var editable_text_exports = /* @__PURE__ */ __exportAll({
|
|
164
|
+
EditAsked: () => EditAsked,
|
|
165
|
+
TextCancelled: () => TextCancelled,
|
|
166
|
+
TextCommitted: () => TextCommitted,
|
|
167
|
+
TextEdited: () => TextEdited,
|
|
168
|
+
behavior: () => behavior$13,
|
|
169
|
+
mount: () => EditableText,
|
|
170
|
+
textOf: () => textOf
|
|
171
|
+
});
|
|
172
|
+
const behavior$13 = (slots) => (options) => Behavior.forSlots(slots)({ [options.container]: Behavior.slot({
|
|
173
|
+
requires: { capability: Capability.Container },
|
|
174
|
+
mount: () => Mount.mapMessage(EditableText({ attribute: options.attribute }), options.toMessage)
|
|
175
|
+
}) }, { name: "EditableText" });
|
|
176
|
+
//#endregion
|
|
151
177
|
//#region src/interaction/focus-scope.ts
|
|
152
178
|
/**
|
|
153
179
|
* `FocusScope` as a Behavior: attaches the `foldkit-primitives/dom` Mount to a
|
|
154
180
|
* container slot. No Bundle, because which element has focus is a DOM fact.
|
|
155
181
|
*/
|
|
156
182
|
var focus_scope_exports = /* @__PURE__ */ __exportAll({
|
|
157
|
-
behavior: () => behavior$
|
|
183
|
+
behavior: () => behavior$12,
|
|
158
184
|
mount: () => FocusScope,
|
|
159
185
|
tabbableWithin: () => tabbableWithin
|
|
160
186
|
});
|
|
@@ -165,7 +191,7 @@ var focus_scope_exports = /* @__PURE__ */ __exportAll({
|
|
|
165
191
|
* `restore`, focus returns to the element that had it. A dialog that is not
|
|
166
192
|
* a native `<dialog>`, a menu, or a command palette takes this.
|
|
167
193
|
*/
|
|
168
|
-
const behavior$
|
|
194
|
+
const behavior$12 = (slots) => (options) => Behavior.forSlots(slots)({ [options.container]: Behavior.slot({
|
|
169
195
|
requires: { capability: Capability.Container },
|
|
170
196
|
mount: () => FocusScope({
|
|
171
197
|
contain: options.contain ?? true,
|
|
@@ -177,11 +203,11 @@ const behavior$9 = (slots) => (options) => Behavior.forSlots(slots)({ [options.c
|
|
|
177
203
|
//#region src/interaction/focus-visible.ts
|
|
178
204
|
var focus_visible_exports = /* @__PURE__ */ __exportAll({
|
|
179
205
|
Modality: () => Modality,
|
|
180
|
-
behavior: () => behavior$
|
|
206
|
+
behavior: () => behavior$11,
|
|
181
207
|
bundle: () => InputModality,
|
|
182
208
|
modalityOfKey: () => modalityOfKey
|
|
183
209
|
});
|
|
184
|
-
const behavior$
|
|
210
|
+
const behavior$11 = (declared) => (slots) => (options) => Behavior.forSlots(slots)({ [options.target]: Behavior.slot({
|
|
185
211
|
requires: { capability: Capability.Focusable },
|
|
186
212
|
attributes: ({ input, h }) => input[declared.field].modality === "keyboard" ? [h.DataAttribute("focus-visible", "true")] : []
|
|
187
213
|
}) }, { name: "FocusVisible" });
|
|
@@ -195,29 +221,29 @@ const behavior$8 = (declared) => (slots) => (options) => Behavior.forSlots(slots
|
|
|
195
221
|
* function of the items and the key; the Behavior wires it to the slots.
|
|
196
222
|
*/
|
|
197
223
|
var roving_tabindex_exports = /* @__PURE__ */ __exportAll({
|
|
198
|
-
Args: () => Args$
|
|
199
|
-
Message: () => Message$
|
|
200
|
-
Model: () => Model$
|
|
224
|
+
Args: () => Args$8,
|
|
225
|
+
Message: () => Message$8,
|
|
226
|
+
Model: () => Model$8,
|
|
201
227
|
Orientation: () => Orientation,
|
|
202
|
-
behavior: () => behavior$
|
|
203
|
-
bundle: () => bundle$
|
|
228
|
+
behavior: () => behavior$10,
|
|
229
|
+
bundle: () => bundle$8,
|
|
204
230
|
idSelector: () => idSelector,
|
|
205
231
|
itemAttributes: () => itemAttributes,
|
|
206
|
-
move: () => move$
|
|
207
|
-
tabStop: () => tabStop
|
|
232
|
+
move: () => move$2,
|
|
233
|
+
tabStop: () => tabStop$1
|
|
208
234
|
});
|
|
209
235
|
const Orientation = Schema.Literals([
|
|
210
236
|
"vertical",
|
|
211
237
|
"horizontal",
|
|
212
238
|
"both"
|
|
213
239
|
]);
|
|
214
|
-
const Model$
|
|
240
|
+
const Model$8 = Schema.Struct({
|
|
215
241
|
/** The current item's id, or `null` before any item has been focused. */
|
|
216
242
|
current: Schema.NullOr(Schema.String) });
|
|
217
|
-
const Message$
|
|
243
|
+
const Message$8 = defineMessageUnion({
|
|
218
244
|
/** An item became current: the user focused it, or a key moved there. */
|
|
219
245
|
Focused: { id: Schema.String } });
|
|
220
|
-
const Args$
|
|
246
|
+
const Args$8 = Schema.Struct({
|
|
221
247
|
orientation: Orientation,
|
|
222
248
|
/** Wrap from the last enabled item to the first, and back. */
|
|
223
249
|
loop: Schema.Boolean,
|
|
@@ -225,14 +251,14 @@ const Args$7 = Schema.Struct({
|
|
|
225
251
|
* `aria-activedescendant` instead of moving focus. */
|
|
226
252
|
virtual: Schema.Boolean
|
|
227
253
|
});
|
|
228
|
-
const bundle$
|
|
229
|
-
Model: Model$
|
|
230
|
-
Message: Message$
|
|
231
|
-
args: Args$
|
|
254
|
+
const bundle$8 = Bundle.make("RovingTabindex", {
|
|
255
|
+
Model: Model$8,
|
|
256
|
+
Message: Message$8,
|
|
257
|
+
args: Args$8,
|
|
232
258
|
init: () => ({ model: { current: null } }),
|
|
233
|
-
update: (
|
|
259
|
+
update: (model, message) => model.current === message.id ? { model } : { model: { current: message.id } }
|
|
234
260
|
});
|
|
235
|
-
const isPlain = (modifiers) => !modifiers.ctrlKey && !modifiers.altKey && !modifiers.metaKey;
|
|
261
|
+
const isPlain$1 = (modifiers) => !modifiers.ctrlKey && !modifiers.altKey && !modifiers.metaKey;
|
|
236
262
|
/**
|
|
237
263
|
* The index the key moves to from `current`, over the enabled indices only, or
|
|
238
264
|
* `undefined` when the key is not a navigation key here. `current` may be
|
|
@@ -240,8 +266,8 @@ const isPlain = (modifiers) => !modifiers.ctrlKey && !modifiers.altKey && !modif
|
|
|
240
266
|
* item. Under `rtl`, left and right swap. PageUp and PageDown move by `page`
|
|
241
267
|
* and clamp at the ends, never wrapping.
|
|
242
268
|
*/
|
|
243
|
-
const move$
|
|
244
|
-
if (enabled.length === 0 || !isPlain(modifiers)) return void 0;
|
|
269
|
+
const move$2 = (enabled, current, key, modifiers, options) => {
|
|
270
|
+
if (enabled.length === 0 || !isPlain$1(modifiers)) return void 0;
|
|
245
271
|
const first = enabled[0];
|
|
246
272
|
const last = enabled[enabled.length - 1];
|
|
247
273
|
if (key === "Home") return first;
|
|
@@ -266,7 +292,7 @@ const move$1 = (enabled, current, key, modifiers, options) => {
|
|
|
266
292
|
return enabled[next];
|
|
267
293
|
};
|
|
268
294
|
/** The item that holds the tab stop: the current one, else the first enabled. */
|
|
269
|
-
const tabStop = (items, current) => {
|
|
295
|
+
const tabStop$1 = (items, current) => {
|
|
270
296
|
const index = current === null ? -1 : items.indexOf(current);
|
|
271
297
|
if (index !== -1 && !items.isDisabled(index)) return index;
|
|
272
298
|
return items.enabled[0] ?? -1;
|
|
@@ -281,7 +307,7 @@ const idSelector = (id) => `[id="${id.replace(/["\\]/g, "\\$&")}"]`;
|
|
|
281
307
|
const itemAttributes = (h, items, current, item, virtual, focused) => {
|
|
282
308
|
const id = item.id ?? items.ids[item.index];
|
|
283
309
|
if (id === void 0) return [];
|
|
284
|
-
const isStop = tabStop(items, current) === item.index;
|
|
310
|
+
const isStop = tabStop$1(items, current) === item.index;
|
|
285
311
|
return [...virtual ? [] : [h.Tabindex(isStop ? 0 : -1)], h.OnFocus(focused(id))];
|
|
286
312
|
};
|
|
287
313
|
/**
|
|
@@ -295,21 +321,22 @@ const itemAttributes = (h, items, current, item, virtual, focused) => {
|
|
|
295
321
|
* attributes are data, so a view that no longer attaches this leaves no
|
|
296
322
|
* `tabindex` behind.
|
|
297
323
|
*/
|
|
298
|
-
const behavior$
|
|
299
|
-
const wrap = (id) => declared.wrapper.make(Message$
|
|
324
|
+
const behavior$10 = (declared, args) => (slots) => (options) => {
|
|
325
|
+
const wrap = (id) => declared.wrapper.make(Message$8.Focused({ id }));
|
|
300
326
|
const slice = (input) => input[declared.field];
|
|
327
|
+
const itemsOf = perInput(options.items);
|
|
301
328
|
return Behavior.forSlots(slots)({
|
|
302
329
|
[options.container]: Behavior.slot({
|
|
303
330
|
requires: { capability: Capability.Interactive },
|
|
304
331
|
attributes: ({ input, h }) => {
|
|
305
|
-
const items =
|
|
332
|
+
const items = itemsOf(input);
|
|
306
333
|
const current = slice(input).current;
|
|
307
334
|
const from = current === null ? -1 : items.indexOf(current);
|
|
308
335
|
const direction = options.direction?.(input) ?? "ltr";
|
|
309
|
-
const stop = tabStop(items, current);
|
|
336
|
+
const stop = tabStop$1(items, current);
|
|
310
337
|
const stopId = stop === -1 ? void 0 : items.ids[stop];
|
|
311
338
|
const target = (key, modifiers) => {
|
|
312
|
-
const next = move$
|
|
339
|
+
const next = move$2(items.enabled, from, key, modifiers, {
|
|
313
340
|
...args,
|
|
314
341
|
direction
|
|
315
342
|
});
|
|
@@ -326,7 +353,7 @@ const behavior$7 = (declared, args) => (slots) => (options) => {
|
|
|
326
353
|
requires: { capability: Capability.Focusable },
|
|
327
354
|
attributes: ({ input, h, item }) => {
|
|
328
355
|
if (item === void 0) return [];
|
|
329
|
-
return itemAttributes(h,
|
|
356
|
+
return itemAttributes(h, itemsOf(input), slice(input).current, item, args.virtual, wrap);
|
|
330
357
|
}
|
|
331
358
|
})
|
|
332
359
|
}, { name: "RovingTabindex" });
|
|
@@ -343,20 +370,20 @@ const behavior$7 = (declared, args) => (slots) => (options) => {
|
|
|
343
370
|
* on a view.
|
|
344
371
|
*/
|
|
345
372
|
var grid_navigation_exports = /* @__PURE__ */ __exportAll({
|
|
346
|
-
Args: () => Args$
|
|
347
|
-
Message: () => Message$
|
|
348
|
-
Model: () => Model$
|
|
349
|
-
behavior: () => behavior$
|
|
350
|
-
bundle: () => bundle$
|
|
351
|
-
move: () => move
|
|
373
|
+
Args: () => Args$7,
|
|
374
|
+
Message: () => Message$7,
|
|
375
|
+
Model: () => Model$7,
|
|
376
|
+
behavior: () => behavior$9,
|
|
377
|
+
bundle: () => bundle$7,
|
|
378
|
+
move: () => move$1
|
|
352
379
|
});
|
|
353
|
-
const Model$
|
|
380
|
+
const Model$7 = Schema.Struct({
|
|
354
381
|
/** The current cell's id, or `null` before any cell has been focused. */
|
|
355
382
|
current: Schema.NullOr(Schema.String) });
|
|
356
|
-
const Message$
|
|
383
|
+
const Message$7 = defineMessageUnion({
|
|
357
384
|
/** A cell became current: the user focused it, or a key moved there. */
|
|
358
385
|
Focused: { id: Schema.String } });
|
|
359
|
-
const Args$
|
|
386
|
+
const Args$7 = Schema.Struct({
|
|
360
387
|
/** Cells per row; the last row may be short. */
|
|
361
388
|
columns: Schema.Number.pipe(Schema.check(Schema.isGreaterThanOrEqualTo(1))),
|
|
362
389
|
/** Left and right continue into the previous and next row; up and down
|
|
@@ -366,12 +393,12 @@ const Args$6 = Schema.Struct({
|
|
|
366
393
|
* `aria-activedescendant` instead of moving focus. */
|
|
367
394
|
virtual: Schema.Boolean
|
|
368
395
|
});
|
|
369
|
-
const bundle$
|
|
370
|
-
Model: Model$
|
|
371
|
-
Message: Message$
|
|
372
|
-
args: Args$
|
|
396
|
+
const bundle$7 = Bundle.make("GridNavigation", {
|
|
397
|
+
Model: Model$7,
|
|
398
|
+
Message: Message$7,
|
|
399
|
+
args: Args$7,
|
|
373
400
|
init: () => ({ model: { current: null } }),
|
|
374
|
-
update: (
|
|
401
|
+
update: (model, message) => model.current === message.id ? { model } : { model: { current: message.id } }
|
|
375
402
|
});
|
|
376
403
|
/**
|
|
377
404
|
* The index the key moves to from `current` over a grid of `count` cells in
|
|
@@ -384,7 +411,7 @@ const bundle$6 = Bundle.make("GridNavigation", {
|
|
|
384
411
|
* the first enabled cell and a backward key on the last. Under `rtl`, left
|
|
385
412
|
* and right swap.
|
|
386
413
|
*/
|
|
387
|
-
const move = (enabled, count, current, key, modifiers, options) => {
|
|
414
|
+
const move$1 = (enabled, count, current, key, modifiers, options) => {
|
|
388
415
|
if (enabled.length === 0 || modifiers.altKey || modifiers.metaKey) return void 0;
|
|
389
416
|
const first = enabled[0];
|
|
390
417
|
const last = enabled[enabled.length - 1];
|
|
@@ -430,21 +457,22 @@ const move = (enabled, count, current, key, modifiers, options) => {
|
|
|
430
457
|
* only dispatch and point with `aria-activedescendant`; each cell gets
|
|
431
458
|
* `tabindex` 0 or -1 and reports `Focused` when focused.
|
|
432
459
|
*/
|
|
433
|
-
const behavior$
|
|
434
|
-
const wrap = (id) => declared.wrapper.make(Message$
|
|
460
|
+
const behavior$9 = (declared, args) => (slots) => (options) => {
|
|
461
|
+
const wrap = (id) => declared.wrapper.make(Message$7.Focused({ id }));
|
|
435
462
|
const slice = (input) => input[declared.field];
|
|
463
|
+
const itemsOf = perInput(options.items);
|
|
436
464
|
return Behavior.forSlots(slots)({
|
|
437
465
|
[options.container]: Behavior.slot({
|
|
438
466
|
requires: { capability: Capability.Interactive },
|
|
439
467
|
attributes: ({ input, h }) => {
|
|
440
|
-
const items =
|
|
468
|
+
const items = itemsOf(input);
|
|
441
469
|
const current = slice(input).current;
|
|
442
470
|
const from = current === null ? -1 : items.indexOf(current);
|
|
443
471
|
const direction = options.direction?.(input) ?? "ltr";
|
|
444
|
-
const stop = tabStop(items, current);
|
|
472
|
+
const stop = tabStop$1(items, current);
|
|
445
473
|
const stopId = stop === -1 ? void 0 : items.ids[stop];
|
|
446
474
|
const target = (key, modifiers) => {
|
|
447
|
-
const next = move(items.enabled, items.size, from, key, modifiers, {
|
|
475
|
+
const next = move$1(items.enabled, items.size, from, key, modifiers, {
|
|
448
476
|
columns: args.columns,
|
|
449
477
|
wrap: args.wrap,
|
|
450
478
|
direction
|
|
@@ -462,7 +490,7 @@ const behavior$6 = (declared, args) => (slots) => (options) => {
|
|
|
462
490
|
requires: { capability: Capability.Focusable },
|
|
463
491
|
attributes: ({ input, h, item }) => {
|
|
464
492
|
if (item === void 0) return [];
|
|
465
|
-
return itemAttributes(h,
|
|
493
|
+
return itemAttributes(h, itemsOf(input), slice(input).current, item, args.virtual, wrap);
|
|
466
494
|
}
|
|
467
495
|
})
|
|
468
496
|
}, { name: "GridNavigation" });
|
|
@@ -495,18 +523,18 @@ const hideOutside = mountOn("HideOutside", () => HideOutside());
|
|
|
495
523
|
* once in the page and style them visually hidden.
|
|
496
524
|
*/
|
|
497
525
|
var live_announce_exports = /* @__PURE__ */ __exportAll({
|
|
498
|
-
Args: () => Args$
|
|
499
|
-
Message: () => Message$
|
|
500
|
-
Model: () => Model$
|
|
526
|
+
Args: () => Args$6,
|
|
527
|
+
Message: () => Message$6,
|
|
528
|
+
Model: () => Model$6,
|
|
501
529
|
Politeness: () => Politeness,
|
|
502
530
|
REGION_ATTRIBUTE: () => REGION_ATTRIBUTE,
|
|
503
|
-
bundle: () => bundle$
|
|
531
|
+
bundle: () => bundle$6,
|
|
504
532
|
reread: () => reread,
|
|
505
533
|
say: () => say,
|
|
506
534
|
view: () => view
|
|
507
535
|
});
|
|
508
536
|
const Politeness = Schema.Literals(["polite", "assertive"]);
|
|
509
|
-
const Model$
|
|
537
|
+
const Model$6 = Schema.Struct({
|
|
510
538
|
polite: Schema.String,
|
|
511
539
|
assertive: Schema.String,
|
|
512
540
|
/** Waiting for the debounce to read. */
|
|
@@ -516,7 +544,7 @@ const Model$5 = Schema.Struct({
|
|
|
516
544
|
})),
|
|
517
545
|
generation: Schema.Number
|
|
518
546
|
});
|
|
519
|
-
const Message$
|
|
547
|
+
const Message$6 = defineMessageUnion({
|
|
520
548
|
Announced: {
|
|
521
549
|
message: Schema.String,
|
|
522
550
|
politeness: Politeness
|
|
@@ -525,7 +553,7 @@ const Message$5 = defineMessageUnion({
|
|
|
525
553
|
Cleared: { generation: Schema.Number }
|
|
526
554
|
});
|
|
527
555
|
const positive = Schema.Number.pipe(Schema.check(Schema.isGreaterThanOrEqualTo(0)), Schema.check(Schema.isFinite()));
|
|
528
|
-
const Args$
|
|
556
|
+
const Args$6 = Schema.Struct({
|
|
529
557
|
/** A burst of announcements inside this window reads once, the last one. */
|
|
530
558
|
debounceMs: positive,
|
|
531
559
|
/** How long the text stays in the region. */
|
|
@@ -539,17 +567,17 @@ const after = (ms, message, name, generation) => ({
|
|
|
539
567
|
args: { generation },
|
|
540
568
|
effect: Effect.as(Effect.sleep(ms), message)
|
|
541
569
|
});
|
|
542
|
-
const bundle$
|
|
543
|
-
Model: Model$
|
|
544
|
-
Message: Message$
|
|
545
|
-
args: Args$
|
|
570
|
+
const bundle$6 = Bundle.make("LiveAnnounce", {
|
|
571
|
+
Model: Model$6,
|
|
572
|
+
Message: Message$6,
|
|
573
|
+
args: Args$6,
|
|
546
574
|
init: () => ({ model: {
|
|
547
575
|
polite: "",
|
|
548
576
|
assertive: "",
|
|
549
577
|
pending: null,
|
|
550
578
|
generation: 0
|
|
551
579
|
} }),
|
|
552
|
-
update: (model, message, args) => Message$
|
|
580
|
+
update: (model, message, args) => Message$6.match(message, {
|
|
553
581
|
Announced: ({ message: text, politeness }) => {
|
|
554
582
|
const generation = model.generation + 1;
|
|
555
583
|
return {
|
|
@@ -561,7 +589,7 @@ const bundle$5 = Bundle.make("LiveAnnounce", {
|
|
|
561
589
|
},
|
|
562
590
|
generation
|
|
563
591
|
},
|
|
564
|
-
commands: [after(args.debounceMs, Message$
|
|
592
|
+
commands: [after(args.debounceMs, Message$6.Read({ generation }), "LiveAnnounce.read", generation)]
|
|
565
593
|
};
|
|
566
594
|
},
|
|
567
595
|
Read: ({ generation }) => {
|
|
@@ -574,7 +602,7 @@ const bundle$5 = Bundle.make("LiveAnnounce", {
|
|
|
574
602
|
pending: null,
|
|
575
603
|
[region]: reread(model[region], text)
|
|
576
604
|
},
|
|
577
|
-
commands: [after(args.clearAfterMs, Message$
|
|
605
|
+
commands: [after(args.clearAfterMs, Message$6.Cleared({ generation }), "LiveAnnounce.clear", generation)]
|
|
578
606
|
};
|
|
579
607
|
},
|
|
580
608
|
Cleared: ({ generation }) => generation === model.generation ? { model: {
|
|
@@ -585,7 +613,7 @@ const bundle$5 = Bundle.make("LiveAnnounce", {
|
|
|
585
613
|
})
|
|
586
614
|
});
|
|
587
615
|
/** The parent Message that announces `text`, to return from `update` or `onOut`. */
|
|
588
|
-
const say = (declared) => (text, politeness = "polite") => declared.wrapper.make(Message$
|
|
616
|
+
const say = (declared) => (text, politeness = "polite") => declared.wrapper.make(Message$6.Announced({
|
|
589
617
|
message: text,
|
|
590
618
|
politeness
|
|
591
619
|
}));
|
|
@@ -613,21 +641,21 @@ const view = (model, h) => h.div([h.Attribute(REGION_ATTRIBUTE, "regions")], [h.
|
|
|
613
641
|
* TestClock drives it. Which item matches is a pure function.
|
|
614
642
|
*/
|
|
615
643
|
var typeahead_exports = /* @__PURE__ */ __exportAll({
|
|
616
|
-
Args: () => Args$
|
|
617
|
-
Message: () => Message$
|
|
618
|
-
Model: () => Model$
|
|
619
|
-
behavior: () => behavior$
|
|
620
|
-
bundle: () => bundle$
|
|
644
|
+
Args: () => Args$5,
|
|
645
|
+
Message: () => Message$5,
|
|
646
|
+
Model: () => Model$5,
|
|
647
|
+
behavior: () => behavior$8,
|
|
648
|
+
bundle: () => bundle$5,
|
|
621
649
|
isPrintable: () => isPrintable,
|
|
622
650
|
match: () => match
|
|
623
651
|
});
|
|
624
|
-
const Model$
|
|
652
|
+
const Model$5 = Schema.Struct({
|
|
625
653
|
query: Schema.String,
|
|
626
654
|
/** Bumped per keystroke, carried by the clear it schedules, so a superseded
|
|
627
655
|
* timer's `Expired` changes nothing. */
|
|
628
656
|
generation: Schema.Number
|
|
629
657
|
});
|
|
630
|
-
const Message$
|
|
658
|
+
const Message$5 = defineMessageUnion({
|
|
631
659
|
/** A printable key was pressed with no ctrl, alt or meta. */
|
|
632
660
|
Typed: { char: Schema.String },
|
|
633
661
|
/** The clear timer fired for `generation`. */
|
|
@@ -635,18 +663,18 @@ const Message$4 = defineMessageUnion({
|
|
|
635
663
|
/** The query was dropped on purpose (a blur, a selection). */
|
|
636
664
|
Cleared: {}
|
|
637
665
|
});
|
|
638
|
-
const Args$
|
|
666
|
+
const Args$5 = Schema.Struct({
|
|
639
667
|
/** How long the query survives after the last key. */
|
|
640
668
|
timeoutMs: Schema.Number.pipe(Schema.check(Schema.isGreaterThan(0)), Schema.check(Schema.isFinite())) });
|
|
641
|
-
const bundle$
|
|
642
|
-
Model: Model$
|
|
643
|
-
Message: Message$
|
|
644
|
-
args: Args$
|
|
669
|
+
const bundle$5 = Bundle.make("Typeahead", {
|
|
670
|
+
Model: Model$5,
|
|
671
|
+
Message: Message$5,
|
|
672
|
+
args: Args$5,
|
|
645
673
|
init: () => ({ model: {
|
|
646
674
|
query: "",
|
|
647
675
|
generation: 0
|
|
648
676
|
} }),
|
|
649
|
-
update: (model, message, args) => Message$
|
|
677
|
+
update: (model, message, args) => Message$5.match(message, {
|
|
650
678
|
Typed: ({ char }) => {
|
|
651
679
|
const generation = model.generation + 1;
|
|
652
680
|
return {
|
|
@@ -657,18 +685,12 @@ const bundle$4 = Bundle.make("Typeahead", {
|
|
|
657
685
|
commands: [{
|
|
658
686
|
name: "Typeahead.expire",
|
|
659
687
|
args: { generation },
|
|
660
|
-
effect: Effect.as(Effect.sleep(args.timeoutMs), Message$
|
|
688
|
+
effect: Effect.as(Effect.sleep(args.timeoutMs), Message$5.Expired({ generation }))
|
|
661
689
|
}]
|
|
662
690
|
};
|
|
663
691
|
},
|
|
664
|
-
Expired: ({ generation }) => generation === model.generation ? { model
|
|
665
|
-
|
|
666
|
-
query: ""
|
|
667
|
-
} } : { model },
|
|
668
|
-
Cleared: () => ({ model: {
|
|
669
|
-
...model,
|
|
670
|
-
query: ""
|
|
671
|
-
} })
|
|
692
|
+
Expired: ({ generation }) => generation === model.generation ? clearQuery(model) : { model },
|
|
693
|
+
Cleared: () => clearQuery(model)
|
|
672
694
|
})
|
|
673
695
|
});
|
|
674
696
|
const normalize = (text) => text.trimStart().toLocaleLowerCase();
|
|
@@ -701,8 +723,8 @@ const isPrintable = (key, modifiers) => key.length === 1 && !modifiers.ctrlKey &
|
|
|
701
723
|
* With no match the key still extends the query, so the next one can match;
|
|
702
724
|
* a space with an empty query is left to the host (it activates).
|
|
703
725
|
*/
|
|
704
|
-
const behavior$
|
|
705
|
-
const wrap = (char) => declared.wrapper.make(Message$
|
|
726
|
+
const behavior$8 = (declared) => (slots) => (options) => {
|
|
727
|
+
const wrap = (char) => declared.wrapper.make(Message$5.Typed({ char }));
|
|
706
728
|
return Behavior.forSlots(slots)({ [options.host]: Behavior.slot({
|
|
707
729
|
requires: { capability: Capability.Interactive },
|
|
708
730
|
attributes: ({ input, h }) => {
|
|
@@ -710,10 +732,10 @@ const behavior$5 = (declared) => (slots) => (options) => {
|
|
|
710
732
|
const query = input[declared.field].query;
|
|
711
733
|
const currentId = options.current(input);
|
|
712
734
|
const current = currentId === null ? -1 : items.indexOf(currentId);
|
|
713
|
-
const texts = items.ids.map((_, index) => options.text(input, index));
|
|
714
735
|
return [h.OnKeyDownFocus((key, modifiers) => {
|
|
715
736
|
if (!isPrintable(key, modifiers)) return Option.none();
|
|
716
737
|
if (key === " " && query === "") return Option.none();
|
|
738
|
+
const texts = items.ids.map((_, index) => options.text(input, index));
|
|
717
739
|
const found = match(texts, items.enabled, query + key, current);
|
|
718
740
|
const target = found === void 0 ? currentId : items.ids[found];
|
|
719
741
|
return Option.some({
|
|
@@ -735,19 +757,19 @@ const behavior$5 = (declared) => (slots) => (options) => {
|
|
|
735
757
|
* both arrows and typeahead takes this, not `RovingTabindex` plus `Typeahead`.
|
|
736
758
|
*/
|
|
737
759
|
var list_navigation_exports = /* @__PURE__ */ __exportAll({
|
|
738
|
-
Args: () => Args$
|
|
739
|
-
Message: () => Message$
|
|
740
|
-
Model: () => Model$
|
|
741
|
-
behavior: () => behavior$
|
|
742
|
-
bundle: () => bundle$
|
|
760
|
+
Args: () => Args$4,
|
|
761
|
+
Message: () => Message$4,
|
|
762
|
+
Model: () => Model$4,
|
|
763
|
+
behavior: () => behavior$7,
|
|
764
|
+
bundle: () => bundle$4,
|
|
743
765
|
keyOutcome: () => keyOutcome
|
|
744
766
|
});
|
|
745
|
-
const Model$
|
|
767
|
+
const Model$4 = Schema.Struct({
|
|
746
768
|
current: Schema.NullOr(Schema.String),
|
|
747
769
|
query: Schema.String,
|
|
748
770
|
generation: Schema.Number
|
|
749
771
|
});
|
|
750
|
-
const Message$
|
|
772
|
+
const Message$4 = defineMessageUnion({
|
|
751
773
|
/** An item became current: focused, or reached by a key. */
|
|
752
774
|
Focused: { id: Schema.String },
|
|
753
775
|
/** A printable key extended the query; `match` is the item it now picks, if any. */
|
|
@@ -758,7 +780,7 @@ const Message$3 = defineMessageUnion({
|
|
|
758
780
|
Expired: { generation: Schema.Number },
|
|
759
781
|
Cleared: {}
|
|
760
782
|
});
|
|
761
|
-
const Args$
|
|
783
|
+
const Args$4 = Schema.Struct({
|
|
762
784
|
orientation: Orientation,
|
|
763
785
|
loop: Schema.Boolean,
|
|
764
786
|
virtual: Schema.Boolean,
|
|
@@ -767,20 +789,20 @@ const Args$3 = Schema.Struct({
|
|
|
767
789
|
/** How many enabled items PageUp and PageDown move. */
|
|
768
790
|
page: Schema.Number.pipe(Schema.check(Schema.isInt()), Schema.check(Schema.isGreaterThan(0)))
|
|
769
791
|
});
|
|
770
|
-
const bundle$
|
|
771
|
-
Model: Model$
|
|
772
|
-
Message: Message$
|
|
773
|
-
args: Args$
|
|
792
|
+
const bundle$4 = Bundle.make("ListNavigation", {
|
|
793
|
+
Model: Model$4,
|
|
794
|
+
Message: Message$4,
|
|
795
|
+
args: Args$4,
|
|
774
796
|
init: () => ({ model: {
|
|
775
797
|
current: null,
|
|
776
798
|
query: "",
|
|
777
799
|
generation: 0
|
|
778
800
|
} }),
|
|
779
|
-
update: (model, message, args) => Message$
|
|
780
|
-
Focused: ({ id }) =>
|
|
801
|
+
update: (model, message, args) => Message$4.match(message, {
|
|
802
|
+
Focused: ({ id }) => model.current === id ? { model } : { model: {
|
|
781
803
|
...model,
|
|
782
804
|
current: id
|
|
783
|
-
} }
|
|
805
|
+
} },
|
|
784
806
|
Typed: ({ char, match }) => {
|
|
785
807
|
const generation = model.generation + 1;
|
|
786
808
|
return {
|
|
@@ -792,23 +814,17 @@ const bundle$3 = Bundle.make("ListNavigation", {
|
|
|
792
814
|
commands: [{
|
|
793
815
|
name: "ListNavigation.expire",
|
|
794
816
|
args: { generation },
|
|
795
|
-
effect: Effect.as(Effect.sleep(args.timeoutMs), Message$
|
|
817
|
+
effect: Effect.as(Effect.sleep(args.timeoutMs), Message$4.Expired({ generation }))
|
|
796
818
|
}]
|
|
797
819
|
};
|
|
798
820
|
},
|
|
799
|
-
Expired: ({ generation }) => generation === model.generation ? { model
|
|
800
|
-
|
|
801
|
-
query: ""
|
|
802
|
-
} } : { model },
|
|
803
|
-
Cleared: () => ({ model: {
|
|
804
|
-
...model,
|
|
805
|
-
query: ""
|
|
806
|
-
} })
|
|
821
|
+
Expired: ({ generation }) => generation === model.generation ? clearQuery(model) : { model },
|
|
822
|
+
Cleared: () => clearQuery(model)
|
|
807
823
|
})
|
|
808
824
|
});
|
|
809
825
|
const keyOutcome = (items, texts, state, key, modifiers, options) => {
|
|
810
826
|
const from = state.current === null ? -1 : items.indexOf(state.current);
|
|
811
|
-
const moved = move$
|
|
827
|
+
const moved = move$2(items.enabled, from, key, modifiers, options);
|
|
812
828
|
const movedId = moved === void 0 ? void 0 : items.ids[moved];
|
|
813
829
|
if (movedId !== void 0) return {
|
|
814
830
|
_tag: "Navigate",
|
|
@@ -830,11 +846,12 @@ const keyOutcome = (items, texts, state, key, modifiers, options) => {
|
|
|
830
846
|
* `OnFocus` as under `RovingTabindex`. Under `virtual` nothing takes DOM focus
|
|
831
847
|
* and the container carries `aria-activedescendant`.
|
|
832
848
|
*/
|
|
833
|
-
const behavior$
|
|
849
|
+
const behavior$7 = (declared, args) => (slots) => (options) => {
|
|
834
850
|
const wrap = (message) => declared.wrapper.make(message);
|
|
835
|
-
const focused = (id) => wrap(Message$
|
|
851
|
+
const focused = (id) => wrap(Message$4.Focused({ id }));
|
|
836
852
|
const slice = (input) => input[declared.field];
|
|
837
|
-
const
|
|
853
|
+
const itemsOf = perInput(options.items);
|
|
854
|
+
const outcomeMessage = (outcome) => outcome._tag === "Navigate" ? focused(outcome.id) : wrap(Message$4.Typed({
|
|
838
855
|
char: outcome.char,
|
|
839
856
|
match: outcome.match
|
|
840
857
|
}));
|
|
@@ -842,17 +859,16 @@ const behavior$4 = (declared, args) => (slots) => (options) => {
|
|
|
842
859
|
[options.container]: Behavior.slot({
|
|
843
860
|
requires: { capability: Capability.Interactive },
|
|
844
861
|
attributes: ({ input, h }) => {
|
|
845
|
-
const items =
|
|
862
|
+
const items = itemsOf(input);
|
|
846
863
|
const state = slice(input);
|
|
847
|
-
const texts = items.ids.map((_, index) => options.text(input, index));
|
|
848
864
|
const moveOptions = {
|
|
849
865
|
orientation: args.orientation,
|
|
850
866
|
loop: args.loop,
|
|
851
867
|
direction: options.direction?.(input) ?? "ltr",
|
|
852
868
|
page: args.page
|
|
853
869
|
};
|
|
854
|
-
const outcome = (key, modifiers) => Option.fromNullishOr(keyOutcome(items,
|
|
855
|
-
const stop = tabStop(items, state.current);
|
|
870
|
+
const outcome = (key, modifiers) => Option.fromNullishOr(keyOutcome(items, items.ids.map((_, index) => options.text(input, index)), state, key, modifiers, moveOptions));
|
|
871
|
+
const stop = tabStop$1(items, state.current);
|
|
856
872
|
const stopId = stop === -1 ? void 0 : items.ids[stop];
|
|
857
873
|
if (args.virtual) return [h.OnKeyDownPreventDefault((key, modifiers) => Option.map(outcome(key, modifiers), outcomeMessage)), ...stopId === void 0 ? [] : [h.AriaActiveDescendant(stopId)]];
|
|
858
874
|
return [h.OnKeyDownFocus((key, modifiers) => Option.map(outcome(key, modifiers), (result) => {
|
|
@@ -866,7 +882,7 @@ const behavior$4 = (declared, args) => (slots) => (options) => {
|
|
|
866
882
|
}),
|
|
867
883
|
[options.item]: Behavior.slot({
|
|
868
884
|
requires: { capability: Capability.Focusable },
|
|
869
|
-
attributes: ({ input, h, item }) => item === void 0 ? [] : itemAttributes(h,
|
|
885
|
+
attributes: ({ input, h, item }) => item === void 0 ? [] : itemAttributes(h, itemsOf(input), slice(input).current, item, args.virtual, focused)
|
|
870
886
|
})
|
|
871
887
|
}, { name: "ListNavigation" });
|
|
872
888
|
};
|
|
@@ -882,13 +898,13 @@ const behavior$4 = (declared, args) => (slots) => (options) => {
|
|
|
882
898
|
* the placement must say what a press does.
|
|
883
899
|
*/
|
|
884
900
|
var press_exports = /* @__PURE__ */ __exportAll({
|
|
885
|
-
Args: () => Args$
|
|
886
|
-
Message: () => Message$
|
|
887
|
-
Model: () => Model$
|
|
901
|
+
Args: () => Args$3,
|
|
902
|
+
Message: () => Message$3,
|
|
903
|
+
Model: () => Model$3,
|
|
888
904
|
PointerType: () => PointerType,
|
|
889
905
|
Pressed: () => Pressed,
|
|
890
|
-
behavior: () => behavior$
|
|
891
|
-
bundle: () => bundle$
|
|
906
|
+
behavior: () => behavior$6,
|
|
907
|
+
bundle: () => bundle$3,
|
|
892
908
|
events: () => events
|
|
893
909
|
});
|
|
894
910
|
const PointerType = Schema.Literals([
|
|
@@ -898,7 +914,7 @@ const PointerType = Schema.Literals([
|
|
|
898
914
|
"keyboard",
|
|
899
915
|
"virtual"
|
|
900
916
|
]);
|
|
901
|
-
const Model$
|
|
917
|
+
const Model$3 = Schema.Struct({
|
|
902
918
|
/** Down and not yet released or cancelled. For styling. */
|
|
903
919
|
pressed: Schema.Boolean,
|
|
904
920
|
/** The pointer that pressed, so another pointer's up cannot release it. */
|
|
@@ -909,7 +925,7 @@ const Model$2 = Schema.Struct({
|
|
|
909
925
|
suppressing: Schema.NullOr(Schema.Number),
|
|
910
926
|
generation: Schema.Number
|
|
911
927
|
});
|
|
912
|
-
const Message$
|
|
928
|
+
const Message$3 = defineMessageUnion({
|
|
913
929
|
PointerDown: {
|
|
914
930
|
pointerId: Schema.Number,
|
|
915
931
|
button: Schema.Number,
|
|
@@ -941,7 +957,7 @@ const Pressed = Schema.TaggedStruct("Pressed", {
|
|
|
941
957
|
/** Shift was held, for a range selection. */
|
|
942
958
|
shiftKey: Schema.Boolean
|
|
943
959
|
});
|
|
944
|
-
const Args$
|
|
960
|
+
const Args$3 = Schema.Struct({
|
|
945
961
|
/** How long after a pointer press a `click` is taken for its ghost. */
|
|
946
962
|
clickSuppressionMs: Schema.Number.pipe(Schema.check(Schema.isGreaterThanOrEqualTo(0)), Schema.check(Schema.isFinite())) });
|
|
947
963
|
const idle = (model) => ({
|
|
@@ -952,10 +968,10 @@ const idle = (model) => ({
|
|
|
952
968
|
});
|
|
953
969
|
const isActivationKey$1 = (key) => key === "Enter" || key === " ";
|
|
954
970
|
const asPointerType = (raw) => raw === "touch" || raw === "pen" ? raw : "mouse";
|
|
955
|
-
const bundle$
|
|
956
|
-
Model: Model$
|
|
957
|
-
Message: Message$
|
|
958
|
-
args: Args$
|
|
971
|
+
const bundle$3 = Bundle.make("Press", {
|
|
972
|
+
Model: Model$3,
|
|
973
|
+
Message: Message$3,
|
|
974
|
+
args: Args$3,
|
|
959
975
|
init: () => ({ model: {
|
|
960
976
|
pressed: false,
|
|
961
977
|
pointerId: null,
|
|
@@ -963,7 +979,7 @@ const bundle$2 = Bundle.make("Press", {
|
|
|
963
979
|
suppressing: null,
|
|
964
980
|
generation: 0
|
|
965
981
|
} }),
|
|
966
|
-
update: (model, message, args) => Message$
|
|
982
|
+
update: (model, message, args) => Message$3.match(message, {
|
|
967
983
|
PointerDown: ({ pointerId, button }) => button !== 0 || model.pointerId !== null ? { model } : { model: {
|
|
968
984
|
...model,
|
|
969
985
|
pressed: true,
|
|
@@ -981,7 +997,7 @@ const bundle$2 = Bundle.make("Press", {
|
|
|
981
997
|
commands: [{
|
|
982
998
|
name: "Press.unsuppress",
|
|
983
999
|
args: { generation },
|
|
984
|
-
effect: Effect.as(Effect.sleep(args.clickSuppressionMs), Message$
|
|
1000
|
+
effect: Effect.as(Effect.sleep(args.clickSuppressionMs), Message$3.Unsuppressed({ generation }))
|
|
985
1001
|
}],
|
|
986
1002
|
outMessage: Pressed.make({
|
|
987
1003
|
pointerType: asPointerType(pointerType),
|
|
@@ -1030,12 +1046,12 @@ const bundle$2 = Bundle.make("Press", {
|
|
|
1030
1046
|
*/
|
|
1031
1047
|
const events = Mount.defineStream("PressEvents", {
|
|
1032
1048
|
messages: [
|
|
1033
|
-
Message$
|
|
1034
|
-
Message$
|
|
1035
|
-
Message$
|
|
1036
|
-
Message$
|
|
1037
|
-
Message$
|
|
1038
|
-
Message$
|
|
1049
|
+
Message$3.PointerDown,
|
|
1050
|
+
Message$3.PointerUp,
|
|
1051
|
+
Message$3.PointerCancelled,
|
|
1052
|
+
Message$3.KeyDown,
|
|
1053
|
+
Message$3.KeyUp,
|
|
1054
|
+
Message$3.Clicked
|
|
1039
1055
|
],
|
|
1040
1056
|
execute: ({ element }) => Stream.callback((queue) => Effect.acquireRelease(Effect.sync(() => {
|
|
1041
1057
|
const offer = (message) => {
|
|
@@ -1045,7 +1061,7 @@ const events = Mount.defineStream("PressEvents", {
|
|
|
1045
1061
|
const listeners = [
|
|
1046
1062
|
["pointerdown", (event) => {
|
|
1047
1063
|
const pointer = event;
|
|
1048
|
-
offer(Message$
|
|
1064
|
+
offer(Message$3.PointerDown({
|
|
1049
1065
|
pointerId: pointer.pointerId ?? 0,
|
|
1050
1066
|
button: pointer.button ?? 0,
|
|
1051
1067
|
pointerType: pointer.pointerType ?? "mouse"
|
|
@@ -1053,32 +1069,32 @@ const events = Mount.defineStream("PressEvents", {
|
|
|
1053
1069
|
}],
|
|
1054
1070
|
["pointerup", (event) => {
|
|
1055
1071
|
const pointer = event;
|
|
1056
|
-
offer(Message$
|
|
1072
|
+
offer(Message$3.PointerUp({
|
|
1057
1073
|
pointerId: pointer.pointerId ?? 0,
|
|
1058
1074
|
pointerType: pointer.pointerType ?? "mouse",
|
|
1059
1075
|
shiftKey: pointer.shiftKey ?? false
|
|
1060
1076
|
}));
|
|
1061
1077
|
}],
|
|
1062
|
-
["pointerleave", (event) => offer(Message$
|
|
1063
|
-
["pointercancel", (event) => offer(Message$
|
|
1078
|
+
["pointerleave", (event) => offer(Message$3.PointerCancelled({ pointerId: event.pointerId ?? 0 }))],
|
|
1079
|
+
["pointercancel", (event) => offer(Message$3.PointerCancelled({ pointerId: event.pointerId ?? 0 }))],
|
|
1064
1080
|
["keydown", (event) => {
|
|
1065
1081
|
const key = event;
|
|
1066
1082
|
if (isActivationKey$1(key.key)) key.preventDefault();
|
|
1067
|
-
offer(Message$
|
|
1083
|
+
offer(Message$3.KeyDown({
|
|
1068
1084
|
key: key.key,
|
|
1069
1085
|
repeat: key.repeat
|
|
1070
1086
|
}));
|
|
1071
1087
|
}],
|
|
1072
1088
|
["keyup", (event) => {
|
|
1073
1089
|
const key = event;
|
|
1074
|
-
offer(Message$
|
|
1090
|
+
offer(Message$3.KeyUp({
|
|
1075
1091
|
key: key.key,
|
|
1076
1092
|
shiftKey: key.shiftKey
|
|
1077
1093
|
}));
|
|
1078
1094
|
}],
|
|
1079
1095
|
["click", (event) => {
|
|
1080
1096
|
const click = event;
|
|
1081
|
-
offer(Message$
|
|
1097
|
+
offer(Message$3.Clicked({
|
|
1082
1098
|
detail: click.detail ?? 0,
|
|
1083
1099
|
shiftKey: click.shiftKey ?? false
|
|
1084
1100
|
}));
|
|
@@ -1096,7 +1112,7 @@ const events = Mount.defineStream("PressEvents", {
|
|
|
1096
1112
|
* decides nothing; a click, a touch, Enter, Space, or an assistive
|
|
1097
1113
|
* technology's click all reach `update` and come out as one `Pressed`.
|
|
1098
1114
|
*/
|
|
1099
|
-
const behavior$
|
|
1115
|
+
const behavior$6 = (declared) => (slots) => (options) => Behavior.forSlots(slots)({ [options.target]: Behavior.slot({
|
|
1100
1116
|
requires: { capability: Capability.Interactive },
|
|
1101
1117
|
attributes: ({ input, h }) => [...input[declared.field].pressed ? [h.DataAttribute("pressed", "true")] : [], ...options.disabled?.(input) === true ? [h.AriaDisabled(true)] : []],
|
|
1102
1118
|
mount: () => Mount.mapMessage(events(), (message) => declared.wrapper.make(message))
|
|
@@ -1111,21 +1127,21 @@ const behavior$3 = (declared) => (slots) => (options) => Behavior.forSlots(slots
|
|
|
1111
1127
|
* handle. A short press is `Press`'s business, not this Bundle's.
|
|
1112
1128
|
*/
|
|
1113
1129
|
var long_press_exports = /* @__PURE__ */ __exportAll({
|
|
1114
|
-
Args: () => Args$
|
|
1130
|
+
Args: () => Args$2,
|
|
1115
1131
|
LongPressed: () => LongPressed,
|
|
1116
|
-
Message: () => Message$
|
|
1117
|
-
Model: () => Model$
|
|
1118
|
-
behavior: () => behavior$
|
|
1119
|
-
bundle: () => bundle$
|
|
1132
|
+
Message: () => Message$2,
|
|
1133
|
+
Model: () => Model$2,
|
|
1134
|
+
behavior: () => behavior$5,
|
|
1135
|
+
bundle: () => bundle$2
|
|
1120
1136
|
});
|
|
1121
|
-
const Model$
|
|
1137
|
+
const Model$2 = Schema.Struct({
|
|
1122
1138
|
/** Down, and the threshold has not passed. */
|
|
1123
1139
|
holding: Schema.Boolean,
|
|
1124
1140
|
pointerId: Schema.NullOr(Schema.Number),
|
|
1125
1141
|
key: Schema.NullOr(Schema.String),
|
|
1126
1142
|
generation: Schema.Number
|
|
1127
1143
|
});
|
|
1128
|
-
const Message$
|
|
1144
|
+
const Message$2 = defineMessageUnion({
|
|
1129
1145
|
PointerDown: {
|
|
1130
1146
|
pointerId: Schema.Number,
|
|
1131
1147
|
button: Schema.Number,
|
|
@@ -1155,7 +1171,7 @@ const Message$1 = defineMessageUnion({
|
|
|
1155
1171
|
}
|
|
1156
1172
|
});
|
|
1157
1173
|
const LongPressed = Schema.TaggedStruct("LongPressed", { pointerType: PointerType });
|
|
1158
|
-
const Args$
|
|
1174
|
+
const Args$2 = Schema.Struct({ thresholdMs: Schema.Number.pipe(Schema.check(Schema.isGreaterThan(0)), Schema.check(Schema.isFinite())) });
|
|
1159
1175
|
const isActivationKey = (key) => key === "Enter" || key === " ";
|
|
1160
1176
|
const released = (model) => ({
|
|
1161
1177
|
...model,
|
|
@@ -1175,24 +1191,24 @@ const start = (model, args, by, pointerType) => {
|
|
|
1175
1191
|
commands: [{
|
|
1176
1192
|
name: "LongPress.elapse",
|
|
1177
1193
|
args: { generation },
|
|
1178
|
-
effect: Effect.as(Effect.sleep(args.thresholdMs), Message$
|
|
1194
|
+
effect: Effect.as(Effect.sleep(args.thresholdMs), Message$2.Elapsed({
|
|
1179
1195
|
generation,
|
|
1180
1196
|
pointerType
|
|
1181
1197
|
}))
|
|
1182
1198
|
}]
|
|
1183
1199
|
};
|
|
1184
1200
|
};
|
|
1185
|
-
const bundle$
|
|
1186
|
-
Model: Model$
|
|
1187
|
-
Message: Message$
|
|
1188
|
-
args: Args$
|
|
1201
|
+
const bundle$2 = Bundle.make("LongPress", {
|
|
1202
|
+
Model: Model$2,
|
|
1203
|
+
Message: Message$2,
|
|
1204
|
+
args: Args$2,
|
|
1189
1205
|
init: () => ({ model: {
|
|
1190
1206
|
holding: false,
|
|
1191
1207
|
pointerId: null,
|
|
1192
1208
|
key: null,
|
|
1193
1209
|
generation: 0
|
|
1194
1210
|
} }),
|
|
1195
|
-
update: (model, message, args) => Message$
|
|
1211
|
+
update: (model, message, args) => Message$2.match(message, {
|
|
1196
1212
|
PointerDown: ({ pointerId, button, pointerType }) => button !== 0 || model.holding ? { model } : start(model, args, { pointerId }, pointerType === "touch" || pointerType === "pen" ? pointerType : "mouse"),
|
|
1197
1213
|
PointerUp: ({ pointerId }) => model.pointerId === pointerId ? { model: released(model) } : { model },
|
|
1198
1214
|
PointerCancelled: ({ pointerId }) => model.pointerId === pointerId ? { model: released(model) } : { model },
|
|
@@ -1211,7 +1227,7 @@ const bundle$1 = Bundle.make("LongPress", {
|
|
|
1211
1227
|
* `LongPress` on one slot are refused by the resolver (two Mounts of one
|
|
1212
1228
|
* name), so today a slot takes one of them.
|
|
1213
1229
|
*/
|
|
1214
|
-
const behavior$
|
|
1230
|
+
const behavior$5 = (declared) => (slots) => (options) => Behavior.forSlots(slots)({ [options.target]: Behavior.slot({
|
|
1215
1231
|
requires: { capability: Capability.Interactive },
|
|
1216
1232
|
attributes: ({ input, h }) => input[declared.field].holding ? [h.DataAttribute("holding", "true")] : [],
|
|
1217
1233
|
mount: () => Mount.mapMessage(events(), (fact) => declared.wrapper.make(fact))
|
|
@@ -1228,10 +1244,10 @@ var move_exports = /* @__PURE__ */ __exportAll({
|
|
|
1228
1244
|
MoveEnded: () => MoveEnded,
|
|
1229
1245
|
MoveStarted: () => MoveStarted,
|
|
1230
1246
|
Moved: () => Moved,
|
|
1231
|
-
behavior: () => behavior$
|
|
1247
|
+
behavior: () => behavior$4,
|
|
1232
1248
|
mount: () => Move
|
|
1233
1249
|
});
|
|
1234
|
-
const behavior$
|
|
1250
|
+
const behavior$4 = (slots) => (options) => Behavior.forSlots(slots)({ [options.handle]: Behavior.slot({
|
|
1235
1251
|
requires: { capability: Capability.Draggable },
|
|
1236
1252
|
mount: () => Mount.mapMessage(Move(), options.toMessage)
|
|
1237
1253
|
}) }, { name: "Move" });
|
|
@@ -1247,26 +1263,26 @@ const behavior$1 = (slots) => (options) => Behavior.forSlots(slots)({ [options.h
|
|
|
1247
1263
|
* `Pressed` carries `shiftKey`.
|
|
1248
1264
|
*/
|
|
1249
1265
|
var selection_exports = /* @__PURE__ */ __exportAll({
|
|
1250
|
-
Args: () => Args,
|
|
1251
|
-
Message: () => Message,
|
|
1266
|
+
Args: () => Args$1,
|
|
1267
|
+
Message: () => Message$1,
|
|
1252
1268
|
Mode: () => Mode,
|
|
1253
|
-
Model: () => Model,
|
|
1254
|
-
behavior: () => behavior,
|
|
1269
|
+
Model: () => Model$1,
|
|
1270
|
+
behavior: () => behavior$3,
|
|
1255
1271
|
between: () => between,
|
|
1256
|
-
bundle: () => bundle
|
|
1272
|
+
bundle: () => bundle$1
|
|
1257
1273
|
});
|
|
1258
1274
|
const Mode = Schema.Literals([
|
|
1259
1275
|
"single",
|
|
1260
1276
|
"multiple",
|
|
1261
1277
|
"none"
|
|
1262
1278
|
]);
|
|
1263
|
-
const Model = Schema.Struct({
|
|
1279
|
+
const Model$1 = Schema.Struct({
|
|
1264
1280
|
/** In selection order. */
|
|
1265
1281
|
selected: Schema.Array(Schema.String),
|
|
1266
1282
|
/** Where the last plain selection happened; a range extends from it. */
|
|
1267
1283
|
anchor: Schema.NullOr(Schema.String)
|
|
1268
1284
|
});
|
|
1269
|
-
const Message = defineMessageUnion({
|
|
1285
|
+
const Message$1 = defineMessageUnion({
|
|
1270
1286
|
/** A plain activation: replaces in single mode, toggles in multiple mode. */
|
|
1271
1287
|
Activated: { id: Schema.String },
|
|
1272
1288
|
/** A Shift activation: selects everything between the anchor and `id` in `order`. */
|
|
@@ -1277,7 +1293,7 @@ const Message = defineMessageUnion({
|
|
|
1277
1293
|
Replaced: { ids: Schema.Array(Schema.String) },
|
|
1278
1294
|
Cleared: {}
|
|
1279
1295
|
});
|
|
1280
|
-
const Args = Schema.Struct({
|
|
1296
|
+
const Args$1 = Schema.Struct({
|
|
1281
1297
|
mode: Mode,
|
|
1282
1298
|
/** In single mode, whether activating the selected item deselects it. */
|
|
1283
1299
|
allowEmpty: Schema.Boolean
|
|
@@ -1291,15 +1307,15 @@ const between = (order, from, to) => {
|
|
|
1291
1307
|
if (start === -1) return [to];
|
|
1292
1308
|
return order.slice(Math.min(start, end), Math.max(start, end) + 1);
|
|
1293
1309
|
};
|
|
1294
|
-
const bundle = Bundle.make("Selection", {
|
|
1295
|
-
Model,
|
|
1296
|
-
Message,
|
|
1297
|
-
args: Args,
|
|
1310
|
+
const bundle$1 = Bundle.make("Selection", {
|
|
1311
|
+
Model: Model$1,
|
|
1312
|
+
Message: Message$1,
|
|
1313
|
+
args: Args$1,
|
|
1298
1314
|
init: () => ({ model: {
|
|
1299
1315
|
selected: [],
|
|
1300
1316
|
anchor: null
|
|
1301
1317
|
} }),
|
|
1302
|
-
update: (model, message, args) => Message.match(message, {
|
|
1318
|
+
update: (model, message, args) => Message$1.match(message, {
|
|
1303
1319
|
Activated: ({ id }) => {
|
|
1304
1320
|
if (args.mode === "none") return { model };
|
|
1305
1321
|
const isSelected = model.selected.includes(id);
|
|
@@ -1318,19 +1334,25 @@ const bundle = Bundle.make("Selection", {
|
|
|
1318
1334
|
Ranged: ({ id, order }) => {
|
|
1319
1335
|
if (args.mode !== "multiple") return { model };
|
|
1320
1336
|
const range = between(order, model.anchor, id);
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1337
|
+
const inRange = new Set(range);
|
|
1338
|
+
const selected = [...model.selected.filter((other) => !inRange.has(other)), ...range];
|
|
1339
|
+
const anchor = model.anchor ?? id;
|
|
1340
|
+
return sameIds(selected, model.selected) && anchor === model.anchor ? { model } : { model: {
|
|
1341
|
+
selected,
|
|
1342
|
+
anchor
|
|
1324
1343
|
} };
|
|
1325
1344
|
},
|
|
1326
|
-
Replaced: ({ ids }) =>
|
|
1327
|
-
selected
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1345
|
+
Replaced: ({ ids }) => {
|
|
1346
|
+
const selected = args.mode === "none" ? [] : [...new Set(ids)];
|
|
1347
|
+
return sameIds(selected, model.selected) ? { model } : { model: {
|
|
1348
|
+
selected,
|
|
1349
|
+
anchor: model.anchor
|
|
1350
|
+
} };
|
|
1351
|
+
},
|
|
1352
|
+
Cleared: () => model.selected.length === 0 ? { model } : { model: {
|
|
1331
1353
|
selected: [],
|
|
1332
1354
|
anchor: model.anchor
|
|
1333
|
-
} }
|
|
1355
|
+
} }
|
|
1334
1356
|
})
|
|
1335
1357
|
});
|
|
1336
1358
|
/**
|
|
@@ -1338,8 +1360,10 @@ const bundle = Bundle.make("Selection", {
|
|
|
1338
1360
|
* `aria-multiselectable` on the container in multiple mode; wires `OnClick`
|
|
1339
1361
|
* to `Activated` unless told not to. A disabled item gets no click.
|
|
1340
1362
|
*/
|
|
1341
|
-
const behavior = (declared, args) => (slots) => (options) => {
|
|
1363
|
+
const behavior$3 = (declared, args) => (slots) => (options) => {
|
|
1342
1364
|
const wrap = (message) => declared.wrapper.make(message);
|
|
1365
|
+
const itemsOf = perInput(options.items);
|
|
1366
|
+
const selectedOf = perInput((input) => new Set(input[declared.field].selected));
|
|
1343
1367
|
return Behavior.forSlots(slots)({
|
|
1344
1368
|
...options.container === void 0 ? {} : { [options.container]: Behavior.slot({
|
|
1345
1369
|
requires: { capability: Capability.Container },
|
|
@@ -1349,16 +1373,275 @@ const behavior = (declared, args) => (slots) => (options) => {
|
|
|
1349
1373
|
requires: { capability: Capability.Interactive },
|
|
1350
1374
|
attributes: ({ input, h, item }) => {
|
|
1351
1375
|
if (item === void 0) return [];
|
|
1352
|
-
const items =
|
|
1376
|
+
const items = itemsOf(input);
|
|
1353
1377
|
const id = item.id ?? items.ids[item.index];
|
|
1354
1378
|
if (id === void 0) return [];
|
|
1355
1379
|
const clickable = options.click !== false && args.mode !== "none" && !items.isDisabled(item.index);
|
|
1356
|
-
return [h.AriaSelected(input
|
|
1380
|
+
return [h.AriaSelected(selectedOf(input).has(id)), ...clickable ? [h.OnClick(wrap(Message$1.Activated({ id })))] : []];
|
|
1357
1381
|
}
|
|
1358
1382
|
})
|
|
1359
1383
|
}, { name: "Selection" });
|
|
1360
1384
|
};
|
|
1361
1385
|
//#endregion
|
|
1362
|
-
|
|
1386
|
+
//#region src/interaction/targets.ts
|
|
1387
|
+
/**
|
|
1388
|
+
* `Targets` as a Behavior: attaches the `foldkit-primitives/dom` Mount to a
|
|
1389
|
+
* container slot and maps what it reports into the view's Messages. No Bundle:
|
|
1390
|
+
* which descendant is under the pointer, or was pressed, is already a fact, and
|
|
1391
|
+
* what it means is the parent's `update`.
|
|
1392
|
+
*/
|
|
1393
|
+
var targets_exports = /* @__PURE__ */ __exportAll({
|
|
1394
|
+
TargetHovered: () => TargetHovered,
|
|
1395
|
+
TargetPressed: () => TargetPressed,
|
|
1396
|
+
behavior: () => behavior$2,
|
|
1397
|
+
mount: () => Targets,
|
|
1398
|
+
targetOf: () => targetOf
|
|
1399
|
+
});
|
|
1400
|
+
const behavior$2 = (slots) => (options) => Behavior.forSlots(slots)({ [options.container]: Behavior.slot({
|
|
1401
|
+
requires: { capability: Capability.Container },
|
|
1402
|
+
mount: () => Mount.mapMessage(Targets({
|
|
1403
|
+
attribute: options.attribute,
|
|
1404
|
+
preventDefault: options.preventDefault ?? false
|
|
1405
|
+
}), options.toMessage)
|
|
1406
|
+
}) }, { name: "Targets" });
|
|
1407
|
+
//#endregion
|
|
1408
|
+
//#region src/interaction/pointer-drag.ts
|
|
1409
|
+
/**
|
|
1410
|
+
* `PointerDrag` as a Behavior: attaches the `foldkit-primitives/dom` Mount to
|
|
1411
|
+
* a container slot and maps what it reports into the view's Messages. No
|
|
1412
|
+
* Bundle: what is dragged and where the pointer is are facts, and what a drop
|
|
1413
|
+
* means, and whether it is allowed, is the parent's `update`.
|
|
1414
|
+
*/
|
|
1415
|
+
var pointer_drag_exports = /* @__PURE__ */ __exportAll({
|
|
1416
|
+
DRAG_THRESHOLD: () => 4,
|
|
1417
|
+
DragCancelled: () => DragCancelled,
|
|
1418
|
+
DragDropped: () => DragDropped,
|
|
1419
|
+
DragPlace: () => DragPlace,
|
|
1420
|
+
DragStarted: () => DragStarted,
|
|
1421
|
+
DragZone: () => DragZone,
|
|
1422
|
+
DraggedOver: () => DraggedOver,
|
|
1423
|
+
behavior: () => behavior$1,
|
|
1424
|
+
boxOf: () => boxOf,
|
|
1425
|
+
mount: () => PointerDrag,
|
|
1426
|
+
zoneOf: () => zoneOf
|
|
1427
|
+
});
|
|
1428
|
+
const behavior$1 = (slots) => (options) => Behavior.forSlots(slots)({ [options.container]: Behavior.slot({
|
|
1429
|
+
requires: { capability: Capability.Container },
|
|
1430
|
+
mount: () => Mount.mapMessage(PointerDrag(options.targets === void 0 ? { attribute: options.attribute } : {
|
|
1431
|
+
attribute: options.attribute,
|
|
1432
|
+
targets: options.targets
|
|
1433
|
+
}), options.toMessage)
|
|
1434
|
+
}) }, { name: "PointerDrag" });
|
|
1435
|
+
//#endregion
|
|
1436
|
+
//#region src/interaction/tree-navigation.ts
|
|
1437
|
+
/**
|
|
1438
|
+
* Keyboard navigation of a tree (a file explorer, a page's layers, a nested
|
|
1439
|
+
* menu), after the WAI-ARIA tree pattern: one tab stop, Up and Down through the
|
|
1440
|
+
* rows that are showing, Right to open a row or step into it, Left to close it
|
|
1441
|
+
* or step out to its parent, Home and End to the first and last row.
|
|
1442
|
+
*
|
|
1443
|
+
* The Model slice is the current row's id and which rows are open. Openness is
|
|
1444
|
+
* stored as the rows `toggled` away from a default, so a layers panel that
|
|
1445
|
+
* starts with everything open and a file tree that starts closed are the same
|
|
1446
|
+
* bundle with a different `openByDefault`, and neither stores every row.
|
|
1447
|
+
*
|
|
1448
|
+
* Which rows exist is the view's to say, each with its parent, in tree order.
|
|
1449
|
+
* What a key does is a pure function of those rows and the Model; the Behavior
|
|
1450
|
+
* wires it to the slots and writes the tree's ARIA attributes.
|
|
1451
|
+
*/
|
|
1452
|
+
var tree_navigation_exports = /* @__PURE__ */ __exportAll({
|
|
1453
|
+
Args: () => Args,
|
|
1454
|
+
Message: () => Message,
|
|
1455
|
+
Model: () => Model,
|
|
1456
|
+
behavior: () => behavior,
|
|
1457
|
+
bundle: () => bundle,
|
|
1458
|
+
isOpen: () => isOpen,
|
|
1459
|
+
move: () => move,
|
|
1460
|
+
shown: () => shown,
|
|
1461
|
+
tabStop: () => tabStop
|
|
1462
|
+
});
|
|
1463
|
+
const Model = Schema.Struct({
|
|
1464
|
+
/** The current row's id, or `null` before any row has been focused. */
|
|
1465
|
+
current: Schema.NullOr(Schema.String),
|
|
1466
|
+
/** The rows whose open state differs from `openByDefault`. */
|
|
1467
|
+
toggled: Schema.Array(Schema.String)
|
|
1468
|
+
});
|
|
1469
|
+
const Message = defineMessageUnion({
|
|
1470
|
+
/** A row became current: the user focused it, or a key moved there. */
|
|
1471
|
+
Focused: { id: Schema.String },
|
|
1472
|
+
Opened: { id: Schema.String },
|
|
1473
|
+
Closed: { id: Schema.String }
|
|
1474
|
+
});
|
|
1475
|
+
const Args = Schema.Struct({
|
|
1476
|
+
/** Whether a row with children starts open. */
|
|
1477
|
+
openByDefault: Schema.Boolean });
|
|
1478
|
+
/** Whether a row is open, given the default and what was toggled. */
|
|
1479
|
+
const isOpen = (model, args, id) => args.openByDefault !== model.toggled.includes(id);
|
|
1480
|
+
const setOpen = (model, args, id, open) => isOpen(model, args, id) === open ? model : {
|
|
1481
|
+
...model,
|
|
1482
|
+
toggled: model.toggled.includes(id) ? model.toggled.filter((toggled) => toggled !== id) : [...model.toggled, id]
|
|
1483
|
+
};
|
|
1484
|
+
const bundle = Bundle.make("TreeNavigation", {
|
|
1485
|
+
Model,
|
|
1486
|
+
Message,
|
|
1487
|
+
args: Args,
|
|
1488
|
+
init: () => ({ model: {
|
|
1489
|
+
current: null,
|
|
1490
|
+
toggled: []
|
|
1491
|
+
} }),
|
|
1492
|
+
update: (model, message, args) => {
|
|
1493
|
+
switch (message._tag) {
|
|
1494
|
+
case "Focused": return model.current === message.id ? { model } : { model: {
|
|
1495
|
+
...model,
|
|
1496
|
+
current: message.id
|
|
1497
|
+
} };
|
|
1498
|
+
case "Opened": return { model: setOpen(model, args, message.id, true) };
|
|
1499
|
+
case "Closed": return { model: setOpen(model, args, message.id, false) };
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
});
|
|
1503
|
+
/**
|
|
1504
|
+
* The rows that are showing: every row whose ancestors are all open, with its
|
|
1505
|
+
* level and its place among its siblings. A row whose parent is not a row is
|
|
1506
|
+
* treated as top-level.
|
|
1507
|
+
*/
|
|
1508
|
+
const shown = (rows, model, args) => {
|
|
1509
|
+
const byId = new Map(rows.map((row) => [row.id, row]));
|
|
1510
|
+
const siblingsOf = /* @__PURE__ */ new Map();
|
|
1511
|
+
const positionOf = /* @__PURE__ */ new Map();
|
|
1512
|
+
for (const row of rows) {
|
|
1513
|
+
const parent = row.parent !== null && byId.has(row.parent) ? row.parent : null;
|
|
1514
|
+
const count = (siblingsOf.get(parent) ?? 0) + 1;
|
|
1515
|
+
siblingsOf.set(parent, count);
|
|
1516
|
+
positionOf.set(row.id, count);
|
|
1517
|
+
}
|
|
1518
|
+
const levels = /* @__PURE__ */ new Map();
|
|
1519
|
+
const showing = /* @__PURE__ */ new Map();
|
|
1520
|
+
const result = [];
|
|
1521
|
+
for (const row of rows) {
|
|
1522
|
+
const parent = row.parent !== null && byId.has(row.parent) ? row.parent : null;
|
|
1523
|
+
const level = parent === null ? 1 : (levels.get(parent) ?? 0) + 1;
|
|
1524
|
+
levels.set(row.id, level);
|
|
1525
|
+
const visible = parent === null || showing.get(parent) === true && isOpen(model, args, parent);
|
|
1526
|
+
showing.set(row.id, visible);
|
|
1527
|
+
if (!visible) continue;
|
|
1528
|
+
result.push({
|
|
1529
|
+
...row,
|
|
1530
|
+
level,
|
|
1531
|
+
position: positionOf.get(row.id) ?? 1,
|
|
1532
|
+
siblings: siblingsOf.get(parent) ?? 1
|
|
1533
|
+
});
|
|
1534
|
+
}
|
|
1535
|
+
return result;
|
|
1536
|
+
};
|
|
1537
|
+
const isPlain = (modifiers) => !modifiers.ctrlKey && !modifiers.altKey && !modifiers.metaKey;
|
|
1538
|
+
/**
|
|
1539
|
+
* What a key does from the current row, over the rows that are showing, or
|
|
1540
|
+
* `undefined` when it is not a key the tree handles. Disabled rows are stepped
|
|
1541
|
+
* over. With nothing current, Down and Home land on the first row, Up and End
|
|
1542
|
+
* on the last. Right and Left swap under `rtl`.
|
|
1543
|
+
*/
|
|
1544
|
+
const move = (rows, current, key, modifiers, model, args, direction = "ltr") => {
|
|
1545
|
+
const enabled = rows.filter((row) => row.disabled !== true);
|
|
1546
|
+
if (enabled.length === 0 || !isPlain(modifiers)) return void 0;
|
|
1547
|
+
const focus = (row) => row === void 0 ? void 0 : {
|
|
1548
|
+
_tag: "Focus",
|
|
1549
|
+
id: row.id
|
|
1550
|
+
};
|
|
1551
|
+
const at = enabled.findIndex((row) => row.id === current);
|
|
1552
|
+
const inward = direction === "rtl" ? "ArrowLeft" : "ArrowRight";
|
|
1553
|
+
const outward = direction === "rtl" ? "ArrowRight" : "ArrowLeft";
|
|
1554
|
+
if (key === "Home") return focus(enabled[0]);
|
|
1555
|
+
if (key === "End") return focus(enabled[enabled.length - 1]);
|
|
1556
|
+
if (key === "ArrowDown") return focus(at === -1 ? enabled[0] : enabled[Math.min(at + 1, enabled.length - 1)]);
|
|
1557
|
+
if (key === "ArrowUp") return focus(at === -1 ? enabled[enabled.length - 1] : enabled[Math.max(at - 1, 0)]);
|
|
1558
|
+
const row = enabled[at];
|
|
1559
|
+
if (row === void 0) return key === inward || key === outward ? focus(enabled[0]) : void 0;
|
|
1560
|
+
if (key === inward) {
|
|
1561
|
+
if (!row.branch) return void 0;
|
|
1562
|
+
if (!isOpen(model, args, row.id)) return {
|
|
1563
|
+
_tag: "Open",
|
|
1564
|
+
id: row.id
|
|
1565
|
+
};
|
|
1566
|
+
return focus(enabled.find((child) => child.parent === row.id));
|
|
1567
|
+
}
|
|
1568
|
+
if (key === outward) {
|
|
1569
|
+
if (row.branch && isOpen(model, args, row.id)) return {
|
|
1570
|
+
_tag: "Close",
|
|
1571
|
+
id: row.id
|
|
1572
|
+
};
|
|
1573
|
+
return focus(enabled.find((parent) => parent.id === row.parent));
|
|
1574
|
+
}
|
|
1575
|
+
};
|
|
1576
|
+
/** The row that holds the tab stop: the current one while it is showing, else the first. */
|
|
1577
|
+
const tabStop = (rows, current) => {
|
|
1578
|
+
return (rows.find((row) => row.id === current && row.disabled !== true) ?? rows.find((row) => row.disabled !== true))?.id;
|
|
1579
|
+
};
|
|
1580
|
+
/**
|
|
1581
|
+
* Wires a placed `TreeNavigation` to the slots. The container's keys move focus
|
|
1582
|
+
* to the row they pick, or open or close the current row with focus left where
|
|
1583
|
+
* it is. Each showing row gets its id, `role="treeitem"`, `aria-level` (and
|
|
1584
|
+
* the same level as `--fk-tree-level`, so one `calc` indents any depth),
|
|
1585
|
+
* `aria-posinset`, `aria-setsize`, `aria-expanded` when it has children,
|
|
1586
|
+
* `aria-disabled` when disabled, a roving `tabindex`, and `OnFocus` reporting
|
|
1587
|
+
* it current. Every handled key is default-prevented.
|
|
1588
|
+
*/
|
|
1589
|
+
const behavior = (declared, args) => (slots) => (options) => {
|
|
1590
|
+
const wrap = (message) => declared.wrapper.make(message);
|
|
1591
|
+
const slice = (input) => input[declared.field];
|
|
1592
|
+
const domId = options.domId ?? ((id) => id);
|
|
1593
|
+
const prepare = perInput((input) => {
|
|
1594
|
+
const model = slice(input);
|
|
1595
|
+
const rows = shown(options.rows(input), model, args);
|
|
1596
|
+
return {
|
|
1597
|
+
model,
|
|
1598
|
+
rows,
|
|
1599
|
+
byId: new Map(rows.map((row) => [row.id, row])),
|
|
1600
|
+
stop: tabStop(rows, model.current)
|
|
1601
|
+
};
|
|
1602
|
+
});
|
|
1603
|
+
return Behavior.forSlots(slots)({
|
|
1604
|
+
[options.container]: Behavior.slot({
|
|
1605
|
+
requires: { capability: Capability.Interactive },
|
|
1606
|
+
mount: () => FollowTabStop(),
|
|
1607
|
+
attributes: ({ input, h }) => {
|
|
1608
|
+
const { model, rows } = prepare(input);
|
|
1609
|
+
const direction = options.direction?.(input) ?? "ltr";
|
|
1610
|
+
return [h.OnKeyDownFocus((key, modifiers) => {
|
|
1611
|
+
const action = move(rows, model.current, key, modifiers, model, args, direction);
|
|
1612
|
+
if (action === void 0) return Option.none();
|
|
1613
|
+
const message = action._tag === "Focus" ? Message.Focused({ id: action.id }) : action._tag === "Open" ? Message.Opened({ id: action.id }) : Message.Closed({ id: action.id });
|
|
1614
|
+
return Option.some({
|
|
1615
|
+
focusSelector: idSelector(domId(action.id)),
|
|
1616
|
+
message: wrap(message)
|
|
1617
|
+
});
|
|
1618
|
+
})];
|
|
1619
|
+
}
|
|
1620
|
+
}),
|
|
1621
|
+
[options.item]: Behavior.slot({
|
|
1622
|
+
requires: { capability: Capability.Focusable },
|
|
1623
|
+
attributes: ({ input, h, item }) => {
|
|
1624
|
+
if (item === void 0) return [];
|
|
1625
|
+
const { model, rows, byId, stop } = prepare(input);
|
|
1626
|
+
const row = item.id === void 0 ? rows[item.index] : byId.get(item.id);
|
|
1627
|
+
if (row === void 0) return [];
|
|
1628
|
+
return [
|
|
1629
|
+
h.Id(domId(row.id)),
|
|
1630
|
+
h.Role("treeitem"),
|
|
1631
|
+
h.AriaLevel(row.level),
|
|
1632
|
+
h.Style({ "--fk-tree-level": String(row.level) }),
|
|
1633
|
+
h.AriaPosinset(row.position),
|
|
1634
|
+
h.AriaSetsize(row.siblings),
|
|
1635
|
+
...row.branch ? [h.AriaExpanded(isOpen(model, args, row.id))] : [],
|
|
1636
|
+
...row.disabled === true ? [h.AriaDisabled(true)] : [],
|
|
1637
|
+
h.Tabindex(stop === row.id ? 0 : -1),
|
|
1638
|
+
h.OnFocus(wrap(Message.Focused({ id: row.id })))
|
|
1639
|
+
];
|
|
1640
|
+
}
|
|
1641
|
+
})
|
|
1642
|
+
}, { name: "TreeNavigation" });
|
|
1643
|
+
};
|
|
1644
|
+
//#endregion
|
|
1645
|
+
export { dismiss_layer_exports as DismissLayer, editable_text_exports as EditableText, focus_scope_exports as FocusScope, focus_visible_exports as FocusVisible, grid_navigation_exports as GridNavigation, layers_exports as Layers, list_navigation_exports as ListNavigation, live_announce_exports as LiveAnnounce, long_press_exports as LongPress, move_exports as Move, pointer_drag_exports as PointerDrag, press_exports as Press, roving_tabindex_exports as RovingTabindex, selection_exports as Selection, targets_exports as Targets, tree_navigation_exports as TreeNavigation, typeahead_exports as Typeahead };
|
|
1363
1646
|
|
|
1364
1647
|
//# sourceMappingURL=index.mjs.map
|