foldkit-primitives 0.3.0 → 0.4.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.
Files changed (51) hide show
  1. package/README.md +202 -13
  2. package/dist/device/index.d.mts.map +1 -1
  3. package/dist/device/index.mjs +95 -87
  4. package/dist/device/index.mjs.map +1 -1
  5. package/dist/dom/index.d.mts +71 -3
  6. package/dist/dom/index.d.mts.map +1 -1
  7. package/dist/dom/index.mjs +303 -6
  8. package/dist/dom/index.mjs.map +1 -1
  9. package/dist/editable-text-CKkS_mWt.d.mts +235 -0
  10. package/dist/editable-text-CKkS_mWt.d.mts.map +1 -0
  11. package/dist/events/index.d.mts +8 -1
  12. package/dist/events/index.d.mts.map +1 -1
  13. package/dist/events/index.mjs +32 -17
  14. package/dist/events/index.mjs.map +1 -1
  15. package/dist/follow-tab-stop-DpxwLXvU.mjs +800 -0
  16. package/dist/follow-tab-stop-DpxwLXvU.mjs.map +1 -0
  17. package/dist/index.d.mts +6 -6
  18. package/dist/index.mjs +7 -7
  19. package/dist/interaction/index.d.mts +247 -92
  20. package/dist/interaction/index.d.mts.map +1 -1
  21. package/dist/interaction/index.mjs +507 -224
  22. package/dist/interaction/index.mjs.map +1 -1
  23. package/dist/internal-DP-xLKbT.mjs +37 -0
  24. package/dist/internal-DP-xLKbT.mjs.map +1 -0
  25. package/dist/media/index.d.mts.map +1 -1
  26. package/dist/media/index.mjs +3 -3
  27. package/dist/media/index.mjs.map +1 -1
  28. package/dist/motion/index.mjs +1 -1
  29. package/dist/{motion-LfJYAMnP.mjs → motion-DGGBZ0wt.mjs} +14 -13
  30. package/dist/motion-DGGBZ0wt.mjs.map +1 -0
  31. package/dist/net/index.d.mts +69 -5
  32. package/dist/net/index.d.mts.map +1 -1
  33. package/dist/net/index.mjs +139 -56
  34. package/dist/net/index.mjs.map +1 -1
  35. package/dist/observers/index.d.mts +1 -1
  36. package/dist/observers/index.mjs +3 -0
  37. package/dist/observers/index.mjs.map +1 -1
  38. package/dist/state/index.d.mts +57 -15
  39. package/dist/state/index.d.mts.map +1 -1
  40. package/dist/state/index.mjs +198 -90
  41. package/dist/state/index.mjs.map +1 -1
  42. package/dist/time/index.d.mts +21 -1
  43. package/dist/time/index.d.mts.map +1 -1
  44. package/dist/time/index.mjs +48 -19
  45. package/dist/time/index.mjs.map +1 -1
  46. package/package.json +16 -5
  47. package/dist/motion-LfJYAMnP.mjs.map +0 -1
  48. package/dist/move-CZwHyTGk.mjs +0 -215
  49. package/dist/move-CZwHyTGk.mjs.map +0 -1
  50. package/dist/move-DNiMPcuf.d.mts +0 -58
  51. 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 { a as HideOutside, c as tabbableWithin, i as Moved, n as MoveEnded, o as ScrollLock, r as MoveStarted, s as FocusScope, t as Move } from "../move-CZwHyTGk.mjs";
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-DpxwLXvU.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$8,
27
- Model: () => Model$8,
27
+ Message: () => Message$9,
28
+ Model: () => Model$9,
28
29
  TRIGGER_ATTRIBUTE: () => TRIGGER_ATTRIBUTE,
29
- behavior: () => behavior$10,
30
- bundle: () => bundle$8,
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$8 = Schema.Struct({
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$8 = defineMessageUnion({
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 bundle$8 = Bundle.make("DismissLayer", {
67
- Model: Model$8,
68
- Message: Message$8,
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: (_model, message) => Message$8.match(message, {
71
- PressedAt: ({ layers, inside }) => {
72
- const ids = toDismiss(layers, inside);
73
- return ids.length === 0 ? { model: { layers } } : {
74
- model: { layers },
75
- outMessage: Dismiss.make({ ids })
76
- };
77
- },
78
- PressedEscape: ({ layers }) => {
79
- const top = layers[layers.length - 1];
80
- return top !== void 0 && top.escape ? {
81
- model: { layers },
82
- outMessage: Dismiss.make({ ids: [top.id] })
83
- } : { model: { layers } };
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$8.PressedAt({
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$8.PressedEscape({ layers: openLayers(document) }));
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$10 = (_declared) => (slots) => (options) => Behavior.forSlots(slots)({
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$9,
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$9 = (slots) => (options) => Behavior.forSlots(slots)({ [options.container]: Behavior.slot({
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$8,
206
+ behavior: () => behavior$11,
181
207
  bundle: () => InputModality,
182
208
  modalityOfKey: () => modalityOfKey
183
209
  });
184
- const behavior$8 = (declared) => (slots) => (options) => Behavior.forSlots(slots)({ [options.target]: Behavior.slot({
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$7,
199
- Message: () => Message$7,
200
- Model: () => Model$7,
224
+ Args: () => Args$8,
225
+ Message: () => Message$8,
226
+ Model: () => Model$8,
201
227
  Orientation: () => Orientation,
202
- behavior: () => behavior$7,
203
- bundle: () => bundle$7,
228
+ behavior: () => behavior$10,
229
+ bundle: () => bundle$8,
204
230
  idSelector: () => idSelector,
205
231
  itemAttributes: () => itemAttributes,
206
- move: () => move$1,
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$7 = Schema.Struct({
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$7 = defineMessageUnion({
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$7 = Schema.Struct({
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$7 = Bundle.make("RovingTabindex", {
229
- Model: Model$7,
230
- Message: Message$7,
231
- args: Args$7,
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: (_model, message) => ({ model: { current: message.id } })
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$1 = (enabled, current, key, modifiers, options) => {
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$7 = (declared, args) => (slots) => (options) => {
299
- const wrap = (id) => declared.wrapper.make(Message$7.Focused({ id }));
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 = options.items(input);
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$1(items.enabled, from, key, modifiers, {
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, options.items(input), slice(input).current, item, args.virtual, wrap);
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$6,
347
- Message: () => Message$6,
348
- Model: () => Model$6,
349
- behavior: () => behavior$6,
350
- bundle: () => bundle$6,
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$6 = Schema.Struct({
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$6 = defineMessageUnion({
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$6 = Schema.Struct({
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$6 = Bundle.make("GridNavigation", {
370
- Model: Model$6,
371
- Message: Message$6,
372
- args: Args$6,
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: (_model, message) => ({ model: { current: message.id } })
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$6 = (declared, args) => (slots) => (options) => {
434
- const wrap = (id) => declared.wrapper.make(Message$6.Focused({ id }));
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 = options.items(input);
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, options.items(input), slice(input).current, item, args.virtual, wrap);
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$5,
499
- Message: () => Message$5,
500
- Model: () => Model$5,
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$5,
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$5 = Schema.Struct({
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$5 = defineMessageUnion({
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$5 = Schema.Struct({
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$5 = Bundle.make("LiveAnnounce", {
543
- Model: Model$5,
544
- Message: Message$5,
545
- args: Args$5,
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$5.match(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$5.Read({ generation }), "LiveAnnounce.read", generation)]
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$5.Cleared({ generation }), "LiveAnnounce.clear", generation)]
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$5.Announced({
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$4,
617
- Message: () => Message$4,
618
- Model: () => Model$4,
619
- behavior: () => behavior$5,
620
- bundle: () => bundle$4,
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$4 = Schema.Struct({
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$4 = defineMessageUnion({
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$4 = Schema.Struct({
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$4 = Bundle.make("Typeahead", {
642
- Model: Model$4,
643
- Message: Message$4,
644
- args: Args$4,
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$4.match(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$4.Expired({ generation }))
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
- ...model,
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$5 = (declared) => (slots) => (options) => {
705
- const wrap = (char) => declared.wrapper.make(Message$4.Typed({ char }));
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$3,
739
- Message: () => Message$3,
740
- Model: () => Model$3,
741
- behavior: () => behavior$4,
742
- bundle: () => bundle$3,
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$3 = Schema.Struct({
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$3 = defineMessageUnion({
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$3 = Schema.Struct({
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$3 = Bundle.make("ListNavigation", {
771
- Model: Model$3,
772
- Message: Message$3,
773
- args: Args$3,
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$3.match(message, {
780
- Focused: ({ id }) => ({ model: {
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$3.Expired({ generation }))
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
- ...model,
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$1(items.enabled, from, key, modifiers, options);
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$4 = (declared, args) => (slots) => (options) => {
849
+ const behavior$7 = (declared, args) => (slots) => (options) => {
834
850
  const wrap = (message) => declared.wrapper.make(message);
835
- const focused = (id) => wrap(Message$3.Focused({ id }));
851
+ const focused = (id) => wrap(Message$4.Focused({ id }));
836
852
  const slice = (input) => input[declared.field];
837
- const outcomeMessage = (outcome) => outcome._tag === "Navigate" ? focused(outcome.id) : wrap(Message$3.Typed({
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 = options.items(input);
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, texts, state, key, modifiers, moveOptions));
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, options.items(input), slice(input).current, item, args.virtual, focused)
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$2,
886
- Message: () => Message$2,
887
- Model: () => Model$2,
901
+ Args: () => Args$3,
902
+ Message: () => Message$3,
903
+ Model: () => Model$3,
888
904
  PointerType: () => PointerType,
889
905
  Pressed: () => Pressed,
890
- behavior: () => behavior$3,
891
- bundle: () => bundle$2,
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$2 = Schema.Struct({
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$2 = defineMessageUnion({
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$2 = Schema.Struct({
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$2 = Bundle.make("Press", {
956
- Model: Model$2,
957
- Message: Message$2,
958
- args: Args$2,
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$2.match(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$2.Unsuppressed({ generation }))
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$2.PointerDown,
1034
- Message$2.PointerUp,
1035
- Message$2.PointerCancelled,
1036
- Message$2.KeyDown,
1037
- Message$2.KeyUp,
1038
- Message$2.Clicked
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$2.PointerDown({
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$2.PointerUp({
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$2.PointerCancelled({ pointerId: event.pointerId ?? 0 }))],
1063
- ["pointercancel", (event) => offer(Message$2.PointerCancelled({ pointerId: event.pointerId ?? 0 }))],
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$2.KeyDown({
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$2.KeyUp({
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$2.Clicked({
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$3 = (declared) => (slots) => (options) => Behavior.forSlots(slots)({ [options.target]: Behavior.slot({
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$1,
1130
+ Args: () => Args$2,
1115
1131
  LongPressed: () => LongPressed,
1116
- Message: () => Message$1,
1117
- Model: () => Model$1,
1118
- behavior: () => behavior$2,
1119
- bundle: () => bundle$1
1132
+ Message: () => Message$2,
1133
+ Model: () => Model$2,
1134
+ behavior: () => behavior$5,
1135
+ bundle: () => bundle$2
1120
1136
  });
1121
- const Model$1 = Schema.Struct({
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$1 = defineMessageUnion({
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$1 = Schema.Struct({ thresholdMs: Schema.Number.pipe(Schema.check(Schema.isGreaterThan(0)), Schema.check(Schema.isFinite())) });
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$1.Elapsed({
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$1 = Bundle.make("LongPress", {
1186
- Model: Model$1,
1187
- Message: Message$1,
1188
- args: Args$1,
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$1.match(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$2 = (declared) => (slots) => (options) => Behavior.forSlots(slots)({ [options.target]: Behavior.slot({
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$1,
1247
+ behavior: () => behavior$4,
1232
1248
  mount: () => Move
1233
1249
  });
1234
- const behavior$1 = (slots) => (options) => Behavior.forSlots(slots)({ [options.handle]: Behavior.slot({
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
- return { model: {
1322
- selected: [...model.selected.filter((other) => !range.includes(other)), ...range],
1323
- anchor: model.anchor ?? id
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 }) => ({ model: {
1327
- selected: args.mode === "none" ? [] : [...new Set(ids)],
1328
- anchor: model.anchor
1329
- } }),
1330
- Cleared: () => ({ model: {
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 = options.items(input);
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[declared.field].selected.includes(id)), ...clickable ? [h.OnClick(wrap(Message.Activated({ id })))] : []];
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
- export { dismiss_layer_exports as DismissLayer, 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, press_exports as Press, roving_tabindex_exports as RovingTabindex, selection_exports as Selection, typeahead_exports as Typeahead };
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