foldkit-primitives 0.15.4 → 0.16.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 (422) hide show
  1. package/dist/device/fullscreen.d.ts +28 -0
  2. package/dist/device/fullscreen.d.ts.map +1 -0
  3. package/dist/device/fullscreen.js +69 -0
  4. package/dist/device/fullscreen.js.map +1 -0
  5. package/dist/device/geolocation.d.ts +56 -0
  6. package/dist/device/geolocation.d.ts.map +1 -0
  7. package/dist/device/geolocation.js +87 -0
  8. package/dist/device/geolocation.js.map +1 -0
  9. package/dist/device/index.d.ts +5 -0
  10. package/dist/device/index.js +6 -0
  11. package/dist/device/media-devices.d.ts +88 -0
  12. package/dist/device/media-devices.d.ts.map +1 -0
  13. package/dist/device/media-devices.js +117 -0
  14. package/dist/device/media-devices.js.map +1 -0
  15. package/dist/device/media-stream.d.ts +93 -0
  16. package/dist/device/media-stream.d.ts.map +1 -0
  17. package/dist/device/media-stream.js +106 -0
  18. package/dist/device/media-stream.js.map +1 -0
  19. package/dist/device/permissions.d.ts +106 -0
  20. package/dist/device/permissions.d.ts.map +1 -0
  21. package/dist/device/permissions.js +143 -0
  22. package/dist/device/permissions.js.map +1 -0
  23. package/dist/dom/autofocus.d.ts +15 -0
  24. package/dist/dom/autofocus.d.ts.map +1 -0
  25. package/dist/dom/autofocus.js +22 -0
  26. package/dist/dom/autofocus.js.map +1 -0
  27. package/dist/dom/clipboard.d.ts +30 -0
  28. package/dist/dom/clipboard.d.ts.map +1 -0
  29. package/dist/dom/clipboard.js +50 -0
  30. package/dist/dom/clipboard.js.map +1 -0
  31. package/dist/dom/editable-text.d.ts +72 -0
  32. package/dist/dom/editable-text.d.ts.map +1 -0
  33. package/dist/dom/editable-text.js +232 -0
  34. package/dist/dom/editable-text.js.map +1 -0
  35. package/dist/dom/fluid-hover.d.ts +93 -0
  36. package/dist/dom/fluid-hover.d.ts.map +1 -0
  37. package/dist/dom/fluid-hover.js +294 -0
  38. package/dist/dom/fluid-hover.js.map +1 -0
  39. package/dist/dom/focus-scope.d.ts +21 -0
  40. package/dist/dom/focus-scope.d.ts.map +1 -0
  41. package/dist/dom/focus-scope.js +93 -0
  42. package/dist/dom/focus-scope.js.map +1 -0
  43. package/dist/dom/follow-tab-stop.d.ts +11 -0
  44. package/dist/dom/follow-tab-stop.d.ts.map +1 -0
  45. package/dist/dom/follow-tab-stop.js +54 -0
  46. package/dist/dom/follow-tab-stop.js.map +1 -0
  47. package/dist/dom/image-decode.d.ts +27 -0
  48. package/dist/dom/image-decode.d.ts.map +1 -0
  49. package/dist/dom/image-decode.js +50 -0
  50. package/dist/dom/image-decode.js.map +1 -0
  51. package/dist/dom/index.d.ts +24 -0
  52. package/dist/dom/index.js +25 -0
  53. package/dist/dom/input-mask.d.ts +35 -0
  54. package/dist/dom/input-mask.d.ts.map +1 -0
  55. package/dist/dom/input-mask.js +90 -0
  56. package/dist/dom/input-mask.js.map +1 -0
  57. package/dist/dom/keep-in-view.d.ts +16 -0
  58. package/dist/dom/keep-in-view.d.ts.map +1 -0
  59. package/dist/dom/keep-in-view.js +38 -0
  60. package/dist/dom/keep-in-view.js.map +1 -0
  61. package/dist/dom/keyboard-inset.d.ts +29 -0
  62. package/dist/dom/keyboard-inset.d.ts.map +1 -0
  63. package/dist/dom/keyboard-inset.js +45 -0
  64. package/dist/dom/keyboard-inset.js.map +1 -0
  65. package/dist/dom/layers.d.ts +6 -0
  66. package/dist/dom/layers.d.ts.map +1 -0
  67. package/dist/dom/layers.js +35 -0
  68. package/dist/dom/layers.js.map +1 -0
  69. package/dist/dom/measure.d.ts +42 -0
  70. package/dist/dom/measure.d.ts.map +1 -0
  71. package/dist/dom/measure.js +123 -0
  72. package/dist/dom/measure.js.map +1 -0
  73. package/dist/dom/move.d.ts +45 -0
  74. package/dist/dom/move.d.ts.map +1 -0
  75. package/dist/dom/move.js +96 -0
  76. package/dist/dom/move.js.map +1 -0
  77. package/dist/dom/pointed-links.d.ts +13 -0
  78. package/dist/dom/pointed-links.d.ts.map +1 -0
  79. package/dist/dom/pointed-links.js +59 -0
  80. package/dist/dom/pointed-links.js.map +1 -0
  81. package/dist/dom/pointer-drag.d.ts +132 -0
  82. package/dist/dom/pointer-drag.d.ts.map +1 -0
  83. package/dist/dom/pointer-drag.js +307 -0
  84. package/dist/dom/pointer-drag.js.map +1 -0
  85. package/dist/dom/pointer-position.d.ts +133 -0
  86. package/dist/dom/pointer-position.d.ts.map +1 -0
  87. package/dist/dom/pointer-position.js +173 -0
  88. package/dist/dom/pointer-position.js.map +1 -0
  89. package/dist/dom/script-loader.d.ts +12 -0
  90. package/dist/dom/script-loader.d.ts.map +1 -0
  91. package/dist/dom/script-loader.js +54 -0
  92. package/dist/dom/script-loader.js.map +1 -0
  93. package/dist/dom/scroll-bars.d.ts +52 -0
  94. package/dist/dom/scroll-bars.d.ts.map +1 -0
  95. package/dist/dom/scroll-bars.js +259 -0
  96. package/dist/dom/scroll-bars.js.map +1 -0
  97. package/dist/dom/scroll-settle.d.ts +33 -0
  98. package/dist/dom/scroll-settle.d.ts.map +1 -0
  99. package/dist/dom/scroll-settle.js +84 -0
  100. package/dist/dom/scroll-settle.js.map +1 -0
  101. package/dist/dom/scroll.d.ts +21 -0
  102. package/dist/dom/scroll.d.ts.map +1 -0
  103. package/dist/dom/scroll.js +141 -0
  104. package/dist/dom/scroll.js.map +1 -0
  105. package/dist/dom/share.d.ts +19 -0
  106. package/dist/dom/share.d.ts.map +1 -0
  107. package/dist/dom/share.js +41 -0
  108. package/dist/dom/share.js.map +1 -0
  109. package/dist/dom/swipe.d.ts +121 -0
  110. package/dist/dom/swipe.d.ts.map +1 -0
  111. package/dist/dom/swipe.js +291 -0
  112. package/dist/dom/swipe.js.map +1 -0
  113. package/dist/dom/targets.d.ts +45 -0
  114. package/dist/dom/targets.d.ts.map +1 -0
  115. package/dist/dom/targets.js +70 -0
  116. package/dist/dom/targets.js.map +1 -0
  117. package/dist/dom/track-box.d.ts +61 -0
  118. package/dist/dom/track-box.d.ts.map +1 -0
  119. package/dist/dom/track-box.js +131 -0
  120. package/dist/dom/track-box.js.map +1 -0
  121. package/dist/events/active-element.d.ts +15 -0
  122. package/dist/events/active-element.d.ts.map +1 -0
  123. package/dist/events/active-element.js +33 -0
  124. package/dist/events/active-element.js.map +1 -0
  125. package/dist/events/hotkeys.d.ts +22 -0
  126. package/dist/events/hotkeys.d.ts.map +1 -0
  127. package/dist/events/hotkeys.js +56 -0
  128. package/dist/events/hotkeys.js.map +1 -0
  129. package/dist/events/idle.d.ts +29 -0
  130. package/dist/events/idle.d.ts.map +1 -0
  131. package/dist/events/idle.js +51 -0
  132. package/dist/events/idle.js.map +1 -0
  133. package/dist/events/index.d.ts +9 -0
  134. package/dist/events/index.js +10 -0
  135. package/dist/{input-modality-l7InESYh.d.mts → events/input-modality.d.ts} +9 -10
  136. package/dist/events/input-modality.d.ts.map +1 -0
  137. package/dist/{input-modality-XwQOFLip.mjs → events/input-modality.js} +2 -2
  138. package/dist/events/input-modality.js.map +1 -0
  139. package/dist/events/keyboard.d.ts +30 -0
  140. package/dist/events/keyboard.d.ts.map +1 -0
  141. package/dist/events/keyboard.js +55 -0
  142. package/dist/events/keyboard.js.map +1 -0
  143. package/dist/events/pointer.d.ts +15 -0
  144. package/dist/events/pointer.d.ts.map +1 -0
  145. package/dist/events/pointer.js +28 -0
  146. package/dist/events/pointer.js.map +1 -0
  147. package/dist/events/scroll.d.ts +16 -0
  148. package/dist/events/scroll.d.ts.map +1 -0
  149. package/dist/events/scroll.js +29 -0
  150. package/dist/events/scroll.js.map +1 -0
  151. package/dist/events/visibility.d.ts +25 -0
  152. package/dist/events/visibility.d.ts.map +1 -0
  153. package/dist/events/visibility.js +28 -0
  154. package/dist/events/visibility.js.map +1 -0
  155. package/dist/events/window-size.d.ts +30 -0
  156. package/dist/events/window-size.d.ts.map +1 -0
  157. package/dist/events/window-size.js +63 -0
  158. package/dist/events/window-size.js.map +1 -0
  159. package/dist/index.d.ts +9 -0
  160. package/dist/index.js +10 -0
  161. package/dist/interaction/bundle-behavior.d.ts +37 -0
  162. package/dist/interaction/bundle-behavior.d.ts.map +1 -0
  163. package/dist/interaction/bundle-behavior.js +34 -0
  164. package/dist/interaction/bundle-behavior.js.map +1 -0
  165. package/dist/interaction/dismiss-layer.d.ts +100 -0
  166. package/dist/interaction/dismiss-layer.d.ts.map +1 -0
  167. package/dist/interaction/dismiss-layer.js +140 -0
  168. package/dist/interaction/dismiss-layer.js.map +1 -0
  169. package/dist/interaction/editable-text.d.ts +13 -0
  170. package/dist/interaction/editable-text.d.ts.map +1 -0
  171. package/dist/interaction/editable-text.js +17 -0
  172. package/dist/interaction/editable-text.js.map +1 -0
  173. package/dist/interaction/focus-scope.d.ts +21 -0
  174. package/dist/interaction/focus-scope.d.ts.map +1 -0
  175. package/dist/interaction/focus-scope.js +25 -0
  176. package/dist/interaction/focus-scope.js.map +1 -0
  177. package/dist/interaction/focus-visible.d.ts +9 -0
  178. package/dist/interaction/focus-visible.d.ts.map +1 -0
  179. package/dist/interaction/focus-visible.js +10 -0
  180. package/dist/interaction/focus-visible.js.map +1 -0
  181. package/dist/interaction/grid-navigation.d.ts +73 -0
  182. package/dist/interaction/grid-navigation.d.ts.map +1 -0
  183. package/dist/interaction/grid-navigation.js +137 -0
  184. package/dist/interaction/grid-navigation.js.map +1 -0
  185. package/dist/interaction/index.d.ts +21 -0
  186. package/dist/interaction/index.js +21 -0
  187. package/dist/interaction/layers.d.ts +12 -0
  188. package/dist/interaction/layers.d.ts.map +1 -0
  189. package/dist/interaction/layers.js +14 -0
  190. package/dist/interaction/layers.js.map +1 -0
  191. package/dist/interaction/list-navigation.d.ts +105 -0
  192. package/dist/interaction/list-navigation.d.ts.map +1 -0
  193. package/dist/interaction/list-navigation.js +168 -0
  194. package/dist/interaction/list-navigation.js.map +1 -0
  195. package/dist/interaction/live-announce.d.ts +70 -0
  196. package/dist/interaction/live-announce.d.ts.map +1 -0
  197. package/dist/interaction/live-announce.js +115 -0
  198. package/dist/interaction/live-announce.js.map +1 -0
  199. package/dist/interaction/long-press.d.ts +102 -0
  200. package/dist/interaction/long-press.d.ts.map +1 -0
  201. package/dist/interaction/long-press.js +115 -0
  202. package/dist/interaction/long-press.js.map +1 -0
  203. package/dist/interaction/move.d.ts +10 -0
  204. package/dist/interaction/move.d.ts.map +1 -0
  205. package/dist/interaction/move.js +17 -0
  206. package/dist/interaction/move.js.map +1 -0
  207. package/dist/interaction/overlay.d.ts +45 -0
  208. package/dist/interaction/overlay.d.ts.map +1 -0
  209. package/dist/interaction/overlay.js +57 -0
  210. package/dist/interaction/overlay.js.map +1 -0
  211. package/dist/interaction/placing.d.ts +86 -0
  212. package/dist/interaction/placing.d.ts.map +1 -0
  213. package/dist/interaction/placing.js +239 -0
  214. package/dist/interaction/placing.js.map +1 -0
  215. package/dist/interaction/pointer-drag.d.ts +23 -0
  216. package/dist/interaction/pointer-drag.d.ts.map +1 -0
  217. package/dist/interaction/pointer-drag.js +21 -0
  218. package/dist/interaction/pointer-drag.js.map +1 -0
  219. package/dist/interaction/press.d.ts +152 -0
  220. package/dist/interaction/press.d.ts.map +1 -0
  221. package/dist/interaction/press.js +229 -0
  222. package/dist/interaction/press.js.map +1 -0
  223. package/dist/interaction/roving-tabindex.d.ts +85 -0
  224. package/dist/interaction/roving-tabindex.d.ts.map +1 -0
  225. package/dist/interaction/roving-tabindex.js +142 -0
  226. package/dist/interaction/roving-tabindex.js.map +1 -0
  227. package/dist/interaction/selection.d.ts +72 -0
  228. package/dist/interaction/selection.d.ts.map +1 -0
  229. package/dist/interaction/selection.js +129 -0
  230. package/dist/interaction/selection.js.map +1 -0
  231. package/dist/interaction/targets.d.ts +15 -0
  232. package/dist/interaction/targets.d.ts.map +1 -0
  233. package/dist/interaction/targets.js +20 -0
  234. package/dist/interaction/targets.js.map +1 -0
  235. package/dist/interaction/tree-navigation.d.ts +111 -0
  236. package/dist/interaction/tree-navigation.d.ts.map +1 -0
  237. package/dist/interaction/tree-navigation.js +207 -0
  238. package/dist/interaction/tree-navigation.js.map +1 -0
  239. package/dist/interaction/typeahead.d.ts +73 -0
  240. package/dist/interaction/typeahead.d.ts.map +1 -0
  241. package/dist/interaction/typeahead.js +113 -0
  242. package/dist/interaction/typeahead.js.map +1 -0
  243. package/dist/internal.d.ts +16 -0
  244. package/dist/internal.d.ts.map +1 -0
  245. package/dist/{internal-C4gHodLc.mjs → internal.js} +7 -5
  246. package/dist/internal.js.map +1 -0
  247. package/dist/media/breakpoints.d.ts +37 -0
  248. package/dist/media/breakpoints.d.ts.map +1 -0
  249. package/dist/media/breakpoints.js +64 -0
  250. package/dist/media/breakpoints.js.map +1 -0
  251. package/dist/media/index.d.ts +3 -0
  252. package/dist/media/index.js +4 -0
  253. package/dist/media/mediaquery.d.ts +40 -0
  254. package/dist/media/mediaquery.d.ts.map +1 -0
  255. package/dist/media/mediaquery.js +42 -0
  256. package/dist/media/mediaquery.js.map +1 -0
  257. package/dist/media/platform.d.ts +30 -0
  258. package/dist/media/platform.d.ts.map +1 -0
  259. package/dist/media/platform.js +50 -0
  260. package/dist/media/platform.js.map +1 -0
  261. package/dist/motion/index.d.ts +5 -0
  262. package/dist/motion/index.js +5 -0
  263. package/dist/motion/motion.d.ts +16 -0
  264. package/dist/motion/motion.d.ts.map +1 -0
  265. package/dist/motion/motion.js +25 -0
  266. package/dist/motion/motion.js.map +1 -0
  267. package/dist/motion/presence.d.ts +37 -0
  268. package/dist/motion/presence.d.ts.map +1 -0
  269. package/dist/motion/presence.js +65 -0
  270. package/dist/motion/presence.js.map +1 -0
  271. package/dist/motion/spring.d.ts +50 -0
  272. package/dist/motion/spring.d.ts.map +1 -0
  273. package/dist/motion/spring.js +92 -0
  274. package/dist/motion/spring.js.map +1 -0
  275. package/dist/motion/tween.d.ts +44 -0
  276. package/dist/motion/tween.d.ts.map +1 -0
  277. package/dist/motion/tween.js +65 -0
  278. package/dist/motion/tween.js.map +1 -0
  279. package/dist/net/broadcast.d.ts +39 -0
  280. package/dist/net/broadcast.d.ts.map +1 -0
  281. package/dist/net/broadcast.js +73 -0
  282. package/dist/net/broadcast.js.map +1 -0
  283. package/dist/net/index.d.ts +5 -0
  284. package/dist/net/index.js +6 -0
  285. package/dist/net/online.d.ts +24 -0
  286. package/dist/net/online.d.ts.map +1 -0
  287. package/dist/net/online.js +26 -0
  288. package/dist/net/online.js.map +1 -0
  289. package/dist/net/sharedHost.d.ts +64 -0
  290. package/dist/net/sharedHost.d.ts.map +1 -0
  291. package/dist/net/sharedHost.js +124 -0
  292. package/dist/net/sharedHost.js.map +1 -0
  293. package/dist/net/sse.d.ts +115 -0
  294. package/dist/net/sse.d.ts.map +1 -0
  295. package/dist/net/sse.js +138 -0
  296. package/dist/net/sse.js.map +1 -0
  297. package/dist/net/websocket.d.ts +172 -0
  298. package/dist/net/websocket.d.ts.map +1 -0
  299. package/dist/net/websocket.js +199 -0
  300. package/dist/net/websocket.js.map +1 -0
  301. package/dist/observers/bounds.d.ts +24 -0
  302. package/dist/observers/bounds.d.ts.map +1 -0
  303. package/dist/observers/bounds.js +51 -0
  304. package/dist/observers/bounds.js.map +1 -0
  305. package/dist/observers/index.d.ts +4 -0
  306. package/dist/observers/index.js +5 -0
  307. package/dist/observers/intersection.d.ts +18 -0
  308. package/dist/observers/intersection.d.ts.map +1 -0
  309. package/dist/observers/intersection.js +33 -0
  310. package/dist/observers/intersection.js.map +1 -0
  311. package/dist/observers/mutation.d.ts +25 -0
  312. package/dist/observers/mutation.d.ts.map +1 -0
  313. package/dist/observers/mutation.js +53 -0
  314. package/dist/observers/mutation.js.map +1 -0
  315. package/dist/observers/resize.d.ts +18 -0
  316. package/dist/observers/resize.d.ts.map +1 -0
  317. package/dist/observers/resize.js +33 -0
  318. package/dist/observers/resize.js.map +1 -0
  319. package/dist/state/history.d.ts +115 -0
  320. package/dist/state/history.d.ts.map +1 -0
  321. package/dist/state/history.js +167 -0
  322. package/dist/state/history.js.map +1 -0
  323. package/dist/state/index.d.ts +6 -0
  324. package/dist/state/index.js +7 -0
  325. package/dist/state/locale.d.ts +27 -0
  326. package/dist/state/locale.d.ts.map +1 -0
  327. package/dist/state/locale.js +25 -0
  328. package/dist/state/locale.js.map +1 -0
  329. package/dist/state/pagination.d.ts +53 -0
  330. package/dist/state/pagination.d.ts.map +1 -0
  331. package/dist/state/pagination.js +72 -0
  332. package/dist/state/pagination.js.map +1 -0
  333. package/dist/state/range.d.ts +9 -0
  334. package/dist/state/range.d.ts.map +1 -0
  335. package/dist/state/range.js +18 -0
  336. package/dist/state/range.js.map +1 -0
  337. package/dist/state/selection.d.ts +47 -0
  338. package/dist/state/selection.d.ts.map +1 -0
  339. package/dist/state/selection.js +39 -0
  340. package/dist/state/selection.js.map +1 -0
  341. package/dist/state/virtual.d.ts +201 -0
  342. package/dist/state/virtual.d.ts.map +1 -0
  343. package/dist/state/{index.mjs → virtual.js} +10 -306
  344. package/dist/state/virtual.js.map +1 -0
  345. package/dist/time/debounce.d.ts +43 -0
  346. package/dist/time/debounce.d.ts.map +1 -0
  347. package/dist/time/debounce.js +65 -0
  348. package/dist/time/debounce.js.map +1 -0
  349. package/dist/time/index.d.ts +6 -0
  350. package/dist/time/index.js +7 -0
  351. package/dist/time/interval.d.ts +37 -0
  352. package/dist/time/interval.d.ts.map +1 -0
  353. package/dist/time/interval.js +52 -0
  354. package/dist/time/interval.js.map +1 -0
  355. package/dist/time/relative.d.ts +8 -0
  356. package/dist/time/relative.d.ts.map +1 -0
  357. package/dist/time/relative.js +27 -0
  358. package/dist/time/relative.js.map +1 -0
  359. package/dist/time/throttle.d.ts +38 -0
  360. package/dist/time/throttle.d.ts.map +1 -0
  361. package/dist/time/throttle.js +39 -0
  362. package/dist/time/throttle.js.map +1 -0
  363. package/dist/time/ticks.d.ts +21 -0
  364. package/dist/time/ticks.d.ts.map +1 -0
  365. package/dist/time/ticks.js +30 -0
  366. package/dist/time/ticks.js.map +1 -0
  367. package/dist/time/timer.d.ts +32 -0
  368. package/dist/time/timer.d.ts.map +1 -0
  369. package/dist/time/timer.js +50 -0
  370. package/dist/time/timer.js.map +1 -0
  371. package/package.json +31 -31
  372. package/dist/device/index.d.mts +0 -335
  373. package/dist/device/index.d.mts.map +0 -1
  374. package/dist/device/index.mjs +0 -493
  375. package/dist/device/index.mjs.map +0 -1
  376. package/dist/dom/index.d.mts +0 -714
  377. package/dist/dom/index.d.mts.map +0 -1
  378. package/dist/dom/index.mjs +0 -1840
  379. package/dist/dom/index.mjs.map +0 -1
  380. package/dist/editable-text-Bg7vil7h.d.mts +0 -307
  381. package/dist/editable-text-Bg7vil7h.d.mts.map +0 -1
  382. package/dist/events/index.d.mts +0 -168
  383. package/dist/events/index.d.mts.map +0 -1
  384. package/dist/events/index.mjs +0 -307
  385. package/dist/events/index.mjs.map +0 -1
  386. package/dist/follow-tab-stop-DM5EI4Ci.mjs +0 -856
  387. package/dist/follow-tab-stop-DM5EI4Ci.mjs.map +0 -1
  388. package/dist/index-Bomdwb7e.d.mts +0 -142
  389. package/dist/index-Bomdwb7e.d.mts.map +0 -1
  390. package/dist/index.d.mts +0 -12
  391. package/dist/index.mjs +0 -12
  392. package/dist/input-modality-XwQOFLip.mjs.map +0 -1
  393. package/dist/input-modality-l7InESYh.d.mts.map +0 -1
  394. package/dist/interaction/index.d.mts +0 -1193
  395. package/dist/interaction/index.d.mts.map +0 -1
  396. package/dist/interaction/index.mjs +0 -1992
  397. package/dist/interaction/index.mjs.map +0 -1
  398. package/dist/internal-C4gHodLc.mjs.map +0 -1
  399. package/dist/media/index.d.mts +0 -103
  400. package/dist/media/index.d.mts.map +0 -1
  401. package/dist/media/index.mjs +0 -146
  402. package/dist/media/index.mjs.map +0 -1
  403. package/dist/motion/index.d.mts +0 -2
  404. package/dist/motion/index.mjs +0 -1
  405. package/dist/motion-BmlJCjn6.mjs +0 -236
  406. package/dist/motion-BmlJCjn6.mjs.map +0 -1
  407. package/dist/net/index.d.mts +0 -338
  408. package/dist/net/index.d.mts.map +0 -1
  409. package/dist/net/index.mjs +0 -530
  410. package/dist/net/index.mjs.map +0 -1
  411. package/dist/observers/index.d.mts +0 -76
  412. package/dist/observers/index.d.mts.map +0 -1
  413. package/dist/observers/index.mjs +0 -155
  414. package/dist/observers/index.mjs.map +0 -1
  415. package/dist/rolldown-runtime-D8jQpDG7.mjs +0 -12
  416. package/dist/state/index.d.mts +0 -432
  417. package/dist/state/index.d.mts.map +0 -1
  418. package/dist/state/index.mjs.map +0 -1
  419. package/dist/time/index.d.mts +0 -167
  420. package/dist/time/index.d.mts.map +0 -1
  421. package/dist/time/index.mjs +0 -236
  422. package/dist/time/index.mjs.map +0 -1
@@ -1,1992 +0,0 @@
1
- import { t as __exportAll } from "../rolldown-runtime-D8jQpDG7.mjs";
2
- import { A as FocusScope, C as targetOf, D as Moved, E as MoveStarted, O as HideOutside, S as Targets, T as MoveEnded, _ as PointerDrag, a as TextCommitted, b as TargetHovered, d as DragPlace, f as DragStarted, i as TextCancelled, j as tabbableWithin, k as ScrollLock, 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 boxOf, w as Move, x as TargetPressed, y as zoneOf } from "../follow-tab-stop-DM5EI4Ci.mjs";
3
- import { n as perInput, r as sameIds, t as clearQuery } from "../internal-C4gHodLc.mjs";
4
- import { a as modalityOfKey, i as Modality, t as InputModality } from "../input-modality-XwQOFLip.mjs";
5
- import { Effect, Option, Queue, Schema, Stream } from "effect";
6
- import * as Mount from "foldkit/mount";
7
- import { defineMessageUnion } from "foldkit/message";
8
- import * as Subscription from "foldkit/subscription";
9
- import { Bundle } from "foldkit-bundle";
10
- import { Behavior, Capability } from "foldkit-mixins";
11
- // src/interaction/bundle-behavior.ts
12
- var bundle_behavior_exports = /* @__PURE__ */ __exportAll({
13
- BundleBehavior: () => BundleBehavior,
14
- fromBundle: () => fromBundle
15
- });
16
- /**
17
- * `fromBundle(declared)(slots)<Input, ParentMessage>(spec)` — a Behavior over
18
- * `declared`, with every `events` mount and every `send` mapped through the
19
- * placement's wrapper. `Input` must hold the Bundle's Model at the declared
20
- * field, so attributes read the placed slice; the parent's `Message` includes
21
- * the declared wrapper cases, so nothing can be sent outside it.
22
- */
23
- const fromBundle = (declared) => (slots) => (spec, options) => {
24
- const send = (message) => declared.wrapper.make(message);
25
- const built = Object.create(null);
26
- for (const [name, slot] of Object.entries(spec)) {
27
- if (slot === void 0) continue;
28
- const { requires, attributes, events } = slot;
29
- if (requires === void 0 && attributes === void 0 && events === void 0) continue;
30
- built[name] = {
31
- ...requires === void 0 ? {} : { requires },
32
- ...attributes === void 0 ? {} : { attributes: (context) => attributes({
33
- input: context.input,
34
- h: context.h,
35
- ...context.item === void 0 ? {} : { item: context.item },
36
- send
37
- }) },
38
- ...events === void 0 ? {} : { mount: () => Mount.mapMessage(events, send) }
39
- };
40
- }
41
- return Behavior.forSlots(slots)(built, options);
42
- };
43
- const BundleBehavior = { fromBundle };
44
- // src/interaction/dismiss-layer.ts
45
- /**
46
- * Dismissable layers: Escape closes the topmost open layer, a pointer press
47
- * outside closes the layers it is outside of. One Bundle, placed once, owns
48
- * the document listeners; each layer's Behavior marks its container (and its
49
- * trigger) with data attributes. The stack is the DOM order of the marked
50
- * elements at the moment of the event, so a layer opening or closing needs no
51
- * registration. A press inside a parent layer is outside its children, so the
52
- * parent stays and the children go; a press on a layer's trigger counts as
53
- * inside it, so a trigger never dismisses and reopens in one click. What a
54
- * dismissal does is the placement's `onOut`.
55
- */
56
- var dismiss_layer_exports = /* @__PURE__ */ __exportAll({
57
- Dismiss: () => Dismiss,
58
- LAYER_ATTRIBUTE: () => LAYER_ATTRIBUTE,
59
- Layer: () => Layer$1,
60
- Message: () => Message$9,
61
- Model: () => Model$9,
62
- TRIGGER_ATTRIBUTE: () => TRIGGER_ATTRIBUTE,
63
- behavior: () => behavior$14,
64
- bundle: () => bundle$9,
65
- layersOnPath: () => layersOnPath,
66
- toDismiss: () => toDismiss
67
- });
68
- const LAYER_ATTRIBUTE = "data-foldkit-plus-layer";
69
- const TRIGGER_ATTRIBUTE = "data-foldkit-plus-layer-trigger";
70
- const OUTSIDE_ATTRIBUTE = "data-foldkit-plus-layer-outside";
71
- const ESCAPE_ATTRIBUTE = "data-foldkit-plus-layer-escape";
72
- /** An open layer as the document showed it: bottom to top is DOM order. */
73
- const Layer$1 = Schema.Struct({
74
- id: Schema.String,
75
- /** Dismiss on a press outside it. */
76
- outside: Schema.Boolean,
77
- /** Dismiss on Escape when topmost. */
78
- escape: Schema.Boolean
79
- });
80
- const Model$9 = Schema.Struct({
81
- /** The open layers at the last event, bottom to top. */
82
- layers: Schema.Array(Layer$1) });
83
- const Message$9 = defineMessageUnion({
84
- /** A pointer went down; `inside` are the layers whose element or trigger was on the event's path. */
85
- PressedAt: {
86
- layers: Schema.Array(Layer$1),
87
- inside: Schema.Array(Schema.String)
88
- },
89
- PressedEscape: { layers: Schema.Array(Layer$1) }
90
- });
91
- const Dismiss = Schema.TaggedStruct("Dismiss", { ids: Schema.Array(Schema.String) });
92
- /** Which layers a press inside `inside` dismisses: every layer above the topmost one hit, or all when none was. */
93
- const toDismiss = (layers, inside) => {
94
- let topmostHit = -1;
95
- layers.forEach((layer, index) => {
96
- if (inside.includes(layer.id)) topmostHit = index;
97
- });
98
- return layers.slice(topmostHit + 1).filter((layer) => layer.outside).map((layer) => layer.id);
99
- };
100
- 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);
101
- const bundle$9 = Bundle.make("DismissLayer", {
102
- Model: Model$9,
103
- Message: Message$9,
104
- init: () => ({ model: { layers: [] } }),
105
- update: (model, message) => {
106
- const next = sameLayers(model.layers, message.layers) ? model : { layers: message.layers };
107
- return Message$9.match(message, {
108
- PressedAt: ({ layers, inside }) => {
109
- const ids = toDismiss(layers, inside);
110
- return ids.length === 0 ? { model: next } : {
111
- model: next,
112
- outMessage: Dismiss.make({ ids })
113
- };
114
- },
115
- PressedEscape: ({ layers }) => {
116
- const top = layers[layers.length - 1];
117
- return top !== void 0 && top.escape ? {
118
- model: next,
119
- outMessage: Dismiss.make({ ids: [top.id] })
120
- } : { model: next };
121
- }
122
- });
123
- },
124
- subscriptions: () => Subscription.make()(() => ({ document: Subscription.persistentEntry(documentEvents()) }))
125
- });
126
- const openLayers = (root) => Array.from(root.querySelectorAll(`[${LAYER_ATTRIBUTE}]`)).map((element) => ({
127
- id: element.getAttribute("data-foldkit-plus-layer") ?? "",
128
- outside: element.getAttribute(OUTSIDE_ATTRIBUTE) !== "false",
129
- escape: element.getAttribute(ESCAPE_ATTRIBUTE) !== "false"
130
- }));
131
- /** The layer ids whose element or trigger is on the event's composed path. */
132
- const layersOnPath = (path) => {
133
- const ids = [];
134
- for (const target of path) {
135
- if (!(target instanceof Element)) continue;
136
- const id = target.getAttribute("data-foldkit-plus-layer") ?? target.getAttribute("data-foldkit-plus-layer-trigger");
137
- if (id !== null && !ids.includes(id)) ids.push(id);
138
- }
139
- return ids;
140
- };
141
- /**
142
- * The facts are read inside the listener, not after: `composedPath()` is
143
- * empty once dispatch has finished, and the open layers must be the ones the
144
- * press saw, not the ones a later render left.
145
- */
146
- const documentEvents = () => {
147
- if (typeof document === "undefined") return Stream.empty;
148
- return Stream.callback((queue) => Effect.acquireRelease(Effect.sync(() => {
149
- const onPointerDown = (event) => Queue.offerUnsafe(queue, Message$9.PressedAt({
150
- layers: openLayers(document),
151
- inside: layersOnPath(event.composedPath())
152
- }));
153
- const onKeyDown = (event) => {
154
- if (event.key !== "Escape") return;
155
- Queue.offerUnsafe(queue, Message$9.PressedEscape({ layers: openLayers(document) }));
156
- };
157
- document.addEventListener("pointerdown", onPointerDown, { capture: true });
158
- document.addEventListener("keydown", onKeyDown, { capture: true });
159
- return {
160
- onPointerDown,
161
- onKeyDown
162
- };
163
- }), ({ onPointerDown, onKeyDown }) => Effect.sync(() => {
164
- document.removeEventListener("pointerdown", onPointerDown, { capture: true });
165
- document.removeEventListener("keydown", onKeyDown, { capture: true });
166
- })));
167
- };
168
- /**
169
- * Marks the layer's container and trigger for the placed stack. The layer
170
- * takes part only while its element is rendered, so an `open` flag in the
171
- * parent Model is the whole lifecycle. Not for an element with `popover`,
172
- * which the browser dismisses itself.
173
- */
174
- const behavior$14 = (_declared) => (slots) => (options) => Behavior.forSlots(slots)({
175
- [options.layer]: Behavior.slot({
176
- requires: { capability: Capability.Container },
177
- attributes: ({ input, h }) => [
178
- h.Attribute(LAYER_ATTRIBUTE, options.id(input)),
179
- ...options.outsidePress === false ? [h.Attribute(OUTSIDE_ATTRIBUTE, "false")] : [],
180
- ...options.escape === false ? [h.Attribute(ESCAPE_ATTRIBUTE, "false")] : []
181
- ]
182
- }),
183
- ...options.trigger === void 0 ? {} : { [options.trigger]: Behavior.slot({
184
- requires: { capability: Capability.Interactive },
185
- attributes: ({ input, h }) => [h.Attribute(TRIGGER_ATTRIBUTE, options.id(input))]
186
- }) }
187
- }, { name: "DismissLayer" });
188
- // src/interaction/editable-text.ts
189
- /**
190
- * `EditableText` as a Behavior: attaches the `foldkit-primitives/dom` Mount to
191
- * a container slot and maps what it reports into the view's Messages. No
192
- * Bundle: the text typed is a fact, and what it changes is the parent's
193
- * `update`.
194
- */
195
- var editable_text_exports = /* @__PURE__ */ __exportAll({
196
- EditAsked: () => EditAsked,
197
- TextCancelled: () => TextCancelled,
198
- TextCommitted: () => TextCommitted,
199
- TextEdited: () => TextEdited,
200
- behavior: () => behavior$13,
201
- mount: () => EditableText,
202
- textOf: () => textOf
203
- });
204
- const behavior$13 = (slots) => (options) => Behavior.forSlots(slots)({ [options.container]: Behavior.slot({
205
- requires: { capability: Capability.Container },
206
- mount: () => Mount.mapMessage(EditableText({ attribute: options.attribute }), options.toMessage)
207
- }) }, { name: "EditableText" });
208
- // src/interaction/focus-scope.ts
209
- /**
210
- * `FocusScope` as a Behavior: attaches the `foldkit-primitives/dom` Mount to a
211
- * container slot. No Bundle, because which element has focus is a DOM fact.
212
- */
213
- var focus_scope_exports = /* @__PURE__ */ __exportAll({
214
- behavior: () => behavior$12,
215
- mount: () => FocusScope,
216
- tabbableWithin: () => tabbableWithin
217
- });
218
- /**
219
- * On insert the container focuses `initialFocus`, else its first tabbable
220
- * descendant, else itself; with `contain`, Tab and Shift+Tab wrap inside and
221
- * focus that lands outside comes back to the first tabbable; on unmount, with
222
- * `restore`, focus returns to the element that had it. A dialog that is not
223
- * a native `<dialog>`, a menu, or a command palette takes this.
224
- */
225
- const behavior$12 = (slots) => (options) => Behavior.forSlots(slots)({ [options.container]: Behavior.slot({
226
- requires: { capability: Capability.Container },
227
- mount: () => FocusScope({
228
- contain: options.contain ?? true,
229
- restore: options.restore ?? true,
230
- initialFocus: options.initialFocus ?? null
231
- })
232
- }) }, { name: "FocusScope" });
233
- // src/interaction/focus-visible.ts
234
- var focus_visible_exports = /* @__PURE__ */ __exportAll({
235
- Modality: () => Modality,
236
- behavior: () => behavior$11,
237
- bundle: () => InputModality,
238
- modalityOfKey: () => modalityOfKey
239
- });
240
- const behavior$11 = (declared) => (slots) => (options) => Behavior.forSlots(slots)({ [options.target]: Behavior.slot({
241
- requires: { capability: Capability.Focusable },
242
- attributes: ({ input, h }) => input[declared.field].modality === "keyboard" ? [h.DataAttribute("focus-visible", "true")] : []
243
- }) }, { name: "FocusVisible" });
244
- // src/interaction/roving-tabindex.ts
245
- /**
246
- * One tab stop for a set of items (a toolbar, a tab list, a listbox): arrows
247
- * move focus between them, the rest stay out of the tab order. The current
248
- * item's id is the Model slice, so a reorder keeps the same item current and a
249
- * resumed page knows where focus was. Which item comes next is a pure
250
- * function of the items and the key; the Behavior wires it to the slots.
251
- */
252
- var roving_tabindex_exports = /* @__PURE__ */ __exportAll({
253
- Args: () => Args$8,
254
- Message: () => Message$8,
255
- Model: () => Model$8,
256
- Orientation: () => Orientation,
257
- behavior: () => behavior$10,
258
- bundle: () => bundle$8,
259
- idSelector: () => idSelector,
260
- itemAttributes: () => itemAttributes,
261
- move: () => move$2,
262
- tabStop: () => tabStop$1
263
- });
264
- const Orientation = Schema.Literals([
265
- "vertical",
266
- "horizontal",
267
- "both"
268
- ]);
269
- const Model$8 = Schema.Struct({
270
- /** The current item's id, or `null` before any item has been focused. */
271
- current: Schema.NullOr(Schema.String) });
272
- const Message$8 = defineMessageUnion({
273
- /** An item became current: the user focused it, or a key moved there. */
274
- Focused: { id: Schema.String } });
275
- const Args$8 = Schema.Struct({
276
- orientation: Orientation,
277
- /** Wrap from the last enabled item to the first, and back. */
278
- loop: Schema.Boolean,
279
- /** Keep DOM focus on the container and point at the current item with
280
- * `aria-activedescendant` instead of moving focus. */
281
- virtual: Schema.Boolean
282
- });
283
- const bundle$8 = Bundle.make("RovingTabindex", {
284
- Model: Model$8,
285
- Message: Message$8,
286
- args: Args$8,
287
- init: () => ({ model: { current: null } }),
288
- update: (model, message) => model.current === message.id ? { model } : { model: { current: message.id } }
289
- });
290
- const isPlain$1 = (modifiers) => !modifiers.ctrlKey && !modifiers.altKey && !modifiers.metaKey;
291
- /**
292
- * The index the key moves to from `current`, over the enabled indices only, or
293
- * `undefined` when the key is not a navigation key here. `current` may be
294
- * `-1` (nothing current yet): arrows then land on the first or last enabled
295
- * item. Under `rtl`, left and right swap. PageUp and PageDown move by `page`
296
- * and clamp at the ends, never wrapping.
297
- */
298
- const move$2 = (enabled, current, key, modifiers, options) => {
299
- if (enabled.length === 0 || !isPlain$1(modifiers)) return void 0;
300
- const first = enabled[0];
301
- const last = enabled[enabled.length - 1];
302
- if (key === "Home") return first;
303
- if (key === "End") return last;
304
- if (options.page !== void 0 && (key === "PageUp" || key === "PageDown")) {
305
- const position = enabled.indexOf(current);
306
- if (position === -1) return key === "PageDown" ? first : last;
307
- const jumped = position + (key === "PageDown" ? options.page : -options.page);
308
- return enabled[Math.min(enabled.length - 1, Math.max(0, jumped))];
309
- }
310
- const vertical = options.orientation !== "horizontal";
311
- const horizontal = options.orientation !== "vertical";
312
- const forwardKey = options.direction === "rtl" ? "ArrowLeft" : "ArrowRight";
313
- const backwardKey = options.direction === "rtl" ? "ArrowRight" : "ArrowLeft";
314
- const step = vertical && key === "ArrowDown" || horizontal && key === forwardKey ? 1 : vertical && key === "ArrowUp" || horizontal && key === backwardKey ? -1 : 0;
315
- if (step === 0) return void 0;
316
- const position = enabled.indexOf(current);
317
- if (position === -1) return step === 1 ? first : last;
318
- const next = position + step;
319
- if (next < 0) return options.loop ? last : first;
320
- if (next >= enabled.length) return options.loop ? first : last;
321
- return enabled[next];
322
- };
323
- /** The item that holds the tab stop: the current one, else the first enabled. */
324
- const tabStop$1 = (items, current) => {
325
- const index = current === null ? -1 : items.indexOf(current);
326
- if (index !== -1 && !items.isDisabled(index)) return index;
327
- return items.enabled[0] ?? -1;
328
- };
329
- /** A selector for an element by id, safe for any id. */
330
- const idSelector = (id) => `[id="${id.replace(/["\\]/g, "\\$&")}"]`;
331
- /**
332
- * The attributes one item gets: `tabindex` 0 when it holds the tab stop and
333
- * -1 otherwise (none under `virtual`), and `OnFocus` reporting it current.
334
- * Shared with `ListNavigation`.
335
- */
336
- const itemAttributes = (h, items, current, item, virtual, focused) => {
337
- const id = item.id ?? items.ids[item.index];
338
- if (id === void 0) return [];
339
- const isStop = tabStop$1(items, current) === item.index;
340
- return [...virtual ? [] : [h.Tabindex(isStop ? 0 : -1)], h.OnFocus(focused(id))];
341
- };
342
- /**
343
- * Wires a placed `RovingTabindex` to the slots: the container's arrow, Home
344
- * and End keys focus the next item (synchronously, then dispatch), each item
345
- * gets `tabindex` 0 or -1 and reports `Focused` when focused. Under `virtual`
346
- * DOM focus never leaves the container: the key only dispatches, the
347
- * container carries `aria-activedescendant`, and items keep no `tabindex`.
348
- *
349
- * Every handled key is default-prevented. Nothing is written on dispose: the
350
- * attributes are data, so a view that no longer attaches this leaves no
351
- * `tabindex` behind.
352
- */
353
- const behavior$10 = (declared, args) => (slots) => (options) => {
354
- const wrap = (id) => declared.wrapper.make(Message$8.Focused({ id }));
355
- const slice = (input) => input[declared.field];
356
- const itemsOf = perInput(options.items);
357
- return Behavior.forSlots(slots)({
358
- [options.container]: Behavior.slot({
359
- requires: { capability: Capability.Interactive },
360
- attributes: ({ input, h }) => {
361
- const items = itemsOf(input);
362
- const current = slice(input).current;
363
- const from = current === null ? -1 : items.indexOf(current);
364
- const direction = options.direction?.(input) ?? "ltr";
365
- const stop = tabStop$1(items, current);
366
- const stopId = stop === -1 ? void 0 : items.ids[stop];
367
- const target = (key, modifiers) => {
368
- const next = move$2(items.enabled, from, key, modifiers, {
369
- ...args,
370
- direction
371
- });
372
- return next === void 0 ? void 0 : items.ids[next];
373
- };
374
- if (args.virtual) return [h.OnKeyDownPreventDefault((key, modifiers) => Option.map(Option.fromNullishOr(target(key, modifiers)), wrap)), ...stopId === void 0 ? [] : [h.AriaActiveDescendant(stopId)]];
375
- return [h.OnKeyDownFocus((key, modifiers) => Option.map(Option.fromNullishOr(target(key, modifiers)), (id) => ({
376
- focusSelector: idSelector(id),
377
- message: wrap(id)
378
- })))];
379
- }
380
- }),
381
- [options.item]: Behavior.slot({
382
- requires: { capability: Capability.Focusable },
383
- attributes: ({ input, h, item }) => {
384
- if (item === void 0) return [];
385
- return itemAttributes(h, itemsOf(input), slice(input).current, item, args.virtual, wrap);
386
- }
387
- })
388
- }, { name: "RovingTabindex" });
389
- };
390
- // src/interaction/grid-navigation.ts
391
- /**
392
- * Two-dimensional arrow keys over a set of items laid out in rows of
393
- * `columns` (a calendar grid, a color swatch picker, an emoji palette). The
394
- * Model slice is the current item's id, as with `RovingTabindex`, so a
395
- * reorder keeps the same item current. Which cell a key lands on is the pure
396
- * `move`; the Behavior wires it to a container slot and a per-item slot and
397
- * shares `RovingTabindex`'s item attributes, so the two are interchangeable
398
- * on a view.
399
- */
400
- var grid_navigation_exports = /* @__PURE__ */ __exportAll({
401
- Args: () => Args$7,
402
- Message: () => Message$7,
403
- Model: () => Model$7,
404
- behavior: () => behavior$9,
405
- bundle: () => bundle$7,
406
- move: () => move$1
407
- });
408
- const Model$7 = Schema.Struct({
409
- /** The current cell's id, or `null` before any cell has been focused. */
410
- current: Schema.NullOr(Schema.String) });
411
- const Message$7 = defineMessageUnion({
412
- /** A cell became current: the user focused it, or a key moved there. */
413
- Focused: { id: Schema.String } });
414
- const Args$7 = Schema.Struct({
415
- /** Cells per row; the last row may be short. */
416
- columns: Schema.Number.pipe(Schema.check(Schema.isGreaterThanOrEqualTo(1))),
417
- /** Left and right continue into the previous and next row; up and down
418
- * continue into the previous and next column. Off, they stop at the edge. */
419
- wrap: Schema.Boolean,
420
- /** Keep DOM focus on the container and point at the current cell with
421
- * `aria-activedescendant` instead of moving focus. */
422
- virtual: Schema.Boolean
423
- });
424
- const bundle$7 = Bundle.make("GridNavigation", {
425
- Model: Model$7,
426
- Message: Message$7,
427
- args: Args$7,
428
- init: () => ({ model: { current: null } }),
429
- update: (model, message) => model.current === message.id ? { model } : { model: { current: message.id } }
430
- });
431
- /**
432
- * The index the key moves to from `current` over a grid of `count` cells in
433
- * rows of `columns`, skipping cells not in `enabled`, or `undefined` when the
434
- * key is not a grid key. Left and right step within the row, and across rows
435
- * under `wrap`; up and down step within the column, and across columns under
436
- * `wrap`. Home and End are the row's first and last enabled cell; with Ctrl
437
- * they are the grid's. At an edge without `wrap` the key is consumed and
438
- * `current` is returned. From `-1` (nothing current) a forward key lands on
439
- * the first enabled cell and a backward key on the last. Under `rtl`, left
440
- * and right swap.
441
- */
442
- const move$1 = (enabled, count, current, key, modifiers, options) => {
443
- if (enabled.length === 0 || modifiers.altKey || modifiers.metaKey) return void 0;
444
- const first = enabled[0];
445
- const last = enabled[enabled.length - 1];
446
- const isEnabled = (index) => enabled.includes(index);
447
- const { columns } = options;
448
- const row = (index) => Math.floor(index / columns);
449
- if (key === "Home" || key === "End") {
450
- if (modifiers.ctrlKey || current === -1) return key === "Home" ? first : last;
451
- const inRow = enabled.filter((index) => row(index) === row(current));
452
- return key === "Home" ? inRow[0] ?? current : inRow[inRow.length - 1] ?? current;
453
- }
454
- if (modifiers.ctrlKey) return void 0;
455
- const forwardKey = options.direction === "rtl" ? "ArrowLeft" : "ArrowRight";
456
- const backwardKey = options.direction === "rtl" ? "ArrowRight" : "ArrowLeft";
457
- const axis = key === forwardKey || key === backwardKey ? "row" : key === "ArrowDown" || key === "ArrowUp" ? "column" : void 0;
458
- if (axis === void 0) return void 0;
459
- const step = key === forwardKey || key === "ArrowDown" ? 1 : -1;
460
- if (current === -1) return step === 1 ? first : last;
461
- if (axis === "row") {
462
- for (let index = current + step; index >= 0 && index < count; index += step) {
463
- if (!options.wrap && row(index) !== row(current)) return current;
464
- if (isEnabled(index)) return index;
465
- }
466
- return current;
467
- }
468
- const column = current % columns;
469
- for (let index = current + step * columns;; index += step * columns) {
470
- if (index < 0 || index >= count) {
471
- if (!options.wrap) return current;
472
- const nextColumn = column + step;
473
- if (nextColumn < 0 || nextColumn >= columns) return current;
474
- const start = step === 1 ? nextColumn : nextColumn + columns * Math.floor((count - 1 - nextColumn) / columns);
475
- for (let next = start; next >= 0 && next < count; next += step * columns) if (isEnabled(next)) return next;
476
- return current;
477
- }
478
- if (isEnabled(index)) return index;
479
- }
480
- };
481
- /**
482
- * Wires a placed `GridNavigation` to the slots exactly as `RovingTabindex`
483
- * does, with `move` above deciding the cell: the container's arrows, Home and
484
- * End focus the next cell (synchronously, then dispatch) or, under `virtual`,
485
- * only dispatch and point with `aria-activedescendant`; each cell gets
486
- * `tabindex` 0 or -1 and reports `Focused` when focused.
487
- */
488
- const behavior$9 = (declared, args) => (slots) => (options) => {
489
- const wrap = (id) => declared.wrapper.make(Message$7.Focused({ id }));
490
- const slice = (input) => input[declared.field];
491
- const itemsOf = perInput(options.items);
492
- return Behavior.forSlots(slots)({
493
- [options.container]: Behavior.slot({
494
- requires: { capability: Capability.Interactive },
495
- attributes: ({ input, h }) => {
496
- const items = itemsOf(input);
497
- const current = slice(input).current;
498
- const from = current === null ? -1 : items.indexOf(current);
499
- const direction = options.direction?.(input) ?? "ltr";
500
- const stop = tabStop$1(items, current);
501
- const stopId = stop === -1 ? void 0 : items.ids[stop];
502
- const target = (key, modifiers) => {
503
- const next = move$1(items.enabled, items.size, from, key, modifiers, {
504
- columns: args.columns,
505
- wrap: args.wrap,
506
- direction
507
- });
508
- return next === void 0 ? void 0 : items.ids[next];
509
- };
510
- if (args.virtual) return [h.OnKeyDownPreventDefault((key, modifiers) => Option.map(Option.fromNullishOr(target(key, modifiers)), wrap)), ...stopId === void 0 ? [] : [h.AriaActiveDescendant(stopId)]];
511
- return [h.OnKeyDownFocus((key, modifiers) => Option.map(Option.fromNullishOr(target(key, modifiers)), (id) => ({
512
- focusSelector: idSelector(id),
513
- message: wrap(id)
514
- })))];
515
- }
516
- }),
517
- [options.item]: Behavior.slot({
518
- requires: { capability: Capability.Focusable },
519
- attributes: ({ input, h, item }) => {
520
- if (item === void 0) return [];
521
- return itemAttributes(h, itemsOf(input), slice(input).current, item, args.virtual, wrap);
522
- }
523
- })
524
- }, { name: "GridNavigation" });
525
- };
526
- // src/interaction/layers.ts
527
- var layers_exports = /* @__PURE__ */ __exportAll({
528
- hideOutside: () => hideOutside,
529
- hideOutsideMount: () => HideOutside,
530
- scrollLock: () => scrollLock,
531
- scrollLockMount: () => ScrollLock
532
- });
533
- const mountOn = (name, mount) => (slots) => (options) => Behavior.forSlots(slots)({ [options.container]: Behavior.slot({
534
- requires: { capability: Capability.Container },
535
- mount
536
- }) }, { name });
537
- /** Locks the document's scroll while the container is mounted; nested locks release together. */
538
- const scrollLock = mountOn("ScrollLock", () => ScrollLock());
539
- /** Marks everything outside the container inert while it is mounted. */
540
- const hideOutside = mountOn("HideOutside", () => HideOutside());
541
- // src/interaction/live-announce.ts
542
- /**
543
- * Announcements for assistive technology: one Bundle, placed once, holds the
544
- * text of a polite and an assertive live region. An announcement waits
545
- * `debounceMs` so a burst reads once, then clears after `clearAfterMs`, both
546
- * on Effect's clock with a generation so a superseded timer changes nothing.
547
- * The same text twice gets a trailing no-break space toggled, which is what
548
- * makes a screen reader read it again. `view` renders the two regions; put it
549
- * once in the page and style them visually hidden.
550
- */
551
- var live_announce_exports = /* @__PURE__ */ __exportAll({
552
- Args: () => Args$6,
553
- Message: () => Message$6,
554
- Model: () => Model$6,
555
- Politeness: () => Politeness,
556
- REGION_ATTRIBUTE: () => REGION_ATTRIBUTE,
557
- bundle: () => bundle$6,
558
- reread: () => reread,
559
- say: () => say,
560
- view: () => view
561
- });
562
- const Politeness = Schema.Literals(["polite", "assertive"]);
563
- const Model$6 = Schema.Struct({
564
- polite: Schema.String,
565
- assertive: Schema.String,
566
- /** Waiting for the debounce to read. */
567
- pending: Schema.NullOr(Schema.Struct({
568
- message: Schema.String,
569
- politeness: Politeness
570
- })),
571
- generation: Schema.Number
572
- });
573
- const Message$6 = defineMessageUnion({
574
- Announced: {
575
- message: Schema.String,
576
- politeness: Politeness
577
- },
578
- Read: { generation: Schema.Number },
579
- Cleared: { generation: Schema.Number }
580
- });
581
- const positive = Schema.Number.pipe(Schema.check(Schema.isGreaterThanOrEqualTo(0)), Schema.check(Schema.isFinite()));
582
- const Args$6 = Schema.Struct({
583
- /** A burst of announcements inside this window reads once, the last one. */
584
- debounceMs: positive,
585
- /** How long the text stays in the region. */
586
- clearAfterMs: positive
587
- });
588
- const NBSP = "\xA0";
589
- /** The text to put in the region so that `next` is read even when it equals `current`. */
590
- const reread = (current, next) => current === next ? `${next}${NBSP}` : current === `${next}${NBSP}` ? next : next;
591
- const after = (ms, message, name, generation) => ({
592
- name,
593
- args: { generation },
594
- effect: Effect.as(Effect.sleep(ms), message)
595
- });
596
- const bundle$6 = Bundle.make("LiveAnnounce", {
597
- Model: Model$6,
598
- Message: Message$6,
599
- args: Args$6,
600
- init: () => ({ model: {
601
- polite: "",
602
- assertive: "",
603
- pending: null,
604
- generation: 0
605
- } }),
606
- update: (model, message, args) => Message$6.match(message, {
607
- Announced: ({ message: text, politeness }) => {
608
- const generation = model.generation + 1;
609
- return {
610
- model: {
611
- ...model,
612
- pending: {
613
- message: text,
614
- politeness
615
- },
616
- generation
617
- },
618
- commands: [after(args.debounceMs, Message$6.Read({ generation }), "LiveAnnounce.read", generation)]
619
- };
620
- },
621
- Read: ({ generation }) => {
622
- if (generation !== model.generation || model.pending === null) return { model };
623
- const { message: text, politeness } = model.pending;
624
- const region = politeness === "polite" ? "polite" : "assertive";
625
- return {
626
- model: {
627
- ...model,
628
- pending: null,
629
- [region]: reread(model[region], text)
630
- },
631
- commands: [after(args.clearAfterMs, Message$6.Cleared({ generation }), "LiveAnnounce.clear", generation)]
632
- };
633
- },
634
- Cleared: ({ generation }) => generation === model.generation ? { model: {
635
- ...model,
636
- polite: "",
637
- assertive: ""
638
- } } : { model }
639
- })
640
- });
641
- /** The parent Message that announces `text`, to return from `update` or `onOut`. */
642
- const say = (declared) => (text, politeness = "polite") => declared.wrapper.make(Message$6.Announced({
643
- message: text,
644
- politeness
645
- }));
646
- const REGION_ATTRIBUTE = "data-foldkit-plus-live";
647
- /**
648
- * The two live regions. Render once, anywhere in the page, and hide them
649
- * visually with a rule on `[data-foldkit-plus-live]`; they must stay in the
650
- * accessibility tree, so no `display: none`.
651
- */
652
- const view = (model, h) => h.div([h.Attribute(REGION_ATTRIBUTE, "regions")], [h.div([
653
- h.Attribute(REGION_ATTRIBUTE, "polite"),
654
- h.AriaLive("polite"),
655
- h.AriaAtomic(true)
656
- ], [model.polite]), h.div([
657
- h.Attribute(REGION_ATTRIBUTE, "assertive"),
658
- h.AriaLive("assertive"),
659
- h.AriaAtomic(true)
660
- ], [model.assertive])]);
661
- // src/interaction/typeahead.ts
662
- /**
663
- * Typeahead over a set of items: printable keys build a query that a timer
664
- * clears, and the query picks the item to move to. The query lives in the
665
- * Model so a replay sees the same picks; the timer runs on Effect's clock so
666
- * TestClock drives it. Which item matches is a pure function.
667
- */
668
- var typeahead_exports = /* @__PURE__ */ __exportAll({
669
- Args: () => Args$5,
670
- Message: () => Message$5,
671
- Model: () => Model$5,
672
- behavior: () => behavior$8,
673
- bundle: () => bundle$5,
674
- isPrintable: () => isPrintable,
675
- match: () => match
676
- });
677
- const Model$5 = Schema.Struct({
678
- query: Schema.String,
679
- /** Bumped per keystroke, carried by the clear it schedules, so a superseded
680
- * timer's `Expired` changes nothing. */
681
- generation: Schema.Number
682
- });
683
- const Message$5 = defineMessageUnion({
684
- /** A printable key was pressed with no ctrl, alt or meta. */
685
- Typed: { char: Schema.String },
686
- /** The clear timer fired for `generation`. */
687
- Expired: { generation: Schema.Number },
688
- /** The query was dropped on purpose (a blur, a selection). */
689
- Cleared: {}
690
- });
691
- const Args$5 = Schema.Struct({
692
- /** How long the query survives after the last key. */
693
- timeoutMs: Schema.Number.pipe(Schema.check(Schema.isGreaterThan(0)), Schema.check(Schema.isFinite())) });
694
- const bundle$5 = Bundle.make("Typeahead", {
695
- Model: Model$5,
696
- Message: Message$5,
697
- args: Args$5,
698
- init: () => ({ model: {
699
- query: "",
700
- generation: 0
701
- } }),
702
- update: (model, message, args) => Message$5.match(message, {
703
- Typed: ({ char }) => {
704
- const generation = model.generation + 1;
705
- return {
706
- model: {
707
- query: model.query + char,
708
- generation
709
- },
710
- commands: [{
711
- name: "Typeahead.expire",
712
- args: { generation },
713
- effect: Effect.as(Effect.sleep(args.timeoutMs), Message$5.Expired({ generation }))
714
- }]
715
- };
716
- },
717
- Expired: ({ generation }) => generation === model.generation ? clearQuery(model) : { model },
718
- Cleared: () => clearQuery(model)
719
- })
720
- });
721
- const normalize = (text) => text.trimStart().toLocaleLowerCase();
722
- const isRepeated = (query) => query.length > 1 && [...query].every((char) => char === query[0]);
723
- /**
724
- * The enabled index whose text starts with `query`, searching forward from
725
- * `current` and wrapping, or `undefined`. A query of one character, or one
726
- * character repeated (`aaa`), starts *after* the current item so repeated
727
- * presses cycle through the items that begin with it; a longer query starts
728
- * *at* the current item, since the user is refining a match. Case and leading
729
- * whitespace are ignored.
730
- */
731
- const match = (texts, enabled, query, current) => {
732
- if (query === "" || enabled.length === 0) return void 0;
733
- const needle = normalize(isRepeated(query) ? query[0] : query);
734
- const position = enabled.indexOf(current);
735
- const startAfter = needle.length === 1 || isRepeated(query);
736
- const start = position === -1 ? 0 : startAfter ? position + 1 : position;
737
- for (let offset = 0; offset < enabled.length; offset += 1) {
738
- const index = enabled[(start + offset) % enabled.length];
739
- if (normalize(texts[index] ?? "").startsWith(needle)) return index;
740
- }
741
- };
742
- /** A single character with no ctrl, alt or meta: a typeahead key. */
743
- const isPrintable = (key, modifiers) => key.length === 1 && !modifiers.ctrlKey && !modifiers.altKey && !modifiers.metaKey;
744
- /**
745
- * Wires a placed `Typeahead` to the host slot: a printable key extends the
746
- * query, focuses the matching item by its id (which then reports itself
747
- * current, through `RovingTabindex`'s `OnFocus`), and dispatches `Typed`.
748
- * With no match the key still extends the query, so the next one can match;
749
- * a space with an empty query is left to the host (it activates).
750
- */
751
- const behavior$8 = (declared) => (slots) => (options) => {
752
- const wrap = (char) => declared.wrapper.make(Message$5.Typed({ char }));
753
- return Behavior.forSlots(slots)({ [options.host]: Behavior.slot({
754
- requires: { capability: Capability.Interactive },
755
- attributes: ({ input, h }) => {
756
- const items = options.items(input);
757
- const query = input[declared.field].query;
758
- const currentId = options.current(input);
759
- const current = currentId === null ? -1 : items.indexOf(currentId);
760
- return [h.OnKeyDownFocus((key, modifiers) => {
761
- if (!isPrintable(key, modifiers)) return Option.none();
762
- if (key === " " && query === "") return Option.none();
763
- const texts = items.ids.map((_, index) => options.text(input, index));
764
- const found = match(texts, items.enabled, query + key, current);
765
- const target = found === void 0 ? currentId : items.ids[found];
766
- return Option.some({
767
- focusSelector: target === null || target === void 0 ? ":not(*)" : idSelector(target),
768
- message: wrap(key)
769
- });
770
- })];
771
- }
772
- }) }, { name: "Typeahead" });
773
- };
774
- // src/interaction/list-navigation.ts
775
- /**
776
- * Keyboard navigation of a list in one placement: a roving tab stop, type to
777
- * find, and PageUp and PageDown. One Bundle holds the current item's id and
778
- * the typeahead query, so a printable key can move the pointer and extend the
779
- * query in one transition, which two placements cannot do under `virtual`.
780
- * The resolver allows one owner per event on a slot, so a host that needs
781
- * both arrows and typeahead takes this, not `RovingTabindex` plus `Typeahead`.
782
- */
783
- var list_navigation_exports = /* @__PURE__ */ __exportAll({
784
- Args: () => Args$4,
785
- Message: () => Message$4,
786
- Model: () => Model$4,
787
- behavior: () => behavior$7,
788
- bundle: () => bundle$4,
789
- keyOutcome: () => keyOutcome
790
- });
791
- const Model$4 = Schema.Struct({
792
- current: Schema.NullOr(Schema.String),
793
- query: Schema.String,
794
- generation: Schema.Number
795
- });
796
- const Message$4 = defineMessageUnion({
797
- /** An item became current: focused, or reached by a key. */
798
- Focused: { id: Schema.String },
799
- /** A printable key extended the query; `match` is the item it now picks, if any. */
800
- Typed: {
801
- char: Schema.String,
802
- match: Schema.NullOr(Schema.String)
803
- },
804
- Expired: { generation: Schema.Number },
805
- Cleared: {}
806
- });
807
- const Args$4 = Schema.Struct({
808
- orientation: Orientation,
809
- loop: Schema.Boolean,
810
- virtual: Schema.Boolean,
811
- /** How long the typeahead query survives after the last key. */
812
- timeoutMs: Schema.Number.pipe(Schema.check(Schema.isGreaterThan(0)), Schema.check(Schema.isFinite())),
813
- /** How many enabled items PageUp and PageDown move. */
814
- page: Schema.Number.pipe(Schema.check(Schema.isInt()), Schema.check(Schema.isGreaterThan(0)))
815
- });
816
- const bundle$4 = Bundle.make("ListNavigation", {
817
- Model: Model$4,
818
- Message: Message$4,
819
- args: Args$4,
820
- init: () => ({ model: {
821
- current: null,
822
- query: "",
823
- generation: 0
824
- } }),
825
- update: (model, message, args) => Message$4.match(message, {
826
- Focused: ({ id }) => model.current === id ? { model } : { model: {
827
- ...model,
828
- current: id
829
- } },
830
- Typed: ({ char, match }) => {
831
- const generation = model.generation + 1;
832
- return {
833
- model: {
834
- current: match ?? model.current,
835
- query: model.query + char,
836
- generation
837
- },
838
- commands: [{
839
- name: "ListNavigation.expire",
840
- args: { generation },
841
- effect: Effect.as(Effect.sleep(args.timeoutMs), Message$4.Expired({ generation }))
842
- }]
843
- };
844
- },
845
- Expired: ({ generation }) => generation === model.generation ? clearQuery(model) : { model },
846
- Cleared: () => clearQuery(model)
847
- })
848
- });
849
- const keyOutcome = (items, texts, state, key, modifiers, options) => {
850
- const from = state.current === null ? -1 : items.indexOf(state.current);
851
- const moved = move$2(items.enabled, from, key, modifiers, options);
852
- const movedId = moved === void 0 ? void 0 : items.ids[moved];
853
- if (movedId !== void 0) return {
854
- _tag: "Navigate",
855
- id: movedId
856
- };
857
- if (!isPrintable(key, modifiers)) return void 0;
858
- if (key === " " && state.query === "") return void 0;
859
- const found = match(texts, items.enabled, state.query + key, from);
860
- return {
861
- _tag: "Type",
862
- char: key,
863
- match: found === void 0 ? null : items.ids[found] ?? null
864
- };
865
- };
866
- /**
867
- * Wires a placed `ListNavigation` to the slots. The container gets one key
868
- * handler: arrows, Home, End, PageUp and PageDown move by id; a printable key
869
- * extends the query and moves to its match. Each item gets `tabindex` and
870
- * `OnFocus` as under `RovingTabindex`. Under `virtual` nothing takes DOM focus
871
- * and the container carries `aria-activedescendant`.
872
- */
873
- const behavior$7 = (declared, args) => (slots) => (options) => {
874
- const wrap = (message) => declared.wrapper.make(message);
875
- const focused = (id) => wrap(Message$4.Focused({ id }));
876
- const slice = (input) => input[declared.field];
877
- const itemsOf = perInput(options.items);
878
- const outcomeMessage = (outcome) => outcome._tag === "Navigate" ? focused(outcome.id) : wrap(Message$4.Typed({
879
- char: outcome.char,
880
- match: outcome.match
881
- }));
882
- return Behavior.forSlots(slots)({
883
- [options.container]: Behavior.slot({
884
- requires: { capability: Capability.Interactive },
885
- attributes: ({ input, h }) => {
886
- const items = itemsOf(input);
887
- const state = slice(input);
888
- const moveOptions = {
889
- orientation: args.orientation,
890
- loop: args.loop,
891
- direction: options.direction?.(input) ?? "ltr",
892
- page: args.page
893
- };
894
- const typeahead = options.typeahead !== false;
895
- const decide = (key, modifiers) => {
896
- const current = state.current;
897
- if (key === "Enter" && options.commit !== void 0 && !modifiers.shiftKey && !modifiers.ctrlKey && !modifiers.altKey && !modifiers.metaKey && current !== null) {
898
- const currentIndex = items.indexOf(current);
899
- if (currentIndex !== -1 && !items.isDisabled(currentIndex)) return Option.some({
900
- _tag: "Commit",
901
- id: current,
902
- message: options.commit(input, current)
903
- });
904
- }
905
- const result = keyOutcome(items, items.ids.map((_, index) => options.text(input, index)), state, key, modifiers, moveOptions);
906
- if (!typeahead && result !== void 0 && result._tag === "Type") return Option.none();
907
- if (result === void 0) return Option.none();
908
- return Option.some({
909
- _tag: "Key",
910
- outcome: result
911
- });
912
- };
913
- const messageOf = (decision) => {
914
- if (decision._tag === "Commit") return decision.message;
915
- return outcomeMessage(decision.outcome);
916
- };
917
- const stop = tabStop$1(items, state.current);
918
- const stopId = stop === -1 ? void 0 : items.ids[stop];
919
- if (args.virtual) return [h.OnKeyDownPreventDefault((key, modifiers) => Option.map(decide(key, modifiers), messageOf)), ...stopId === void 0 ? [] : [h.AriaActiveDescendant(stopId)]];
920
- return [h.OnKeyDownFocus((key, modifiers) => Option.map(decide(key, modifiers), (decision) => {
921
- if (decision._tag === "Commit") return {
922
- focusSelector: idSelector(decision.id),
923
- message: decision.message
924
- };
925
- const result = decision.outcome;
926
- const target = result._tag === "Navigate" ? result.id : result.match ?? state.current;
927
- return {
928
- focusSelector: target === null ? ":not(*)" : idSelector(target),
929
- message: outcomeMessage(result)
930
- };
931
- }))];
932
- }
933
- }),
934
- [options.item]: Behavior.slot({
935
- requires: { capability: Capability.Focusable },
936
- attributes: ({ input, h, item }) => item === void 0 ? [] : itemAttributes(h, itemsOf(input), slice(input).current, item, args.virtual, focused)
937
- })
938
- }, { name: "ListNavigation" });
939
- };
940
- // src/interaction/press.ts
941
- /**
942
- * Press: pointer and keyboard activation of one element as one fact. A Mount
943
- * reports what the element saw (pointer down, up, cancel, key down, up,
944
- * click) with the detail Foldkit's declarative attributes do not carry; the
945
- * Bundle's `update` decides what counts as a press; the ghost click that
946
- * follows a touch is suppressed by a timed Command on Effect's clock, never
947
- * by a clock read in a handler. Activation is an OutMessage, `Pressed`, so
948
- * the placement must say what a press does.
949
- */
950
- var press_exports = /* @__PURE__ */ __exportAll({
951
- Args: () => Args$3,
952
- Message: () => Message$3,
953
- Model: () => Model$3,
954
- PointerType: () => PointerType,
955
- Pressed: () => Pressed,
956
- behavior: () => behavior$6,
957
- bundle: () => bundle$3,
958
- events: () => events
959
- });
960
- const PointerType = Schema.Literals([
961
- "mouse",
962
- "touch",
963
- "pen",
964
- "keyboard",
965
- "virtual"
966
- ]);
967
- const Model$3 = Schema.Struct({
968
- /** Down and not yet released or cancelled. For styling. */
969
- pressed: Schema.Boolean,
970
- /** The pointer that pressed, so another pointer's up cannot release it. */
971
- pointerId: Schema.NullOr(Schema.Number),
972
- /** Enter or Space is down. */
973
- key: Schema.NullOr(Schema.String),
974
- /** A click arriving before this generation's window ends is the ghost of a pointer press. */
975
- suppressing: Schema.NullOr(Schema.Number),
976
- generation: Schema.Number
977
- });
978
- const Message$3 = defineMessageUnion({
979
- PointerDown: {
980
- pointerId: Schema.Number,
981
- button: Schema.Number,
982
- pointerType: Schema.String
983
- },
984
- PointerUp: {
985
- pointerId: Schema.Number,
986
- pointerType: Schema.String,
987
- shiftKey: Schema.Boolean
988
- },
989
- PointerCancelled: { pointerId: Schema.Number },
990
- KeyDown: {
991
- key: Schema.String,
992
- repeat: Schema.Boolean
993
- },
994
- KeyUp: {
995
- key: Schema.String,
996
- shiftKey: Schema.Boolean
997
- },
998
- /** `detail` is 0 for a click no pointer made: keyboard on a native control, or assistive technology. */
999
- Clicked: {
1000
- detail: Schema.Number,
1001
- shiftKey: Schema.Boolean
1002
- },
1003
- Unsuppressed: { generation: Schema.Number }
1004
- });
1005
- const Pressed = Schema.TaggedStruct("Pressed", {
1006
- pointerType: PointerType,
1007
- /** Shift was held, for a range selection. */
1008
- shiftKey: Schema.Boolean
1009
- });
1010
- const Args$3 = Schema.Struct({
1011
- /** How long after a pointer press a `click` is taken for its ghost. */
1012
- clickSuppressionMs: Schema.Number.pipe(Schema.check(Schema.isGreaterThanOrEqualTo(0)), Schema.check(Schema.isFinite())) });
1013
- const idle = (model) => ({
1014
- ...model,
1015
- pressed: false,
1016
- pointerId: null,
1017
- key: null
1018
- });
1019
- const isActivationKey$1 = (key) => key === "Enter" || key === " ";
1020
- const asPointerType = (raw) => raw === "touch" || raw === "pen" ? raw : "mouse";
1021
- const bundle$3 = Bundle.make("Press", {
1022
- Model: Model$3,
1023
- Message: Message$3,
1024
- args: Args$3,
1025
- init: () => ({ model: {
1026
- pressed: false,
1027
- pointerId: null,
1028
- key: null,
1029
- suppressing: null,
1030
- generation: 0
1031
- } }),
1032
- update: (model, message, args) => Message$3.match(message, {
1033
- PointerDown: ({ pointerId, button }) => button !== 0 || model.pointerId !== null ? { model } : { model: {
1034
- ...model,
1035
- pressed: true,
1036
- pointerId
1037
- } },
1038
- PointerUp: ({ pointerId, pointerType, shiftKey }) => {
1039
- if (model.pointerId !== pointerId) return { model };
1040
- const generation = model.generation + 1;
1041
- return {
1042
- model: {
1043
- ...idle(model),
1044
- suppressing: generation,
1045
- generation
1046
- },
1047
- commands: [{
1048
- name: "Press.unsuppress",
1049
- args: { generation },
1050
- effect: Effect.as(Effect.sleep(args.clickSuppressionMs), Message$3.Unsuppressed({ generation }))
1051
- }],
1052
- outMessage: Pressed.make({
1053
- pointerType: asPointerType(pointerType),
1054
- shiftKey
1055
- })
1056
- };
1057
- },
1058
- PointerCancelled: ({ pointerId }) => model.pointerId === pointerId ? { model: idle(model) } : { model },
1059
- KeyDown: ({ key, repeat }) => !isActivationKey$1(key) || repeat || model.key !== null || model.pointerId !== null ? { model } : { model: {
1060
- ...model,
1061
- pressed: true,
1062
- key
1063
- } },
1064
- KeyUp: ({ key, shiftKey }) => model.key === key ? {
1065
- model: idle(model),
1066
- outMessage: Pressed.make({
1067
- pointerType: "keyboard",
1068
- shiftKey
1069
- })
1070
- } : { model },
1071
- Clicked: ({ detail, shiftKey }) => detail === 0 ? {
1072
- model,
1073
- outMessage: Pressed.make({
1074
- pointerType: "virtual",
1075
- shiftKey
1076
- })
1077
- } : model.suppressing !== null ? { model } : {
1078
- model,
1079
- outMessage: Pressed.make({
1080
- pointerType: "mouse",
1081
- shiftKey
1082
- })
1083
- },
1084
- Unsuppressed: ({ generation }) => model.suppressing === generation ? { model: {
1085
- ...model,
1086
- suppressing: null
1087
- } } : { model }
1088
- })
1089
- });
1090
- /**
1091
- * The element's press-relevant events as facts. Enter and Space key downs
1092
- * are default-prevented so a native control does not also click, and Space
1093
- * does not scroll; the Bundle owns activation. An element marked
1094
- * `aria-disabled="true"` (which the Behavior writes) reports nothing, read
1095
- * at event time so a toggle needs no remount. Nothing else is decided here.
1096
- */
1097
- const events = Mount.defineStream("PressEvents", {
1098
- messages: [
1099
- Message$3.PointerDown,
1100
- Message$3.PointerUp,
1101
- Message$3.PointerCancelled,
1102
- Message$3.KeyDown,
1103
- Message$3.KeyUp,
1104
- Message$3.Clicked
1105
- ],
1106
- execute: ({ element }) => Stream.callback((queue) => Effect.acquireRelease(Effect.sync(() => {
1107
- const offer = (message) => {
1108
- if (element.getAttribute("aria-disabled") === "true") return;
1109
- Queue.offerUnsafe(queue, message);
1110
- };
1111
- const listeners = [
1112
- ["pointerdown", (event) => {
1113
- const pointer = event;
1114
- offer(Message$3.PointerDown({
1115
- pointerId: pointer.pointerId ?? 0,
1116
- button: pointer.button ?? 0,
1117
- pointerType: pointer.pointerType ?? "mouse"
1118
- }));
1119
- }],
1120
- ["pointerup", (event) => {
1121
- const pointer = event;
1122
- offer(Message$3.PointerUp({
1123
- pointerId: pointer.pointerId ?? 0,
1124
- pointerType: pointer.pointerType ?? "mouse",
1125
- shiftKey: pointer.shiftKey ?? false
1126
- }));
1127
- }],
1128
- ["pointerleave", (event) => offer(Message$3.PointerCancelled({ pointerId: event.pointerId ?? 0 }))],
1129
- ["pointercancel", (event) => offer(Message$3.PointerCancelled({ pointerId: event.pointerId ?? 0 }))],
1130
- ["keydown", (event) => {
1131
- const key = event;
1132
- if (isActivationKey$1(key.key)) key.preventDefault();
1133
- offer(Message$3.KeyDown({
1134
- key: key.key,
1135
- repeat: key.repeat
1136
- }));
1137
- }],
1138
- ["keyup", (event) => {
1139
- const key = event;
1140
- offer(Message$3.KeyUp({
1141
- key: key.key,
1142
- shiftKey: key.shiftKey
1143
- }));
1144
- }],
1145
- ["click", (event) => {
1146
- const click = event;
1147
- offer(Message$3.Clicked({
1148
- detail: click.detail ?? 0,
1149
- shiftKey: click.shiftKey ?? false
1150
- }));
1151
- }]
1152
- ];
1153
- for (const [type, listener] of listeners) element.addEventListener(type, listener);
1154
- return listeners;
1155
- }), (listeners) => Effect.sync(() => {
1156
- for (const [type, listener] of listeners) element.removeEventListener(type, listener);
1157
- })))
1158
- });
1159
- /**
1160
- * Attaches the events Mount to the target slot, mapped into the placement's
1161
- * Messages, and writes `data-pressed` while down for styling. The element
1162
- * decides nothing; a click, a touch, Enter, Space, or an assistive
1163
- * technology's click all reach `update` and come out as one `Pressed`.
1164
- */
1165
- const behavior$6 = (declared) => (slots) => (options) => BundleBehavior.fromBundle(declared)(slots)({ [options.target]: {
1166
- requires: { capability: Capability.Interactive },
1167
- attributes: ({ input, h }) => [...input[declared.field].pressed ? [h.DataAttribute("pressed", "true")] : [], ...options.disabled?.(input) === true ? [h.AriaDisabled(true)] : []],
1168
- events: events()
1169
- } }, { name: "Press" });
1170
- // src/interaction/long-press.ts
1171
- /**
1172
- * LongPress: holding an element down for `thresholdMs` is one fact. It reads
1173
- * the same facts `Press.events` reports; the threshold is a Command on
1174
- * Effect's clock carrying a generation, so a release before it fires makes
1175
- * its `Elapsed` a no-op. `LongPressed` is an OutMessage the placement must
1176
- * handle. A short press is `Press`'s business, not this Bundle's.
1177
- */
1178
- var long_press_exports = /* @__PURE__ */ __exportAll({
1179
- Args: () => Args$2,
1180
- LongPressed: () => LongPressed,
1181
- Message: () => Message$2,
1182
- Model: () => Model$2,
1183
- behavior: () => behavior$5,
1184
- bundle: () => bundle$2
1185
- });
1186
- const Model$2 = Schema.Struct({
1187
- /** Down, and the threshold has not passed. */
1188
- holding: Schema.Boolean,
1189
- pointerId: Schema.NullOr(Schema.Number),
1190
- key: Schema.NullOr(Schema.String),
1191
- generation: Schema.Number
1192
- });
1193
- const Message$2 = defineMessageUnion({
1194
- PointerDown: {
1195
- pointerId: Schema.Number,
1196
- button: Schema.Number,
1197
- pointerType: Schema.String
1198
- },
1199
- PointerUp: {
1200
- pointerId: Schema.Number,
1201
- pointerType: Schema.String,
1202
- shiftKey: Schema.Boolean
1203
- },
1204
- PointerCancelled: { pointerId: Schema.Number },
1205
- KeyDown: {
1206
- key: Schema.String,
1207
- repeat: Schema.Boolean
1208
- },
1209
- KeyUp: {
1210
- key: Schema.String,
1211
- shiftKey: Schema.Boolean
1212
- },
1213
- Clicked: {
1214
- detail: Schema.Number,
1215
- shiftKey: Schema.Boolean
1216
- },
1217
- Elapsed: {
1218
- generation: Schema.Number,
1219
- pointerType: PointerType
1220
- }
1221
- });
1222
- const LongPressed = Schema.TaggedStruct("LongPressed", { pointerType: PointerType });
1223
- const Args$2 = Schema.Struct({ thresholdMs: Schema.Number.pipe(Schema.check(Schema.isGreaterThan(0)), Schema.check(Schema.isFinite())) });
1224
- const isActivationKey = (key) => key === "Enter" || key === " ";
1225
- const released = (model) => ({
1226
- ...model,
1227
- holding: false,
1228
- pointerId: null,
1229
- key: null
1230
- });
1231
- const start = (model, args, by, pointerType) => {
1232
- const generation = model.generation + 1;
1233
- return {
1234
- model: {
1235
- holding: true,
1236
- pointerId: "pointerId" in by ? by.pointerId : null,
1237
- key: "key" in by ? by.key : null,
1238
- generation
1239
- },
1240
- commands: [{
1241
- name: "LongPress.elapse",
1242
- args: { generation },
1243
- effect: Effect.as(Effect.sleep(args.thresholdMs), Message$2.Elapsed({
1244
- generation,
1245
- pointerType
1246
- }))
1247
- }]
1248
- };
1249
- };
1250
- const bundle$2 = Bundle.make("LongPress", {
1251
- Model: Model$2,
1252
- Message: Message$2,
1253
- args: Args$2,
1254
- init: () => ({ model: {
1255
- holding: false,
1256
- pointerId: null,
1257
- key: null,
1258
- generation: 0
1259
- } }),
1260
- update: (model, message, args) => Message$2.match(message, {
1261
- PointerDown: ({ pointerId, button, pointerType }) => button !== 0 || model.holding ? { model } : start(model, args, { pointerId }, pointerType === "touch" || pointerType === "pen" ? pointerType : "mouse"),
1262
- PointerUp: ({ pointerId }) => model.pointerId === pointerId ? { model: released(model) } : { model },
1263
- PointerCancelled: ({ pointerId }) => model.pointerId === pointerId ? { model: released(model) } : { model },
1264
- KeyDown: ({ key, repeat }) => !isActivationKey(key) || repeat || model.holding ? { model } : start(model, args, { key }, "keyboard"),
1265
- KeyUp: ({ key }) => model.key === key ? { model: released(model) } : { model },
1266
- Clicked: () => ({ model }),
1267
- Elapsed: ({ generation, pointerType }) => model.holding && model.generation === generation ? {
1268
- model: released(model),
1269
- outMessage: LongPressed.make({ pointerType })
1270
- } : { model }
1271
- })
1272
- });
1273
- /**
1274
- * Attaches `Press.events` to the target, mapped into this placement's
1275
- * Messages, and writes `data-holding` while the element is down. `Press` and
1276
- * `LongPress` on one slot are refused by the resolver (two Mounts of one
1277
- * name), so today a slot takes one of them.
1278
- */
1279
- const behavior$5 = (declared) => (slots) => (options) => Behavior.forSlots(slots)({ [options.target]: Behavior.slot({
1280
- requires: { capability: Capability.Interactive },
1281
- attributes: ({ input, h }) => input[declared.field].holding ? [h.DataAttribute("holding", "true")] : [],
1282
- mount: () => Mount.mapMessage(events(), (fact) => declared.wrapper.make(fact))
1283
- }) }, { name: "LongPress" });
1284
- // src/interaction/move.ts
1285
- /**
1286
- * `Move` as a Behavior: attaches the `foldkit-primitives/dom` Mount to a
1287
- * draggable slot and maps its facts into the view's Messages. No Bundle: a
1288
- * drag's meaning (a threshold, a snap, a reorder) is the parent's `update`,
1289
- * and the deltas are already facts.
1290
- */
1291
- var move_exports = /* @__PURE__ */ __exportAll({
1292
- MoveEnded: () => MoveEnded,
1293
- MoveStarted: () => MoveStarted,
1294
- Moved: () => Moved,
1295
- behavior: () => behavior$4,
1296
- mount: () => Move
1297
- });
1298
- const behavior$4 = (slots) => (options) => Behavior.forSlots(slots)({ [options.handle]: Behavior.slot({
1299
- requires: { capability: Capability.Draggable },
1300
- mount: () => Mount.mapMessage(Move(), options.toMessage)
1301
- }) }, { name: "Move" });
1302
- // src/interaction/overlay.ts
1303
- var overlay_exports = /* @__PURE__ */ __exportAll({
1304
- behaviors: () => behaviors,
1305
- modal: () => modal,
1306
- nonModal: () => nonModal
1307
- });
1308
- /** A modal overlay: dismissed by Escape and outside press, focused, locked, inert. */
1309
- const modal = {
1310
- dismiss: {
1311
- outside: true,
1312
- escape: true
1313
- },
1314
- focus: {
1315
- contain: true,
1316
- restore: true
1317
- },
1318
- scroll: { lock: true },
1319
- inert: true
1320
- };
1321
- /** A modeless overlay: dismissed by Escape and outside press, focus restored, page usable. */
1322
- const nonModal = {
1323
- dismiss: {
1324
- outside: true,
1325
- escape: true
1326
- },
1327
- focus: {
1328
- contain: false,
1329
- restore: true
1330
- },
1331
- scroll: { lock: false },
1332
- inert: false
1333
- };
1334
- /**
1335
- * The policy as behaviors, to spread into the view's pipe alongside the
1336
- * widget's own. Conditional pieces are included or left out when the list
1337
- * is built — a static policy per composition — so no slot carries a mount
1338
- * its policy never uses.
1339
- */
1340
- const behaviors = (slots) => (options) => [
1341
- behavior$14(options.stack)(slots)({
1342
- layer: options.layer,
1343
- ...options.trigger === void 0 ? {} : { trigger: options.trigger },
1344
- id: options.id,
1345
- outsidePress: options.policy.dismiss.outside,
1346
- escape: options.policy.dismiss.escape
1347
- }),
1348
- behavior$12(slots)({
1349
- container: options.layer,
1350
- contain: options.policy.focus.contain,
1351
- restore: options.policy.focus.restore,
1352
- ...options.policy.focus.initialFocus === void 0 ? {} : { initialFocus: options.policy.focus.initialFocus }
1353
- }),
1354
- ...options.policy.scroll.lock ? [scrollLock(slots)({ container: options.layer })] : [],
1355
- ...options.policy.inert ? [hideOutside(slots)({ container: options.layer })] : []
1356
- ];
1357
- // src/interaction/selection.ts
1358
- /**
1359
- * Selection over a set of items: which ids are selected, and the anchor a
1360
- * range extends from. `mode` is `'single'` (a click replaces), `'multiple'`
1361
- * (a click toggles), or `'none'`. Single mode keeps one item selected unless
1362
- * `allowEmpty`. A range needs the items' order, which the view knows and the
1363
- * Bundle does not, so `Ranged` carries it. The Behavior says the state in
1364
- * ARIA and wires a plain click; a Shift range comes from `Press`, whose
1365
- * `Pressed` carries `shiftKey`.
1366
- */
1367
- var selection_exports = /* @__PURE__ */ __exportAll({
1368
- Args: () => Args$1,
1369
- Message: () => Message$1,
1370
- Mode: () => Mode,
1371
- Model: () => Model$1,
1372
- behavior: () => behavior$3,
1373
- between: () => between,
1374
- bundle: () => bundle$1
1375
- });
1376
- const Mode = Schema.Literals([
1377
- "single",
1378
- "multiple",
1379
- "none"
1380
- ]);
1381
- const Model$1 = Schema.Struct({
1382
- /** In selection order. */
1383
- selected: Schema.Array(Schema.String),
1384
- /** Where the last plain selection happened; a range extends from it. */
1385
- anchor: Schema.NullOr(Schema.String)
1386
- });
1387
- const Message$1 = defineMessageUnion({
1388
- /** A plain activation: replaces in single mode, toggles in multiple mode. */
1389
- Activated: { id: Schema.String },
1390
- /** A Shift activation: selects everything between the anchor and `id` in `order`. */
1391
- Ranged: {
1392
- id: Schema.String,
1393
- order: Schema.Array(Schema.String)
1394
- },
1395
- Replaced: { ids: Schema.Array(Schema.String) },
1396
- Cleared: {}
1397
- });
1398
- const Args$1 = Schema.Struct({
1399
- mode: Mode,
1400
- /** In single mode, whether activating the selected item deselects it. */
1401
- allowEmpty: Schema.Boolean
1402
- });
1403
- const without = (ids, id) => ids.filter((other) => other !== id);
1404
- /** The ids between `from` and `to` in `order`, inclusive, in `order`'s order. */
1405
- const between = (order, from, to) => {
1406
- const end = order.indexOf(to);
1407
- if (end === -1) return [];
1408
- const start = from === null ? -1 : order.indexOf(from);
1409
- if (start === -1) return [to];
1410
- return order.slice(Math.min(start, end), Math.max(start, end) + 1);
1411
- };
1412
- const bundle$1 = Bundle.make("Selection", {
1413
- Model: Model$1,
1414
- Message: Message$1,
1415
- args: Args$1,
1416
- init: () => ({ model: {
1417
- selected: [],
1418
- anchor: null
1419
- } }),
1420
- update: (model, message, args) => Message$1.match(message, {
1421
- Activated: ({ id }) => {
1422
- if (args.mode === "none") return { model };
1423
- const isSelected = model.selected.includes(id);
1424
- if (args.mode === "single") return isSelected ? args.allowEmpty ? { model: {
1425
- selected: [],
1426
- anchor: id
1427
- } } : { model } : { model: {
1428
- selected: [id],
1429
- anchor: id
1430
- } };
1431
- return { model: {
1432
- selected: isSelected ? without(model.selected, id) : [...model.selected, id],
1433
- anchor: id
1434
- } };
1435
- },
1436
- Ranged: ({ id, order }) => {
1437
- if (args.mode !== "multiple") return { model };
1438
- const range = between(order, model.anchor, id);
1439
- const inRange = new Set(range);
1440
- const selected = [...model.selected.filter((other) => !inRange.has(other)), ...range];
1441
- const anchor = model.anchor ?? id;
1442
- return sameIds(selected, model.selected) && anchor === model.anchor ? { model } : { model: {
1443
- selected,
1444
- anchor
1445
- } };
1446
- },
1447
- Replaced: ({ ids }) => {
1448
- const selected = args.mode === "none" ? [] : [...new Set(ids)];
1449
- return sameIds(selected, model.selected) ? { model } : { model: {
1450
- selected,
1451
- anchor: model.anchor
1452
- } };
1453
- },
1454
- Cleared: () => model.selected.length === 0 ? { model } : { model: {
1455
- selected: [],
1456
- anchor: model.anchor
1457
- } }
1458
- })
1459
- });
1460
- /**
1461
- * Writes `aria-selected` on each item from the placed slice, and
1462
- * `aria-multiselectable` on the container in multiple mode; wires `OnClick`
1463
- * to `Activated` unless told not to. A disabled item gets no click.
1464
- */
1465
- const behavior$3 = (declared, args) => (slots) => (options) => {
1466
- const itemsOf = perInput(options.items);
1467
- const selectedOf = perInput((input) => new Set(input[declared.field].selected));
1468
- return BundleBehavior.fromBundle(declared)(slots)({
1469
- ...options.container === void 0 ? {} : { [options.container]: {
1470
- requires: { capability: Capability.Container },
1471
- attributes: ({ h }) => [h.AriaMultiSelectable(args.mode === "multiple")]
1472
- } },
1473
- [options.item]: {
1474
- requires: { capability: Capability.Interactive },
1475
- attributes: ({ input, h, item, send }) => {
1476
- if (item === void 0) return [];
1477
- const items = itemsOf(input);
1478
- const id = item.id ?? items.ids[item.index];
1479
- if (id === void 0) return [];
1480
- const clickable = options.click !== false && args.mode !== "none" && !items.isDisabled(item.index);
1481
- return [h.AriaSelected(selectedOf(input).has(id)), ...clickable ? [h.OnClick(send(Message$1.Activated({ id })))] : []];
1482
- }
1483
- }
1484
- }, { name: "Selection" });
1485
- };
1486
- // src/interaction/targets.ts
1487
- /**
1488
- * `Targets` as a Behavior: attaches the `foldkit-primitives/dom` Mount to a
1489
- * container slot and maps what it reports into the view's Messages. No Bundle:
1490
- * which descendant is under the pointer, or was pressed, is already a fact, and
1491
- * what it means is the parent's `update`.
1492
- */
1493
- var targets_exports = /* @__PURE__ */ __exportAll({
1494
- TargetHovered: () => TargetHovered,
1495
- TargetPressed: () => TargetPressed,
1496
- behavior: () => behavior$2,
1497
- mount: () => Targets,
1498
- targetOf: () => targetOf
1499
- });
1500
- const behavior$2 = (slots) => (options) => Behavior.forSlots(slots)({ [options.container]: Behavior.slot({
1501
- requires: { capability: Capability.Container },
1502
- mount: () => Mount.mapMessage(Targets({
1503
- attribute: options.attribute,
1504
- preventDefault: options.preventDefault ?? false
1505
- }), options.toMessage)
1506
- }) }, { name: "Targets" });
1507
- // src/interaction/pointer-drag.ts
1508
- /**
1509
- * `PointerDrag` as a Behavior: attaches the `foldkit-primitives/dom` Mount to
1510
- * a container slot and maps what it reports into the view's Messages. No
1511
- * Bundle: what is dragged and where the pointer is are facts, and what a drop
1512
- * means, and whether it is allowed, is the parent's `update`.
1513
- */
1514
- var pointer_drag_exports = /* @__PURE__ */ __exportAll({
1515
- DRAG_THRESHOLD: () => 4,
1516
- DragCancelled: () => DragCancelled,
1517
- DragDropped: () => DragDropped,
1518
- DragPlace: () => DragPlace,
1519
- DragStarted: () => DragStarted,
1520
- DragZone: () => DragZone,
1521
- DraggedOver: () => DraggedOver,
1522
- behavior: () => behavior$1,
1523
- boxOf: () => boxOf,
1524
- mount: () => PointerDrag,
1525
- zoneOf: () => zoneOf
1526
- });
1527
- const behavior$1 = (slots) => (options) => Behavior.forSlots(slots)({ [options.container]: Behavior.slot({
1528
- requires: { capability: Capability.Container },
1529
- mount: () => Mount.mapMessage(PointerDrag({
1530
- attribute: options.attribute,
1531
- ...options.targets === void 0 ? {} : { targets: options.targets },
1532
- ...options.part === void 0 ? {} : { part: options.part }
1533
- }), options.toMessage)
1534
- }) }, { name: "PointerDrag" });
1535
- // src/interaction/placing.ts
1536
- /**
1537
- * Keeps a floating panel inside the viewport: on insert it shifts left past
1538
- * the right edge and flips above its trigger when the bottom overflows and
1539
- * the room overhead fits; on release every inline prop goes. `placeFor` is
1540
- * pure geometry over rects, so the rule is unit-tested and the Mount only
1541
- * reads and writes. Inert without layout (zero rects never overflow), with
1542
- * no timers, observers, or reposition loops.
1543
- *
1544
- * `placeAt` is the other half, for a popup that must open under the trigger
1545
- * that opened it. It writes `left` and `--fk-placed-top` once, relative to
1546
- * the popup's offset parent; the panel's stylesheet reads the variable as
1547
- * `top`, so `keepWithin` can still replace `top` when it flips. Key the
1548
- * popup by the trigger: a Mount reads its args at insert and never again.
1549
- * `placeAtPoint` is the same write for a viewport point, when the open
1550
- * event carried coordinates the message builder dropped. The two mounts
1551
- * start together, so the fit may run before that write; the write measures
1552
- * again. A point that would hang past the bottom shifts up. A trigger still
1553
- * flips, and the panel is marked `data-fk-placed="above"` so a hover bridge
1554
- * can follow the side that opened.
1555
- * Zero-size rects stay at the origin, so an inert document writes nothing
1556
- * a layout pass would have to undo.
1557
- *
1558
- * Three related pieces this is not:
1559
- *
1560
- * - `Anchor.behavior` (`foldkit-mixins-ui`) binds a floating element to
1561
- * another element's id through floating-ui, with portals and continuous
1562
- * repositioning. It relocates nodes out from under the runtime (portal by
1563
- * default) and burns under zero geometry; reach for it when the panel must
1564
- * track a moving anchor, not for a popup placed once.
1565
- * - `KeepInView` (`foldkit-primitives/dom`) scrolls newly marked content
1566
- * into view. Same verb, opposite direction: that one moves the page to
1567
- * the panel, this one moves the panel into the page.
1568
- * - `Overlay.behaviors` owns dismissal, focus, scroll lock, and inertness,
1569
- * but no positioning; spread this alongside it for the full overlay.
1570
- */
1571
- var placing_exports = /* @__PURE__ */ __exportAll({
1572
- KeepWithin: () => KeepWithin,
1573
- PlaceAt: () => PlaceAt,
1574
- PlaceAtPoint: () => PlaceAtPoint,
1575
- keepWithin: () => keepWithin,
1576
- placeAt: () => placeAt,
1577
- placeAtPoint: () => placeAtPoint,
1578
- placeAtTrigger: () => placeAtTrigger,
1579
- placeFor: () => placeFor,
1580
- placedAbove: () => placedAbove,
1581
- placedSide: () => placedSide,
1582
- placedTop: () => placedTop,
1583
- shiftIntoViewport: () => shiftIntoViewport
1584
- });
1585
- /** Where the panel goes so it fits: left by the overflow, above when the
1586
- * bottom clears the window and the room overhead fits the panel. */
1587
- const placeFor = (rect, viewport, margin = 8) => {
1588
- const over = rect.right - viewport.width + margin;
1589
- return {
1590
- dx: over > 0 ? -over : 0,
1591
- flip: rect.bottom > viewport.height && rect.top - margin >= rect.height
1592
- };
1593
- };
1594
- /** Pixels to move a box up so its bottom sits `margin` inside the viewport.
1595
- * `0` when it already fits, or when its top is already at the margin. */
1596
- const shiftIntoViewport = (rect, viewportHeight, margin = 8) => {
1597
- const overflow = rect.bottom - (viewportHeight - margin);
1598
- if (overflow <= 0) return 0;
1599
- const room = rect.top - margin;
1600
- if (room <= 0) return 0;
1601
- return Math.min(overflow, room);
1602
- };
1603
- /** Attribute `keepWithin` sets to `placedAbove` when the panel flips. */
1604
- const placedSide = "data-fk-placed";
1605
- /** Value of `placedSide` while the panel stands above its anchor. */
1606
- const placedAbove = "above";
1607
- /** Custom property `PlaceAt` writes. A panel reads it as `top` so a viewport
1608
- * flip, which sets the `top` property, still wins. */
1609
- const placedTop = "--fk-placed-top";
1610
- /** Where a popup sits under its trigger's left edge, in the offset parent's
1611
- * coordinates. A zero-size trigger (an inert document) stays at the origin. */
1612
- const placeAt = (trigger, origin, gap = 4) => {
1613
- if (trigger.width === 0 && trigger.height === 0) return {
1614
- left: 0,
1615
- top: 0
1616
- };
1617
- return {
1618
- left: trigger.left - origin.left,
1619
- top: trigger.bottom - origin.top + gap
1620
- };
1621
- };
1622
- /** Where a popup's top-left sits on a viewport point, in the offset parent's
1623
- * coordinates. A context menu uses it: the right-click event carries the
1624
- * point, and `OnContextMenu` does not. */
1625
- const placeAtPoint = (point, origin) => ({
1626
- left: point.x - origin.left,
1627
- top: point.y - origin.top
1628
- });
1629
- const clearFit = (element) => {
1630
- element.style.translate = "";
1631
- element.style.top = "";
1632
- element.style.bottom = "";
1633
- element.removeAttribute(placedSide);
1634
- };
1635
- const fitWithin = (element) => {
1636
- clearFit(element);
1637
- const rect = element.getBoundingClientRect();
1638
- const placed = placeFor({
1639
- top: rect.top,
1640
- right: rect.right,
1641
- bottom: rect.bottom,
1642
- height: rect.height
1643
- }, {
1644
- width: document.documentElement.clientWidth,
1645
- height: window.innerHeight
1646
- });
1647
- if (placed.dx !== 0) element.style.translate = `${placed.dx}px 0`;
1648
- if (placed.flip) {
1649
- element.style.top = "auto";
1650
- element.style.bottom = "calc(100% + 4px)";
1651
- element.setAttribute(placedSide, placedAbove);
1652
- }
1653
- };
1654
- const refits = /* @__PURE__ */ new WeakMap();
1655
- const onPlaced = (element, refit) => {
1656
- const waiting = refits.get(element) ?? /* @__PURE__ */ new Set();
1657
- waiting.add(refit);
1658
- refits.set(element, waiting);
1659
- };
1660
- const offPlaced = (element, refit) => {
1661
- const waiting = refits.get(element);
1662
- if (waiting === void 0) return;
1663
- waiting.delete(refit);
1664
- if (waiting.size === 0) refits.delete(element);
1665
- };
1666
- const notifyPlaced = (element) => {
1667
- const waiting = refits.get(element);
1668
- if (waiting === void 0) return;
1669
- for (const refit of waiting) refit();
1670
- };
1671
- const applyPlaced = (element, placed) => {
1672
- element.style.left = `${placed.left}px`;
1673
- element.style.setProperty(placedTop, `${placed.top}px`);
1674
- };
1675
- const clearPlaced = (element) => {
1676
- element.style.left = "";
1677
- element.style.removeProperty(placedTop);
1678
- };
1679
- const writePlaced = (element, placed, shift) => {
1680
- clearFit(element);
1681
- applyPlaced(element, placed);
1682
- if (shift) {
1683
- const rect = element.getBoundingClientRect();
1684
- const dy = shiftIntoViewport({
1685
- top: rect.top,
1686
- bottom: rect.bottom
1687
- }, window.innerHeight);
1688
- if (dy !== 0) element.style.setProperty(placedTop, `${placed.top - dy}px`);
1689
- }
1690
- notifyPlaced(element);
1691
- };
1692
- const KeepWithin = Mount.defineStream("KeepWithin", {
1693
- messages: [Schema.Never],
1694
- execute: ({ element }) => Stream.callback(() => {
1695
- if (!(element instanceof HTMLElement)) return Effect.acquireRelease(Effect.void, () => Effect.void);
1696
- const node = element;
1697
- const refit = () => fitWithin(node);
1698
- return Effect.acquireRelease(Effect.sync(() => {
1699
- onPlaced(node, refit);
1700
- refit();
1701
- }), () => Effect.sync(() => {
1702
- offPlaced(node, refit);
1703
- clearFit(node);
1704
- }));
1705
- })
1706
- });
1707
- const paddingEdge = (node) => {
1708
- const rect = node.getBoundingClientRect();
1709
- return {
1710
- left: rect.left + node.clientLeft,
1711
- top: rect.top + node.clientTop
1712
- };
1713
- };
1714
- const originOf = (element) => {
1715
- const originNode = element.offsetParent;
1716
- return originNode instanceof HTMLElement ? paddingEdge(originNode) : {
1717
- left: 0,
1718
- top: 0
1719
- };
1720
- };
1721
- const PlaceAt = Mount.defineStream("PlaceAt", {
1722
- messages: [Schema.Never],
1723
- args: {
1724
- /** Element id of the trigger the popup opens under. */
1725
- triggerId: Schema.String,
1726
- /** Pixels between the trigger's bottom and the popup. */
1727
- gap: Schema.Number
1728
- },
1729
- execute: ({ element, triggerId, gap }) => Stream.callback(() => Effect.acquireRelease(Effect.sync(() => {
1730
- if (!(element instanceof HTMLElement)) return;
1731
- const trigger = document.getElementById(triggerId);
1732
- if (!(trigger instanceof HTMLElement)) return;
1733
- const rect = trigger.getBoundingClientRect();
1734
- writePlaced(element, placeAt({
1735
- left: rect.left,
1736
- top: rect.top,
1737
- bottom: rect.bottom,
1738
- width: rect.width,
1739
- height: rect.height
1740
- }, originOf(element), gap), false);
1741
- }), () => Effect.sync(() => {
1742
- if (!(element instanceof HTMLElement)) return;
1743
- clearPlaced(element);
1744
- })))
1745
- });
1746
- const PlaceAtPoint = Mount.defineStream("PlaceAtPoint", {
1747
- messages: [Schema.Never],
1748
- args: {
1749
- /** Viewport x of the popup's top-left. */
1750
- x: Schema.Number,
1751
- /** Viewport y of the popup's top-left. */
1752
- y: Schema.Number
1753
- },
1754
- execute: ({ element, x, y }) => Stream.callback(() => Effect.acquireRelease(Effect.sync(() => {
1755
- if (!(element instanceof HTMLElement)) return;
1756
- writePlaced(element, placeAtPoint({
1757
- x,
1758
- y
1759
- }, originOf(element)), true);
1760
- }), () => Effect.sync(() => {
1761
- if (!(element instanceof HTMLElement)) return;
1762
- clearPlaced(element);
1763
- })))
1764
- });
1765
- const placeAtTrigger = (slots) => (options) => Behavior.forSlots(slots)({ [options.panel]: Behavior.slot({
1766
- requires: { capability: Capability.Container },
1767
- mount: (input) => {
1768
- const at = options.at?.(input) ?? Option.none();
1769
- return Option.isSome(at) ? PlaceAtPoint({
1770
- x: at.value.x,
1771
- y: at.value.y
1772
- }) : PlaceAt({
1773
- triggerId: options.triggerId(input),
1774
- gap: options.gap ?? 4
1775
- });
1776
- }
1777
- }) }, { name: "PlaceAt" });
1778
- const keepWithin = (slots) => (options) => Behavior.forSlots(slots)({ [options.panel]: Behavior.slot({
1779
- requires: { capability: Capability.Container },
1780
- mount: () => KeepWithin()
1781
- }) }, { name: "KeepWithin" });
1782
- // src/interaction/tree-navigation.ts
1783
- /**
1784
- * Keyboard navigation of a tree (a file explorer, a page's layers, a nested
1785
- * menu), after the WAI-ARIA tree pattern: one tab stop, Up and Down through the
1786
- * rows that are showing, Right to open a row or step into it, Left to close it
1787
- * or step out to its parent, Home and End to the first and last row.
1788
- *
1789
- * The Model slice is the current row's id and which rows are open. Openness is
1790
- * stored as the rows `toggled` away from a default, so a layers panel that
1791
- * starts with everything open and a file tree that starts closed are the same
1792
- * bundle with a different `openByDefault`, and neither stores every row.
1793
- *
1794
- * Which rows exist is the view's to say, each with its parent, in tree order.
1795
- * What a key does is a pure function of those rows and the Model; the Behavior
1796
- * wires it to the slots and writes the tree's ARIA attributes.
1797
- */
1798
- var tree_navigation_exports = /* @__PURE__ */ __exportAll({
1799
- Args: () => Args,
1800
- Message: () => Message,
1801
- Model: () => Model,
1802
- behavior: () => behavior,
1803
- bundle: () => bundle,
1804
- isOpen: () => isOpen,
1805
- move: () => move,
1806
- shown: () => shown,
1807
- tabStop: () => tabStop
1808
- });
1809
- const Model = Schema.Struct({
1810
- /** The current row's id, or `null` before any row has been focused. */
1811
- current: Schema.NullOr(Schema.String),
1812
- /** The rows whose open state differs from `openByDefault`. */
1813
- toggled: Schema.Array(Schema.String)
1814
- });
1815
- const Message = defineMessageUnion({
1816
- /** A row became current: the user focused it, or a key moved there. */
1817
- Focused: { id: Schema.String },
1818
- Opened: { id: Schema.String },
1819
- Closed: { id: Schema.String }
1820
- });
1821
- const Args = Schema.Struct({
1822
- /** Whether a row with children starts open. */
1823
- openByDefault: Schema.Boolean });
1824
- /** Whether a row is open, given the default and what was toggled. */
1825
- const isOpen = (model, args, id) => args.openByDefault !== model.toggled.includes(id);
1826
- const setOpen = (model, args, id, open) => isOpen(model, args, id) === open ? model : {
1827
- ...model,
1828
- toggled: model.toggled.includes(id) ? model.toggled.filter((toggled) => toggled !== id) : [...model.toggled, id]
1829
- };
1830
- const bundle = Bundle.make("TreeNavigation", {
1831
- Model,
1832
- Message,
1833
- args: Args,
1834
- init: () => ({ model: {
1835
- current: null,
1836
- toggled: []
1837
- } }),
1838
- update: (model, message, args) => {
1839
- switch (message._tag) {
1840
- case "Focused": return model.current === message.id ? { model } : { model: {
1841
- ...model,
1842
- current: message.id
1843
- } };
1844
- case "Opened": return { model: setOpen(model, args, message.id, true) };
1845
- case "Closed": return { model: setOpen(model, args, message.id, false) };
1846
- }
1847
- }
1848
- });
1849
- /**
1850
- * The rows that are showing: every row whose ancestors are all open, with its
1851
- * level and its place among its siblings. A row whose parent is not a row is
1852
- * treated as top-level.
1853
- */
1854
- const shown = (rows, model, args) => {
1855
- const byId = new Map(rows.map((row) => [row.id, row]));
1856
- const siblingsOf = /* @__PURE__ */ new Map();
1857
- const positionOf = /* @__PURE__ */ new Map();
1858
- for (const row of rows) {
1859
- const parent = row.parent !== null && byId.has(row.parent) ? row.parent : null;
1860
- const count = (siblingsOf.get(parent) ?? 0) + 1;
1861
- siblingsOf.set(parent, count);
1862
- positionOf.set(row.id, count);
1863
- }
1864
- const levels = /* @__PURE__ */ new Map();
1865
- const showing = /* @__PURE__ */ new Map();
1866
- const result = [];
1867
- for (const row of rows) {
1868
- const parent = row.parent !== null && byId.has(row.parent) ? row.parent : null;
1869
- const level = parent === null ? 1 : (levels.get(parent) ?? 0) + 1;
1870
- levels.set(row.id, level);
1871
- const visible = parent === null || showing.get(parent) === true && isOpen(model, args, parent);
1872
- showing.set(row.id, visible);
1873
- if (!visible) continue;
1874
- result.push({
1875
- ...row,
1876
- level,
1877
- position: positionOf.get(row.id) ?? 1,
1878
- siblings: siblingsOf.get(parent) ?? 1
1879
- });
1880
- }
1881
- return result;
1882
- };
1883
- const isPlain = (modifiers) => !modifiers.ctrlKey && !modifiers.altKey && !modifiers.metaKey;
1884
- /**
1885
- * What a key does from the current row, over the rows that are showing, or
1886
- * `undefined` when it is not a key the tree handles. Disabled rows are stepped
1887
- * over. With nothing current, Down and Home land on the first row, Up and End
1888
- * on the last. Right and Left swap under `rtl`.
1889
- */
1890
- const move = (rows, current, key, modifiers, model, args, direction = "ltr") => {
1891
- const enabled = rows.filter((row) => row.disabled !== true);
1892
- if (enabled.length === 0 || !isPlain(modifiers)) return void 0;
1893
- const focus = (row) => row === void 0 ? void 0 : {
1894
- _tag: "Focus",
1895
- id: row.id
1896
- };
1897
- const at = enabled.findIndex((row) => row.id === current);
1898
- const inward = direction === "rtl" ? "ArrowLeft" : "ArrowRight";
1899
- const outward = direction === "rtl" ? "ArrowRight" : "ArrowLeft";
1900
- if (key === "Home") return focus(enabled[0]);
1901
- if (key === "End") return focus(enabled[enabled.length - 1]);
1902
- if (key === "ArrowDown") return focus(at === -1 ? enabled[0] : enabled[Math.min(at + 1, enabled.length - 1)]);
1903
- if (key === "ArrowUp") return focus(at === -1 ? enabled[enabled.length - 1] : enabled[Math.max(at - 1, 0)]);
1904
- const row = enabled[at];
1905
- if (row === void 0) return key === inward || key === outward ? focus(enabled[0]) : void 0;
1906
- if (key === inward) {
1907
- if (!row.branch) return void 0;
1908
- if (!isOpen(model, args, row.id)) return {
1909
- _tag: "Open",
1910
- id: row.id
1911
- };
1912
- return focus(enabled.find((child) => child.parent === row.id));
1913
- }
1914
- if (key === outward) {
1915
- if (row.branch && isOpen(model, args, row.id)) return {
1916
- _tag: "Close",
1917
- id: row.id
1918
- };
1919
- return focus(enabled.find((parent) => parent.id === row.parent));
1920
- }
1921
- };
1922
- /** The row that holds the tab stop: the current one while it is showing, else the first. */
1923
- const tabStop = (rows, current) => {
1924
- return (rows.find((row) => row.id === current && row.disabled !== true) ?? rows.find((row) => row.disabled !== true))?.id;
1925
- };
1926
- /**
1927
- * Wires a placed `TreeNavigation` to the slots. The container's keys move focus
1928
- * to the row they pick, or open or close the current row with focus left where
1929
- * it is. Each showing row gets its id, `role="treeitem"`, `aria-level` (and
1930
- * the same level as `--fk-tree-level`, so one `calc` indents any depth),
1931
- * `aria-posinset`, `aria-setsize`, `aria-expanded` when it has children,
1932
- * `aria-disabled` when disabled, a roving `tabindex`, and `OnFocus` reporting
1933
- * it current. Every handled key is default-prevented.
1934
- */
1935
- const behavior = (declared, args) => (slots) => (options) => {
1936
- const wrap = (message) => declared.wrapper.make(message);
1937
- const slice = (input) => input[declared.field];
1938
- const domId = options.domId ?? ((id) => id);
1939
- const prepare = perInput((input) => {
1940
- const model = slice(input);
1941
- const rows = shown(options.rows(input), model, args);
1942
- return {
1943
- model,
1944
- rows,
1945
- byId: new Map(rows.map((row) => [row.id, row])),
1946
- stop: tabStop(rows, model.current)
1947
- };
1948
- });
1949
- return Behavior.forSlots(slots)({
1950
- [options.container]: Behavior.slot({
1951
- requires: { capability: Capability.Interactive },
1952
- mount: () => FollowTabStop(),
1953
- attributes: ({ input, h }) => {
1954
- const { model, rows } = prepare(input);
1955
- const direction = options.direction?.(input) ?? "ltr";
1956
- return [h.OnKeyDownFocus((key, modifiers) => {
1957
- const action = move(rows, model.current, key, modifiers, model, args, direction);
1958
- if (action === void 0) return Option.none();
1959
- const message = action._tag === "Focus" ? Message.Focused({ id: action.id }) : action._tag === "Open" ? Message.Opened({ id: action.id }) : Message.Closed({ id: action.id });
1960
- return Option.some({
1961
- focusSelector: idSelector(domId(action.id)),
1962
- message: wrap(message)
1963
- });
1964
- })];
1965
- }
1966
- }),
1967
- [options.item]: Behavior.slot({
1968
- requires: { capability: Capability.Focusable },
1969
- attributes: ({ input, h, item }) => {
1970
- if (item === void 0) return [];
1971
- const { model, rows, byId, stop } = prepare(input);
1972
- const row = item.id === void 0 ? rows[item.index] : byId.get(item.id);
1973
- if (row === void 0) return [];
1974
- return [
1975
- h.Id(domId(row.id)),
1976
- h.Role("treeitem"),
1977
- h.AriaLevel(row.level),
1978
- h.Style({ "--fk-tree-level": String(row.level) }),
1979
- h.AriaPosinset(row.position),
1980
- h.AriaSetsize(row.siblings),
1981
- ...row.branch ? [h.AriaExpanded(isOpen(model, args, row.id))] : [],
1982
- ...row.disabled === true ? [h.AriaDisabled(true)] : [],
1983
- h.Tabindex(stop === row.id ? 0 : -1),
1984
- h.OnFocus(wrap(Message.Focused({ id: row.id })))
1985
- ];
1986
- }
1987
- })
1988
- }, { name: "TreeNavigation" });
1989
- };
1990
- export { bundle_behavior_exports as BundleBehavior, 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, overlay_exports as Overlay, placing_exports as Placing, 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 };
1991
-
1992
- //# sourceMappingURL=index.mjs.map