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