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.
- package/dist/device/fullscreen.d.ts +28 -0
- package/dist/device/fullscreen.d.ts.map +1 -0
- package/dist/device/fullscreen.js +69 -0
- package/dist/device/fullscreen.js.map +1 -0
- package/dist/device/geolocation.d.ts +56 -0
- package/dist/device/geolocation.d.ts.map +1 -0
- package/dist/device/geolocation.js +87 -0
- package/dist/device/geolocation.js.map +1 -0
- package/dist/device/index.d.ts +5 -0
- package/dist/device/index.js +6 -0
- package/dist/device/media-devices.d.ts +88 -0
- package/dist/device/media-devices.d.ts.map +1 -0
- package/dist/device/media-devices.js +117 -0
- package/dist/device/media-devices.js.map +1 -0
- package/dist/device/media-stream.d.ts +93 -0
- package/dist/device/media-stream.d.ts.map +1 -0
- package/dist/device/media-stream.js +106 -0
- package/dist/device/media-stream.js.map +1 -0
- package/dist/device/permissions.d.ts +106 -0
- package/dist/device/permissions.d.ts.map +1 -0
- package/dist/device/permissions.js +143 -0
- package/dist/device/permissions.js.map +1 -0
- package/dist/dom/autofocus.d.ts +15 -0
- package/dist/dom/autofocus.d.ts.map +1 -0
- package/dist/dom/autofocus.js +22 -0
- package/dist/dom/autofocus.js.map +1 -0
- package/dist/dom/clipboard.d.ts +30 -0
- package/dist/dom/clipboard.d.ts.map +1 -0
- package/dist/dom/clipboard.js +50 -0
- package/dist/dom/clipboard.js.map +1 -0
- package/dist/dom/editable-text.d.ts +72 -0
- package/dist/dom/editable-text.d.ts.map +1 -0
- package/dist/dom/editable-text.js +232 -0
- package/dist/dom/editable-text.js.map +1 -0
- package/dist/dom/fluid-hover.d.ts +93 -0
- package/dist/dom/fluid-hover.d.ts.map +1 -0
- package/dist/dom/fluid-hover.js +294 -0
- package/dist/dom/fluid-hover.js.map +1 -0
- package/dist/dom/focus-scope.d.ts +21 -0
- package/dist/dom/focus-scope.d.ts.map +1 -0
- package/dist/dom/focus-scope.js +93 -0
- package/dist/dom/focus-scope.js.map +1 -0
- package/dist/dom/follow-tab-stop.d.ts +11 -0
- package/dist/dom/follow-tab-stop.d.ts.map +1 -0
- package/dist/dom/follow-tab-stop.js +54 -0
- package/dist/dom/follow-tab-stop.js.map +1 -0
- package/dist/dom/image-decode.d.ts +27 -0
- package/dist/dom/image-decode.d.ts.map +1 -0
- package/dist/dom/image-decode.js +50 -0
- package/dist/dom/image-decode.js.map +1 -0
- package/dist/dom/index.d.ts +24 -0
- package/dist/dom/index.js +25 -0
- package/dist/dom/input-mask.d.ts +35 -0
- package/dist/dom/input-mask.d.ts.map +1 -0
- package/dist/dom/input-mask.js +90 -0
- package/dist/dom/input-mask.js.map +1 -0
- package/dist/dom/keep-in-view.d.ts +16 -0
- package/dist/dom/keep-in-view.d.ts.map +1 -0
- package/dist/dom/keep-in-view.js +38 -0
- package/dist/dom/keep-in-view.js.map +1 -0
- package/dist/dom/keyboard-inset.d.ts +29 -0
- package/dist/dom/keyboard-inset.d.ts.map +1 -0
- package/dist/dom/keyboard-inset.js +45 -0
- package/dist/dom/keyboard-inset.js.map +1 -0
- package/dist/dom/layers.d.ts +6 -0
- package/dist/dom/layers.d.ts.map +1 -0
- package/dist/dom/layers.js +35 -0
- package/dist/dom/layers.js.map +1 -0
- package/dist/dom/measure.d.ts +42 -0
- package/dist/dom/measure.d.ts.map +1 -0
- package/dist/dom/measure.js +123 -0
- package/dist/dom/measure.js.map +1 -0
- package/dist/dom/move.d.ts +45 -0
- package/dist/dom/move.d.ts.map +1 -0
- package/dist/dom/move.js +96 -0
- package/dist/dom/move.js.map +1 -0
- package/dist/dom/pointed-links.d.ts +13 -0
- package/dist/dom/pointed-links.d.ts.map +1 -0
- package/dist/dom/pointed-links.js +59 -0
- package/dist/dom/pointed-links.js.map +1 -0
- package/dist/dom/pointer-drag.d.ts +132 -0
- package/dist/dom/pointer-drag.d.ts.map +1 -0
- package/dist/dom/pointer-drag.js +307 -0
- package/dist/dom/pointer-drag.js.map +1 -0
- package/dist/dom/pointer-position.d.ts +133 -0
- package/dist/dom/pointer-position.d.ts.map +1 -0
- package/dist/dom/pointer-position.js +173 -0
- package/dist/dom/pointer-position.js.map +1 -0
- package/dist/dom/script-loader.d.ts +12 -0
- package/dist/dom/script-loader.d.ts.map +1 -0
- package/dist/dom/script-loader.js +54 -0
- package/dist/dom/script-loader.js.map +1 -0
- package/dist/dom/scroll-bars.d.ts +52 -0
- package/dist/dom/scroll-bars.d.ts.map +1 -0
- package/dist/dom/scroll-bars.js +259 -0
- package/dist/dom/scroll-bars.js.map +1 -0
- package/dist/dom/scroll-settle.d.ts +33 -0
- package/dist/dom/scroll-settle.d.ts.map +1 -0
- package/dist/dom/scroll-settle.js +84 -0
- package/dist/dom/scroll-settle.js.map +1 -0
- package/dist/dom/scroll.d.ts +21 -0
- package/dist/dom/scroll.d.ts.map +1 -0
- package/dist/dom/scroll.js +141 -0
- package/dist/dom/scroll.js.map +1 -0
- package/dist/dom/share.d.ts +19 -0
- package/dist/dom/share.d.ts.map +1 -0
- package/dist/dom/share.js +41 -0
- package/dist/dom/share.js.map +1 -0
- package/dist/dom/swipe.d.ts +121 -0
- package/dist/dom/swipe.d.ts.map +1 -0
- package/dist/dom/swipe.js +291 -0
- package/dist/dom/swipe.js.map +1 -0
- package/dist/dom/targets.d.ts +45 -0
- package/dist/dom/targets.d.ts.map +1 -0
- package/dist/dom/targets.js +70 -0
- package/dist/dom/targets.js.map +1 -0
- package/dist/dom/track-box.d.ts +61 -0
- package/dist/dom/track-box.d.ts.map +1 -0
- package/dist/dom/track-box.js +131 -0
- package/dist/dom/track-box.js.map +1 -0
- package/dist/events/active-element.d.ts +15 -0
- package/dist/events/active-element.d.ts.map +1 -0
- package/dist/events/active-element.js +33 -0
- package/dist/events/active-element.js.map +1 -0
- package/dist/events/hotkeys.d.ts +22 -0
- package/dist/events/hotkeys.d.ts.map +1 -0
- package/dist/events/hotkeys.js +56 -0
- package/dist/events/hotkeys.js.map +1 -0
- package/dist/events/idle.d.ts +29 -0
- package/dist/events/idle.d.ts.map +1 -0
- package/dist/events/idle.js +51 -0
- package/dist/events/idle.js.map +1 -0
- package/dist/events/index.d.ts +9 -0
- package/dist/events/index.js +10 -0
- package/dist/{input-modality-BY-bSrEc.d.mts → events/input-modality.d.ts} +9 -12
- package/dist/events/input-modality.d.ts.map +1 -0
- package/dist/{input-modality-BXD383jg.mjs → events/input-modality.js} +3 -4
- package/dist/events/input-modality.js.map +1 -0
- package/dist/events/keyboard.d.ts +30 -0
- package/dist/events/keyboard.d.ts.map +1 -0
- package/dist/events/keyboard.js +55 -0
- package/dist/events/keyboard.js.map +1 -0
- package/dist/events/pointer.d.ts +15 -0
- package/dist/events/pointer.d.ts.map +1 -0
- package/dist/events/pointer.js +28 -0
- package/dist/events/pointer.js.map +1 -0
- package/dist/events/scroll.d.ts +16 -0
- package/dist/events/scroll.d.ts.map +1 -0
- package/dist/events/scroll.js +29 -0
- package/dist/events/scroll.js.map +1 -0
- package/dist/events/visibility.d.ts +25 -0
- package/dist/events/visibility.d.ts.map +1 -0
- package/dist/events/visibility.js +28 -0
- package/dist/events/visibility.js.map +1 -0
- package/dist/events/window-size.d.ts +30 -0
- package/dist/events/window-size.d.ts.map +1 -0
- package/dist/events/window-size.js +63 -0
- package/dist/events/window-size.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +10 -0
- package/dist/interaction/bundle-behavior.d.ts +37 -0
- package/dist/interaction/bundle-behavior.d.ts.map +1 -0
- package/dist/interaction/bundle-behavior.js +34 -0
- package/dist/interaction/bundle-behavior.js.map +1 -0
- package/dist/interaction/dismiss-layer.d.ts +100 -0
- package/dist/interaction/dismiss-layer.d.ts.map +1 -0
- package/dist/interaction/dismiss-layer.js +140 -0
- package/dist/interaction/dismiss-layer.js.map +1 -0
- package/dist/interaction/editable-text.d.ts +13 -0
- package/dist/interaction/editable-text.d.ts.map +1 -0
- package/dist/interaction/editable-text.js +17 -0
- package/dist/interaction/editable-text.js.map +1 -0
- package/dist/interaction/focus-scope.d.ts +21 -0
- package/dist/interaction/focus-scope.d.ts.map +1 -0
- package/dist/interaction/focus-scope.js +25 -0
- package/dist/interaction/focus-scope.js.map +1 -0
- package/dist/interaction/focus-visible.d.ts +9 -0
- package/dist/interaction/focus-visible.d.ts.map +1 -0
- package/dist/interaction/focus-visible.js +10 -0
- package/dist/interaction/focus-visible.js.map +1 -0
- package/dist/interaction/grid-navigation.d.ts +73 -0
- package/dist/interaction/grid-navigation.d.ts.map +1 -0
- package/dist/interaction/grid-navigation.js +137 -0
- package/dist/interaction/grid-navigation.js.map +1 -0
- package/dist/interaction/index.d.ts +21 -0
- package/dist/interaction/index.js +21 -0
- package/dist/interaction/layers.d.ts +12 -0
- package/dist/interaction/layers.d.ts.map +1 -0
- package/dist/interaction/layers.js +14 -0
- package/dist/interaction/layers.js.map +1 -0
- package/dist/interaction/list-navigation.d.ts +105 -0
- package/dist/interaction/list-navigation.d.ts.map +1 -0
- package/dist/interaction/list-navigation.js +168 -0
- package/dist/interaction/list-navigation.js.map +1 -0
- package/dist/interaction/live-announce.d.ts +70 -0
- package/dist/interaction/live-announce.d.ts.map +1 -0
- package/dist/interaction/live-announce.js +115 -0
- package/dist/interaction/live-announce.js.map +1 -0
- package/dist/interaction/long-press.d.ts +102 -0
- package/dist/interaction/long-press.d.ts.map +1 -0
- package/dist/interaction/long-press.js +115 -0
- package/dist/interaction/long-press.js.map +1 -0
- package/dist/interaction/move.d.ts +10 -0
- package/dist/interaction/move.d.ts.map +1 -0
- package/dist/interaction/move.js +17 -0
- package/dist/interaction/move.js.map +1 -0
- package/dist/interaction/overlay.d.ts +45 -0
- package/dist/interaction/overlay.d.ts.map +1 -0
- package/dist/interaction/overlay.js +57 -0
- package/dist/interaction/overlay.js.map +1 -0
- package/dist/interaction/placing.d.ts +86 -0
- package/dist/interaction/placing.d.ts.map +1 -0
- package/dist/interaction/placing.js +239 -0
- package/dist/interaction/placing.js.map +1 -0
- package/dist/interaction/pointer-drag.d.ts +23 -0
- package/dist/interaction/pointer-drag.d.ts.map +1 -0
- package/dist/interaction/pointer-drag.js +21 -0
- package/dist/interaction/pointer-drag.js.map +1 -0
- package/dist/interaction/press.d.ts +152 -0
- package/dist/interaction/press.d.ts.map +1 -0
- package/dist/interaction/press.js +229 -0
- package/dist/interaction/press.js.map +1 -0
- package/dist/interaction/roving-tabindex.d.ts +85 -0
- package/dist/interaction/roving-tabindex.d.ts.map +1 -0
- package/dist/interaction/roving-tabindex.js +142 -0
- package/dist/interaction/roving-tabindex.js.map +1 -0
- package/dist/interaction/selection.d.ts +72 -0
- package/dist/interaction/selection.d.ts.map +1 -0
- package/dist/interaction/selection.js +129 -0
- package/dist/interaction/selection.js.map +1 -0
- package/dist/interaction/targets.d.ts +15 -0
- package/dist/interaction/targets.d.ts.map +1 -0
- package/dist/interaction/targets.js +20 -0
- package/dist/interaction/targets.js.map +1 -0
- package/dist/interaction/tree-navigation.d.ts +111 -0
- package/dist/interaction/tree-navigation.d.ts.map +1 -0
- package/dist/interaction/tree-navigation.js +207 -0
- package/dist/interaction/tree-navigation.js.map +1 -0
- package/dist/interaction/typeahead.d.ts +73 -0
- package/dist/interaction/typeahead.d.ts.map +1 -0
- package/dist/interaction/typeahead.js +113 -0
- package/dist/interaction/typeahead.js.map +1 -0
- package/dist/internal.d.ts +16 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/{internal-DP-xLKbT.mjs → internal.js} +8 -7
- package/dist/internal.js.map +1 -0
- package/dist/media/breakpoints.d.ts +37 -0
- package/dist/media/breakpoints.d.ts.map +1 -0
- package/dist/media/breakpoints.js +64 -0
- package/dist/media/breakpoints.js.map +1 -0
- package/dist/media/index.d.ts +3 -0
- package/dist/media/index.js +4 -0
- package/dist/media/mediaquery.d.ts +40 -0
- package/dist/media/mediaquery.d.ts.map +1 -0
- package/dist/media/mediaquery.js +42 -0
- package/dist/media/mediaquery.js.map +1 -0
- package/dist/media/platform.d.ts +30 -0
- package/dist/media/platform.d.ts.map +1 -0
- package/dist/media/platform.js +50 -0
- package/dist/media/platform.js.map +1 -0
- package/dist/motion/index.d.ts +5 -0
- package/dist/motion/index.js +5 -0
- package/dist/motion/motion.d.ts +16 -0
- package/dist/motion/motion.d.ts.map +1 -0
- package/dist/motion/motion.js +25 -0
- package/dist/motion/motion.js.map +1 -0
- package/dist/motion/presence.d.ts +37 -0
- package/dist/motion/presence.d.ts.map +1 -0
- package/dist/motion/presence.js +65 -0
- package/dist/motion/presence.js.map +1 -0
- package/dist/motion/spring.d.ts +50 -0
- package/dist/motion/spring.d.ts.map +1 -0
- package/dist/motion/spring.js +92 -0
- package/dist/motion/spring.js.map +1 -0
- package/dist/motion/tween.d.ts +44 -0
- package/dist/motion/tween.d.ts.map +1 -0
- package/dist/motion/tween.js +65 -0
- package/dist/motion/tween.js.map +1 -0
- package/dist/net/broadcast.d.ts +39 -0
- package/dist/net/broadcast.d.ts.map +1 -0
- package/dist/net/broadcast.js +73 -0
- package/dist/net/broadcast.js.map +1 -0
- package/dist/net/index.d.ts +5 -0
- package/dist/net/index.js +6 -0
- package/dist/net/online.d.ts +24 -0
- package/dist/net/online.d.ts.map +1 -0
- package/dist/net/online.js +26 -0
- package/dist/net/online.js.map +1 -0
- package/dist/net/sharedHost.d.ts +64 -0
- package/dist/net/sharedHost.d.ts.map +1 -0
- package/dist/net/sharedHost.js +124 -0
- package/dist/net/sharedHost.js.map +1 -0
- package/dist/net/sse.d.ts +115 -0
- package/dist/net/sse.d.ts.map +1 -0
- package/dist/net/sse.js +138 -0
- package/dist/net/sse.js.map +1 -0
- package/dist/net/websocket.d.ts +172 -0
- package/dist/net/websocket.d.ts.map +1 -0
- package/dist/net/websocket.js +199 -0
- package/dist/net/websocket.js.map +1 -0
- package/dist/observers/bounds.d.ts +24 -0
- package/dist/observers/bounds.d.ts.map +1 -0
- package/dist/observers/bounds.js +51 -0
- package/dist/observers/bounds.js.map +1 -0
- package/dist/observers/index.d.ts +4 -0
- package/dist/observers/index.js +5 -0
- package/dist/observers/intersection.d.ts +18 -0
- package/dist/observers/intersection.d.ts.map +1 -0
- package/dist/observers/intersection.js +33 -0
- package/dist/observers/intersection.js.map +1 -0
- package/dist/observers/mutation.d.ts +25 -0
- package/dist/observers/mutation.d.ts.map +1 -0
- package/dist/observers/mutation.js +53 -0
- package/dist/observers/mutation.js.map +1 -0
- package/dist/observers/resize.d.ts +18 -0
- package/dist/observers/resize.d.ts.map +1 -0
- package/dist/observers/resize.js +33 -0
- package/dist/observers/resize.js.map +1 -0
- package/dist/state/history.d.ts +115 -0
- package/dist/state/history.d.ts.map +1 -0
- package/dist/state/history.js +167 -0
- package/dist/state/history.js.map +1 -0
- package/dist/state/index.d.ts +6 -0
- package/dist/state/index.js +7 -0
- package/dist/state/locale.d.ts +27 -0
- package/dist/state/locale.d.ts.map +1 -0
- package/dist/state/locale.js +25 -0
- package/dist/state/locale.js.map +1 -0
- package/dist/state/pagination.d.ts +53 -0
- package/dist/state/pagination.d.ts.map +1 -0
- package/dist/state/pagination.js +72 -0
- package/dist/state/pagination.js.map +1 -0
- package/dist/state/range.d.ts +9 -0
- package/dist/state/range.d.ts.map +1 -0
- package/dist/state/range.js +18 -0
- package/dist/state/range.js.map +1 -0
- package/dist/state/selection.d.ts +47 -0
- package/dist/state/selection.d.ts.map +1 -0
- package/dist/state/selection.js +39 -0
- package/dist/state/selection.js.map +1 -0
- package/dist/state/virtual.d.ts +201 -0
- package/dist/state/virtual.d.ts.map +1 -0
- package/dist/state/{index.mjs → virtual.js} +11 -313
- package/dist/state/virtual.js.map +1 -0
- package/dist/time/debounce.d.ts +43 -0
- package/dist/time/debounce.d.ts.map +1 -0
- package/dist/time/debounce.js +65 -0
- package/dist/time/debounce.js.map +1 -0
- package/dist/time/index.d.ts +6 -0
- package/dist/time/index.js +7 -0
- package/dist/time/interval.d.ts +37 -0
- package/dist/time/interval.d.ts.map +1 -0
- package/dist/time/interval.js +52 -0
- package/dist/time/interval.js.map +1 -0
- package/dist/time/relative.d.ts +8 -0
- package/dist/time/relative.d.ts.map +1 -0
- package/dist/time/relative.js +27 -0
- package/dist/time/relative.js.map +1 -0
- package/dist/time/throttle.d.ts +38 -0
- package/dist/time/throttle.d.ts.map +1 -0
- package/dist/time/throttle.js +39 -0
- package/dist/time/throttle.js.map +1 -0
- package/dist/time/ticks.d.ts +21 -0
- package/dist/time/ticks.d.ts.map +1 -0
- package/dist/time/ticks.js +30 -0
- package/dist/time/ticks.js.map +1 -0
- package/dist/time/timer.d.ts +32 -0
- package/dist/time/timer.d.ts.map +1 -0
- package/dist/time/timer.js +50 -0
- package/dist/time/timer.js.map +1 -0
- package/package.json +31 -31
- package/dist/device/index.d.mts +0 -345
- package/dist/device/index.d.mts.map +0 -1
- package/dist/device/index.mjs +0 -498
- package/dist/device/index.mjs.map +0 -1
- package/dist/dom/index.d.mts +0 -750
- package/dist/dom/index.d.mts.map +0 -1
- package/dist/dom/index.mjs +0 -1857
- package/dist/dom/index.mjs.map +0 -1
- package/dist/editable-text-1D31qthY.d.mts +0 -319
- package/dist/editable-text-1D31qthY.d.mts.map +0 -1
- package/dist/events/index.d.mts +0 -184
- package/dist/events/index.d.mts.map +0 -1
- package/dist/events/index.mjs +0 -315
- package/dist/events/index.mjs.map +0 -1
- package/dist/follow-tab-stop-Caog2qg1.mjs +0 -863
- package/dist/follow-tab-stop-Caog2qg1.mjs.map +0 -1
- package/dist/index-CmmxqSEi.d.mts +0 -149
- package/dist/index-CmmxqSEi.d.mts.map +0 -1
- package/dist/index.d.mts +0 -12
- package/dist/index.mjs +0 -12
- package/dist/input-modality-BXD383jg.mjs.map +0 -1
- package/dist/input-modality-BY-bSrEc.d.mts.map +0 -1
- package/dist/interaction/index.d.mts +0 -1194
- package/dist/interaction/index.d.mts.map +0 -1
- package/dist/interaction/index.mjs +0 -2012
- package/dist/interaction/index.mjs.map +0 -1
- package/dist/internal-DP-xLKbT.mjs.map +0 -1
- package/dist/media/index.d.mts +0 -109
- package/dist/media/index.d.mts.map +0 -1
- package/dist/media/index.mjs +0 -149
- package/dist/media/index.mjs.map +0 -1
- package/dist/motion/index.d.mts +0 -2
- package/dist/motion/index.mjs +0 -1
- package/dist/motion-DmFDppRn.mjs +0 -240
- package/dist/motion-DmFDppRn.mjs.map +0 -1
- package/dist/net/index.d.mts +0 -348
- package/dist/net/index.d.mts.map +0 -1
- package/dist/net/index.mjs +0 -535
- package/dist/net/index.mjs.map +0 -1
- package/dist/observers/index.d.mts +0 -84
- package/dist/observers/index.d.mts.map +0 -1
- package/dist/observers/index.mjs +0 -159
- package/dist/observers/index.mjs.map +0 -1
- package/dist/rolldown-runtime-D7D4PA-g.mjs +0 -13
- package/dist/state/index.d.mts +0 -444
- package/dist/state/index.d.mts.map +0 -1
- package/dist/state/index.mjs.map +0 -1
- package/dist/time/index.d.mts +0 -179
- package/dist/time/index.d.mts.map +0 -1
- package/dist/time/index.mjs +0 -242
- package/dist/time/index.mjs.map +0 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as Mount from "foldkit/mount";
|
|
2
|
+
/**
|
|
3
|
+
* The custom property `KeyboardInset` writes on its element: how much of the layout
|
|
4
|
+
* viewport's bottom the on-screen keyboard (or anything else the visual viewport leaves
|
|
5
|
+
* out) covers, in pixels.
|
|
6
|
+
*/
|
|
7
|
+
export declare const KEYBOARD_INSET = "--fk-keyboard-inset";
|
|
8
|
+
/**
|
|
9
|
+
* The part of the layout viewport's height below the visual viewport: what the on-screen
|
|
10
|
+
* keyboard covers. Rounded down, since a fraction of a pixel of zoom is no keyboard.
|
|
11
|
+
*/
|
|
12
|
+
export declare const keyboardInset: (view: {
|
|
13
|
+
readonly innerHeight: number;
|
|
14
|
+
readonly visualViewport: {
|
|
15
|
+
readonly height: number;
|
|
16
|
+
readonly offsetTop: number;
|
|
17
|
+
} | null;
|
|
18
|
+
}) => number;
|
|
19
|
+
/**
|
|
20
|
+
* The on-screen keyboard's height as a Mount, with no Message and no Model: where it is
|
|
21
|
+
* is the browser's. iOS Safari, and Chrome on Android unless the page asks otherwise,
|
|
22
|
+
* leave the layout viewport as it is when the keyboard opens, so a panel fixed to the
|
|
23
|
+
* screen keeps its height and its bottom under the keys. This writes `KEYBOARD_INSET` on
|
|
24
|
+
* the element, from the visual viewport's size and scroll, for a style to pad or shrink by
|
|
25
|
+
* (`var(--fk-keyboard-inset, 0px)`), and removes it when the element goes. Where there is
|
|
26
|
+
* no visual viewport it writes nothing.
|
|
27
|
+
*/
|
|
28
|
+
export declare const KeyboardInset: Mount.MountDefinitionWithArgs<"KeyboardInset", {}, never>;
|
|
29
|
+
//# sourceMappingURL=keyboard-inset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyboard-inset.d.ts","names":[],"sources":["../../src/dom/keyboard-inset.ts"],"mappings":";;;;;;qBAQa;;;;;qBAMA,gBAAa;WACf;WACA;aAA2B;aAAyB;;;;;;;;;;;;qBAkBlD,eAAa,MAAA"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Effect, Schema, Stream } from "effect";
|
|
2
|
+
import * as Mount from "foldkit/mount";
|
|
3
|
+
// src/dom/keyboard-inset.ts
|
|
4
|
+
/**
|
|
5
|
+
* The custom property `KeyboardInset` writes on its element: how much of the layout
|
|
6
|
+
* viewport's bottom the on-screen keyboard (or anything else the visual viewport leaves
|
|
7
|
+
* out) covers, in pixels.
|
|
8
|
+
*/
|
|
9
|
+
const KEYBOARD_INSET = "--fk-keyboard-inset";
|
|
10
|
+
/**
|
|
11
|
+
* The part of the layout viewport's height below the visual viewport: what the on-screen
|
|
12
|
+
* keyboard covers. Rounded down, since a fraction of a pixel of zoom is no keyboard.
|
|
13
|
+
*/
|
|
14
|
+
const keyboardInset = (view) => view.visualViewport === null ? 0 : Math.max(0, Math.floor(view.innerHeight - view.visualViewport.height - view.visualViewport.offsetTop));
|
|
15
|
+
/**
|
|
16
|
+
* The on-screen keyboard's height as a Mount, with no Message and no Model: where it is
|
|
17
|
+
* is the browser's. iOS Safari, and Chrome on Android unless the page asks otherwise,
|
|
18
|
+
* leave the layout viewport as it is when the keyboard opens, so a panel fixed to the
|
|
19
|
+
* screen keeps its height and its bottom under the keys. This writes `KEYBOARD_INSET` on
|
|
20
|
+
* the element, from the visual viewport's size and scroll, for a style to pad or shrink by
|
|
21
|
+
* (`var(--fk-keyboard-inset, 0px)`), and removes it when the element goes. Where there is
|
|
22
|
+
* no visual viewport it writes nothing.
|
|
23
|
+
*/
|
|
24
|
+
const KeyboardInset = Mount.defineStream("KeyboardInset", {
|
|
25
|
+
messages: [Schema.Never],
|
|
26
|
+
args: {},
|
|
27
|
+
execute: ({ element }) => Stream.callback(() => Effect.acquireRelease(Effect.sync(() => {
|
|
28
|
+
const view = element.ownerDocument.defaultView;
|
|
29
|
+
const viewport = view?.visualViewport;
|
|
30
|
+
if (!(element instanceof HTMLElement) || view === null || view === void 0 || viewport === null || viewport === void 0) return () => {};
|
|
31
|
+
const target = element;
|
|
32
|
+
const measure = () => target.style.setProperty(KEYBOARD_INSET, `${keyboardInset(view)}px`);
|
|
33
|
+
measure();
|
|
34
|
+
viewport.addEventListener("resize", measure);
|
|
35
|
+
viewport.addEventListener("scroll", measure);
|
|
36
|
+
return () => {
|
|
37
|
+
viewport.removeEventListener("resize", measure);
|
|
38
|
+
viewport.removeEventListener("scroll", measure);
|
|
39
|
+
target.style.removeProperty(KEYBOARD_INSET);
|
|
40
|
+
};
|
|
41
|
+
}), (release) => Effect.sync(release)))
|
|
42
|
+
});
|
|
43
|
+
export { KEYBOARD_INSET, KeyboardInset, keyboardInset };
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=keyboard-inset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyboard-inset.js","names":[],"sources":["../../src/dom/keyboard-inset.ts"],"sourcesContent":["import { Effect, Schema, Stream } from 'effect'\nimport * as Mount from 'foldkit/mount'\n\n/**\n * The custom property `KeyboardInset` writes on its element: how much of the layout\n * viewport's bottom the on-screen keyboard (or anything else the visual viewport leaves\n * out) covers, in pixels.\n */\nexport const KEYBOARD_INSET = '--fk-keyboard-inset'\n\n/**\n * The part of the layout viewport's height below the visual viewport: what the on-screen\n * keyboard covers. Rounded down, since a fraction of a pixel of zoom is no keyboard.\n */\nexport const keyboardInset = (view: {\n readonly innerHeight: number\n readonly visualViewport: { readonly height: number; readonly offsetTop: number } | null\n}): number =>\n view.visualViewport === null\n ? 0\n : Math.max(\n 0,\n Math.floor(view.innerHeight - view.visualViewport.height - view.visualViewport.offsetTop),\n )\n\n/**\n * The on-screen keyboard's height as a Mount, with no Message and no Model: where it is\n * is the browser's. iOS Safari, and Chrome on Android unless the page asks otherwise,\n * leave the layout viewport as it is when the keyboard opens, so a panel fixed to the\n * screen keeps its height and its bottom under the keys. This writes `KEYBOARD_INSET` on\n * the element, from the visual viewport's size and scroll, for a style to pad or shrink by\n * (`var(--fk-keyboard-inset, 0px)`), and removes it when the element goes. Where there is\n * no visual viewport it writes nothing.\n */\nexport const KeyboardInset = Mount.defineStream('KeyboardInset', {\n messages: [Schema.Never],\n args: {},\n execute: ({ element }) =>\n Stream.callback<never>(() =>\n Effect.acquireRelease(\n Effect.sync(() => {\n const view = element.ownerDocument.defaultView\n const viewport = view?.visualViewport\n if (\n !(element instanceof HTMLElement) ||\n view === null ||\n view === undefined ||\n viewport === null ||\n viewport === undefined\n ) {\n return () => {}\n }\n const target = element\n const measure = () => target.style.setProperty(KEYBOARD_INSET, `${keyboardInset(view)}px`)\n measure()\n viewport.addEventListener('resize', measure)\n viewport.addEventListener('scroll', measure)\n return () => {\n viewport.removeEventListener('resize', measure)\n viewport.removeEventListener('scroll', measure)\n target.style.removeProperty(KEYBOARD_INSET)\n }\n }),\n release => Effect.sync(release),\n ),\n ),\n})\n"],"mappings":";;;;;;;;AAQA,MAAa,iBAAiB;;;;;AAM9B,MAAa,iBAAiB,SAI5B,KAAK,mBAAmB,OACpB,IACA,KAAK,IACH,GACA,KAAK,MAAM,KAAK,cAAc,KAAK,eAAe,SAAS,KAAK,eAAe,SAAS,CAC1F;;;;;;;;;;AAWN,MAAa,gBAAgB,MAAM,aAAa,iBAAiB;CAC/D,UAAU,CAAC,OAAO,KAAK;CACvB,MAAM,CAAC;CACP,UAAU,EAAE,cACV,OAAO,eACL,OAAO,eACL,OAAO,WAAW;EAChB,MAAM,OAAO,QAAQ,cAAc;EACnC,MAAM,WAAW,MAAM;EACvB,IACE,EAAE,mBAAmB,gBACrB,SAAS,QACT,SAAS,KAAA,KACT,aAAa,QACb,aAAa,KAAA,GAEb,aAAa,CAAC;EAEhB,MAAM,SAAS;EACf,MAAM,gBAAgB,OAAO,MAAM,YAAY,gBAAgB,GAAG,cAAc,IAAI,EAAE,GAAG;EACzF,QAAQ;EACR,SAAS,iBAAiB,UAAU,OAAO;EAC3C,SAAS,iBAAiB,UAAU,OAAO;EAC3C,aAAa;GACX,SAAS,oBAAoB,UAAU,OAAO;GAC9C,SAAS,oBAAoB,UAAU,OAAO;GAC9C,OAAO,MAAM,eAAe,cAAc;EAC5C;CACF,CAAC,IACD,YAAW,OAAO,KAAK,OAAO,CAChC,CACF;AACJ,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import * as Mount from "foldkit/mount";
|
|
2
|
+
/** Locks the document's scroll while the element is mounted; nested locks release with the last. */
|
|
3
|
+
export declare const ScrollLock: Mount.MountDefinitionNoArgs<"ScrollLock", never>;
|
|
4
|
+
/** Makes everything outside the element inert while it is mounted, and restores it after. */
|
|
5
|
+
export declare const HideOutside: Mount.MountDefinitionNoArgs<"HideOutside", never>;
|
|
6
|
+
//# sourceMappingURL=layers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layers.d.ts","names":[],"sources":["../../src/dom/layers.ts"],"mappings":";;qBAgBa,YAAU,MAAA;;qBAWV,aAAW,MAAA"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Effect, Schema, Stream } from "effect";
|
|
2
|
+
import * as Mount from "foldkit/mount";
|
|
3
|
+
import * as Dom from "foldkit/dom";
|
|
4
|
+
// src/dom/layers.ts
|
|
5
|
+
/**
|
|
6
|
+
* Two Mounts over Foldkit's own DOM helpers, for an overlay that is not a
|
|
7
|
+
* native `<dialog>` or `popover`:
|
|
8
|
+
*
|
|
9
|
+
* - `ScrollLock` locks the document's scroll while mounted. Foldkit's lock is
|
|
10
|
+
* refcounted, so nested overlays release it only when the last one goes.
|
|
11
|
+
* - `HideOutside` marks everything outside the element inert while mounted,
|
|
12
|
+
* through `Dom.inertOthers`, keyed by an id this Mount mints.
|
|
13
|
+
*
|
|
14
|
+
* Both emit no Messages; they are element lifecycle.
|
|
15
|
+
*/
|
|
16
|
+
/** Locks the document's scroll while the element is mounted; nested locks release with the last. */
|
|
17
|
+
const ScrollLock = Mount.defineStream("ScrollLock", {
|
|
18
|
+
messages: [Schema.Never],
|
|
19
|
+
execute: () => Stream.callback(() => Effect.acquireRelease(Dom.lockScroll, () => Dom.unlockScroll))
|
|
20
|
+
});
|
|
21
|
+
const HIDE_OUTSIDE_ATTRIBUTE = "data-foldkit-plus-hide-outside";
|
|
22
|
+
let minted = 0;
|
|
23
|
+
/** Makes everything outside the element inert while it is mounted, and restores it after. */
|
|
24
|
+
const HideOutside = Mount.defineStream("HideOutside", {
|
|
25
|
+
messages: [Schema.Never],
|
|
26
|
+
execute: ({ element }) => Stream.callback(() => Effect.acquireRelease(Effect.gen(function* () {
|
|
27
|
+
const id = `hide-outside-${minted += 1}`;
|
|
28
|
+
element.setAttribute(HIDE_OUTSIDE_ATTRIBUTE, id);
|
|
29
|
+
yield* Dom.inertOthers(id, [`[${HIDE_OUTSIDE_ATTRIBUTE}="${id}"]`]);
|
|
30
|
+
return id;
|
|
31
|
+
}), (id) => Effect.andThen(Dom.restoreInert(id), () => Effect.sync(() => element.removeAttribute(HIDE_OUTSIDE_ATTRIBUTE)))))
|
|
32
|
+
});
|
|
33
|
+
export { HideOutside, ScrollLock };
|
|
34
|
+
|
|
35
|
+
//# sourceMappingURL=layers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layers.js","names":[],"sources":["../../src/dom/layers.ts"],"sourcesContent":["/**\n * Two Mounts over Foldkit's own DOM helpers, for an overlay that is not a\n * native `<dialog>` or `popover`:\n *\n * - `ScrollLock` locks the document's scroll while mounted. Foldkit's lock is\n * refcounted, so nested overlays release it only when the last one goes.\n * - `HideOutside` marks everything outside the element inert while mounted,\n * through `Dom.inertOthers`, keyed by an id this Mount mints.\n *\n * Both emit no Messages; they are element lifecycle.\n */\nimport { Effect, Schema, Stream } from 'effect'\nimport * as Dom from 'foldkit/dom'\nimport * as Mount from 'foldkit/mount'\n\n/** Locks the document's scroll while the element is mounted; nested locks release with the last. */\nexport const ScrollLock = Mount.defineStream('ScrollLock', {\n messages: [Schema.Never],\n execute: () =>\n Stream.callback<never>(() => Effect.acquireRelease(Dom.lockScroll, () => Dom.unlockScroll)),\n})\n\nconst HIDE_OUTSIDE_ATTRIBUTE = 'data-foldkit-plus-hide-outside'\n\nlet minted = 0\n\n/** Makes everything outside the element inert while it is mounted, and restores it after. */\nexport const HideOutside = Mount.defineStream('HideOutside', {\n messages: [Schema.Never],\n execute: ({ element }) =>\n Stream.callback<never>(() =>\n Effect.acquireRelease(\n Effect.gen(function* () {\n // A per-mount id: Foldkit keys the inert set by it, so two overlays\n // restore independently. The attribute is how the element is found.\n const id = `hide-outside-${(minted += 1)}`\n element.setAttribute(HIDE_OUTSIDE_ATTRIBUTE, id)\n yield* Dom.inertOthers(id, [`[${HIDE_OUTSIDE_ATTRIBUTE}=\"${id}\"]`])\n return id\n }),\n id =>\n Effect.andThen(Dom.restoreInert(id), () =>\n Effect.sync(() => element.removeAttribute(HIDE_OUTSIDE_ATTRIBUTE)),\n ),\n ),\n ),\n})\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,MAAa,aAAa,MAAM,aAAa,cAAc;CACzD,UAAU,CAAC,OAAO,KAAK;CACvB,eACE,OAAO,eAAsB,OAAO,eAAe,IAAI,kBAAkB,IAAI,YAAY,CAAC;AAC9F,CAAC;AAED,MAAM,yBAAyB;AAE/B,IAAI,SAAS;;AAGb,MAAa,cAAc,MAAM,aAAa,eAAe;CAC3D,UAAU,CAAC,OAAO,KAAK;CACvB,UAAU,EAAE,cACV,OAAO,eACL,OAAO,eACL,OAAO,IAAI,aAAa;EAGtB,MAAM,KAAK,gBAAiB,UAAU;EACtC,QAAQ,aAAa,wBAAwB,EAAE;EAC/C,OAAO,IAAI,YAAY,IAAI,CAAC,IAAI,uBAAuB,IAAI,GAAG,GAAG,CAAC;EAClE,OAAO;CACT,CAAC,IACD,OACE,OAAO,QAAQ,IAAI,aAAa,EAAE,SAChC,OAAO,WAAW,QAAQ,gBAAgB,sBAAsB,CAAC,CACnE,CACJ,CACF;AACJ,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import * as Mount from "foldkit/mount";
|
|
3
|
+
/** The custom properties `Measure` writes for a target named `name`. */
|
|
4
|
+
export declare const measured: (name: string) => {
|
|
5
|
+
readonly x: `--fk-${string}-x`;
|
|
6
|
+
readonly y: `--fk-${string}-y`;
|
|
7
|
+
readonly w: `--fk-${string}-w`;
|
|
8
|
+
readonly h: `--fk-${string}-h`;
|
|
9
|
+
readonly display: `--fk-${string}-display`;
|
|
10
|
+
/**
|
|
11
|
+
* `1` for the measure that finds the mark on another element than the
|
|
12
|
+
* last, `0` for any other (the layout moved or resized the same one, or
|
|
13
|
+
* it is the first found): an overlay that glides to a new mark scales its
|
|
14
|
+
* duration by it, so it keeps to its element as the layout settles.
|
|
15
|
+
*/
|
|
16
|
+
readonly moving: `--fk-${string}-moving`;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Measures marked elements as a Mount, for a view to draw something over them,
|
|
20
|
+
* such as an editor's selection outline, with no Message and no Model: where
|
|
21
|
+
* an element is, is presentation, known to the DOM and to no transition.
|
|
22
|
+
*
|
|
23
|
+
* For each named target, the first element in the mounted one's subtree that
|
|
24
|
+
* matches its selector is measured relative to the mounted element's scroll
|
|
25
|
+
* box, which is what an absolutely placed child of it is laid out in. The
|
|
26
|
+
* mounted element gets `--fk-<name>-x`, `-y`, `-w` and `-h` in pixels, and
|
|
27
|
+
* `--fk-<name>-display`: `block` while a target is found, `none` while none is,
|
|
28
|
+
* and `--fk-<name>-moving` (see `measured`).
|
|
29
|
+
*
|
|
30
|
+
* It measures again when the subtree changes (a selection moves) or the
|
|
31
|
+
* mounted element or a measured one changes size, in the frame the change is
|
|
32
|
+
* drawn in; and, at most once a frame, when the mounted element scrolls, the
|
|
33
|
+
* window resizes, an image or a font inside loads, and on every frame of a
|
|
34
|
+
* transition or an animation inside. What moves a target by any other way,
|
|
35
|
+
* such as a stylesheet added that shifts it without resizing it, is seen at
|
|
36
|
+
* the next of these.
|
|
37
|
+
*/
|
|
38
|
+
export declare const Measure: Mount.MountDefinitionWithArgs<"Measure", {
|
|
39
|
+
/** Each target by name, as a selector within the element: `{ selected: '[aria-selected="true"]' }`. */
|
|
40
|
+
targets: Schema.$Record<Schema.String, Schema.String>;
|
|
41
|
+
}, never>;
|
|
42
|
+
//# sourceMappingURL=measure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"measure.d.ts","names":[],"sources":["../../src/dom/measure.ts"],"mappings":";;;qBAIa,WAAQ;WAEjB;WACA;WACA;WACA;WACA;;;;;;;WAOA;;;;;;;;;;;;;;;;;;;;;;qBAuBS,SAAO,MAAA"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Effect, Schema, Stream } from "effect";
|
|
2
|
+
import * as Mount from "foldkit/mount";
|
|
3
|
+
// src/dom/measure.ts
|
|
4
|
+
/** The custom properties `Measure` writes for a target named `name`. */
|
|
5
|
+
const measured = (name) => ({
|
|
6
|
+
x: `--fk-${name}-x`,
|
|
7
|
+
y: `--fk-${name}-y`,
|
|
8
|
+
w: `--fk-${name}-w`,
|
|
9
|
+
h: `--fk-${name}-h`,
|
|
10
|
+
display: `--fk-${name}-display`,
|
|
11
|
+
/**
|
|
12
|
+
* `1` for the measure that finds the mark on another element than the
|
|
13
|
+
* last, `0` for any other (the layout moved or resized the same one, or
|
|
14
|
+
* it is the first found): an overlay that glides to a new mark scales its
|
|
15
|
+
* duration by it, so it keeps to its element as the layout settles.
|
|
16
|
+
*/
|
|
17
|
+
moving: `--fk-${name}-moving`
|
|
18
|
+
});
|
|
19
|
+
/**
|
|
20
|
+
* Measures marked elements as a Mount, for a view to draw something over them,
|
|
21
|
+
* such as an editor's selection outline, with no Message and no Model: where
|
|
22
|
+
* an element is, is presentation, known to the DOM and to no transition.
|
|
23
|
+
*
|
|
24
|
+
* For each named target, the first element in the mounted one's subtree that
|
|
25
|
+
* matches its selector is measured relative to the mounted element's scroll
|
|
26
|
+
* box, which is what an absolutely placed child of it is laid out in. The
|
|
27
|
+
* mounted element gets `--fk-<name>-x`, `-y`, `-w` and `-h` in pixels, and
|
|
28
|
+
* `--fk-<name>-display`: `block` while a target is found, `none` while none is,
|
|
29
|
+
* and `--fk-<name>-moving` (see `measured`).
|
|
30
|
+
*
|
|
31
|
+
* It measures again when the subtree changes (a selection moves) or the
|
|
32
|
+
* mounted element or a measured one changes size, in the frame the change is
|
|
33
|
+
* drawn in; and, at most once a frame, when the mounted element scrolls, the
|
|
34
|
+
* window resizes, an image or a font inside loads, and on every frame of a
|
|
35
|
+
* transition or an animation inside. What moves a target by any other way,
|
|
36
|
+
* such as a stylesheet added that shifts it without resizing it, is seen at
|
|
37
|
+
* the next of these.
|
|
38
|
+
*/
|
|
39
|
+
const Measure = Mount.defineStream("Measure", {
|
|
40
|
+
messages: [Schema.Never],
|
|
41
|
+
args: {
|
|
42
|
+
/** Each target by name, as a selector within the element: `{ selected: '[aria-selected="true"]' }`. */
|
|
43
|
+
targets: Schema.Record(Schema.String, Schema.String) },
|
|
44
|
+
execute: ({ element, targets }) => Stream.callback(() => Effect.acquireRelease(Effect.sync(() => {
|
|
45
|
+
const container = element;
|
|
46
|
+
const names = Object.keys(targets);
|
|
47
|
+
let watched = [];
|
|
48
|
+
const last = /* @__PURE__ */ new Map();
|
|
49
|
+
let frame;
|
|
50
|
+
let animating = false;
|
|
51
|
+
const measure = () => {
|
|
52
|
+
if (frame !== void 0) cancelAnimationFrame(frame);
|
|
53
|
+
frame = void 0;
|
|
54
|
+
const box = container.getBoundingClientRect();
|
|
55
|
+
const found = [];
|
|
56
|
+
for (const name of names) {
|
|
57
|
+
const property = measured(name);
|
|
58
|
+
const target = container.querySelector(targets[name]);
|
|
59
|
+
if (target === null) {
|
|
60
|
+
container.style.setProperty(property.display, "none");
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
found.push(target);
|
|
64
|
+
const previous = last.get(name);
|
|
65
|
+
last.set(name, target);
|
|
66
|
+
const rect = target.getBoundingClientRect();
|
|
67
|
+
const x = rect.left - box.left - container.clientLeft + container.scrollLeft;
|
|
68
|
+
const y = rect.top - box.top - container.clientTop + container.scrollTop;
|
|
69
|
+
container.style.setProperty(property.x, `${x}px`);
|
|
70
|
+
container.style.setProperty(property.y, `${y}px`);
|
|
71
|
+
container.style.setProperty(property.w, `${rect.width}px`);
|
|
72
|
+
container.style.setProperty(property.h, `${rect.height}px`);
|
|
73
|
+
container.style.setProperty(property.display, "block");
|
|
74
|
+
container.style.setProperty(property.moving, previous === void 0 || previous === target ? "0" : "1");
|
|
75
|
+
}
|
|
76
|
+
if (found.length !== watched.length || found.some((each, at) => each !== watched[at])) {
|
|
77
|
+
for (const each of watched) sizes.unobserve(each);
|
|
78
|
+
for (const each of found) sizes.observe(each);
|
|
79
|
+
watched = found;
|
|
80
|
+
}
|
|
81
|
+
if (animating) {
|
|
82
|
+
animating = container.getAnimations({ subtree: true }).some((animation) => animation.playState === "running");
|
|
83
|
+
if (animating) soon();
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const soon = () => {
|
|
87
|
+
if (frame === void 0) frame = requestAnimationFrame(measure);
|
|
88
|
+
};
|
|
89
|
+
const moving = () => {
|
|
90
|
+
animating = true;
|
|
91
|
+
soon();
|
|
92
|
+
};
|
|
93
|
+
const changes = new MutationObserver(measure);
|
|
94
|
+
const sizes = new ResizeObserver(measure);
|
|
95
|
+
document.fonts.addEventListener("loadingdone", soon);
|
|
96
|
+
changes.observe(container, {
|
|
97
|
+
subtree: true,
|
|
98
|
+
childList: true,
|
|
99
|
+
attributes: true
|
|
100
|
+
});
|
|
101
|
+
sizes.observe(container);
|
|
102
|
+
container.addEventListener("scroll", soon, { passive: true });
|
|
103
|
+
window.addEventListener("resize", soon);
|
|
104
|
+
container.addEventListener("load", soon, { capture: true });
|
|
105
|
+
container.addEventListener("transitionrun", moving);
|
|
106
|
+
container.addEventListener("animationstart", moving);
|
|
107
|
+
measure();
|
|
108
|
+
return () => {
|
|
109
|
+
if (frame !== void 0) cancelAnimationFrame(frame);
|
|
110
|
+
changes.disconnect();
|
|
111
|
+
sizes.disconnect();
|
|
112
|
+
container.removeEventListener("scroll", soon);
|
|
113
|
+
window.removeEventListener("resize", soon);
|
|
114
|
+
container.removeEventListener("load", soon, { capture: true });
|
|
115
|
+
document.fonts.removeEventListener("loadingdone", soon);
|
|
116
|
+
container.removeEventListener("transitionrun", moving);
|
|
117
|
+
container.removeEventListener("animationstart", moving);
|
|
118
|
+
};
|
|
119
|
+
}), (stop) => Effect.sync(stop)))
|
|
120
|
+
});
|
|
121
|
+
export { Measure, measured };
|
|
122
|
+
|
|
123
|
+
//# sourceMappingURL=measure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"measure.js","names":[],"sources":["../../src/dom/measure.ts"],"sourcesContent":["import { Effect, Schema, Stream } from 'effect'\nimport * as Mount from 'foldkit/mount'\n\n/** The custom properties `Measure` writes for a target named `name`. */\nexport const measured = (name: string) =>\n ({\n x: `--fk-${name}-x`,\n y: `--fk-${name}-y`,\n w: `--fk-${name}-w`,\n h: `--fk-${name}-h`,\n display: `--fk-${name}-display`,\n /**\n * `1` for the measure that finds the mark on another element than the\n * last, `0` for any other (the layout moved or resized the same one, or\n * it is the first found): an overlay that glides to a new mark scales its\n * duration by it, so it keeps to its element as the layout settles.\n */\n moving: `--fk-${name}-moving`,\n }) as const\n\n/**\n * Measures marked elements as a Mount, for a view to draw something over them,\n * such as an editor's selection outline, with no Message and no Model: where\n * an element is, is presentation, known to the DOM and to no transition.\n *\n * For each named target, the first element in the mounted one's subtree that\n * matches its selector is measured relative to the mounted element's scroll\n * box, which is what an absolutely placed child of it is laid out in. The\n * mounted element gets `--fk-<name>-x`, `-y`, `-w` and `-h` in pixels, and\n * `--fk-<name>-display`: `block` while a target is found, `none` while none is,\n * and `--fk-<name>-moving` (see `measured`).\n *\n * It measures again when the subtree changes (a selection moves) or the\n * mounted element or a measured one changes size, in the frame the change is\n * drawn in; and, at most once a frame, when the mounted element scrolls, the\n * window resizes, an image or a font inside loads, and on every frame of a\n * transition or an animation inside. What moves a target by any other way,\n * such as a stylesheet added that shifts it without resizing it, is seen at\n * the next of these.\n */\nexport const Measure = Mount.defineStream('Measure', {\n messages: [Schema.Never],\n args: {\n /** Each target by name, as a selector within the element: `{ selected: '[aria-selected=\"true\"]' }`. */\n targets: Schema.Record(Schema.String, Schema.String),\n },\n execute: ({ element, targets }) =>\n Stream.callback<never>(() =>\n Effect.acquireRelease(\n Effect.sync(() => {\n const container = element as HTMLElement\n const names = Object.keys(targets)\n let watched: ReadonlyArray<Element> = []\n // The element each target was last measured on, to tell a new mark from a moved layout.\n const last = new Map<string, Element>()\n let frame: number | undefined\n // Whether a transition or an animation may be moving something, until one frame finds none.\n let animating = false\n\n const measure = () => {\n if (frame !== undefined) cancelAnimationFrame(frame)\n frame = undefined\n const box = container.getBoundingClientRect()\n const found: Array<Element> = []\n for (const name of names) {\n const property = measured(name)\n const target = container.querySelector(targets[name]!)\n if (target === null) {\n container.style.setProperty(property.display, 'none')\n continue\n }\n found.push(target)\n const previous = last.get(name)\n last.set(name, target)\n const rect = target.getBoundingClientRect()\n // Relative to the scroll box: where the content is, not where the viewport shows it.\n const x = rect.left - box.left - container.clientLeft + container.scrollLeft\n const y = rect.top - box.top - container.clientTop + container.scrollTop\n container.style.setProperty(property.x, `${x}px`)\n container.style.setProperty(property.y, `${y}px`)\n container.style.setProperty(property.w, `${rect.width}px`)\n container.style.setProperty(property.h, `${rect.height}px`)\n container.style.setProperty(property.display, 'block')\n container.style.setProperty(\n property.moving,\n previous === undefined || previous === target ? '0' : '1',\n )\n }\n // A measured element that changes size, as an image loading does, is measured again.\n if (found.length !== watched.length || found.some((each, at) => each !== watched[at])) {\n for (const each of watched) sizes.unobserve(each)\n for (const each of found) sizes.observe(each)\n watched = found\n }\n if (animating) {\n // Asked each frame rather than counted: an element removed mid-transition ends none.\n animating = container\n .getAnimations({ subtree: true })\n .some(animation => animation.playState === 'running')\n if (animating) soon()\n }\n }\n const soon = () => {\n if (frame === undefined) frame = requestAnimationFrame(measure)\n }\n const moving = () => {\n animating = true\n soon()\n }\n\n // A change is measured as its records arrive, before the frame it is drawn in is\n // painted, so a box never shows one frame on the element it has left.\n const changes = new MutationObserver(measure)\n // Made, and the fonts listened to, before anything observes: where one is\n // missing (jsdom has neither a ResizeObserver nor `document.fonts`), the Mount\n // fails with nothing attached to call `measure`. A size is measured as it is\n // observed, after layout and before paint: a frame later, a popup whose width\n // settled as it opened showed its marker at the old width, past its edge.\n const sizes = new ResizeObserver(measure)\n document.fonts.addEventListener('loadingdone', soon)\n // Its own properties change the container's style too, but writing the same\n // values again records nothing, so a measure that changed nothing rests.\n changes.observe(container, { subtree: true, childList: true, attributes: true })\n sizes.observe(container)\n container.addEventListener('scroll', soon, { passive: true })\n window.addEventListener('resize', soon)\n // `load` does not bubble, so it is caught on the way down.\n container.addEventListener('load', soon, { capture: true })\n container.addEventListener('transitionrun', moving)\n container.addEventListener('animationstart', moving)\n measure()\n return () => {\n if (frame !== undefined) cancelAnimationFrame(frame)\n changes.disconnect()\n sizes.disconnect()\n container.removeEventListener('scroll', soon)\n window.removeEventListener('resize', soon)\n container.removeEventListener('load', soon, { capture: true })\n document.fonts.removeEventListener('loadingdone', soon)\n container.removeEventListener('transitionrun', moving)\n container.removeEventListener('animationstart', moving)\n }\n }),\n stop => Effect.sync(stop),\n ),\n ),\n})\n"],"mappings":";;;;AAIA,MAAa,YAAY,UACtB;CACC,GAAG,QAAQ,KAAK;CAChB,GAAG,QAAQ,KAAK;CAChB,GAAG,QAAQ,KAAK;CAChB,GAAG,QAAQ,KAAK;CAChB,SAAS,QAAQ,KAAK;;;;;;;CAOtB,QAAQ,QAAQ,KAAK;AACvB;;;;;;;;;;;;;;;;;;;;;AAsBF,MAAa,UAAU,MAAM,aAAa,WAAW;CACnD,UAAU,CAAC,OAAO,KAAK;CACvB,MAAM;;AAEJ,SAAS,OAAO,OAAO,OAAO,QAAQ,OAAO,MAAM,EACrD;CACA,UAAU,EAAE,SAAS,cACnB,OAAO,eACL,OAAO,eACL,OAAO,WAAW;EAChB,MAAM,YAAY;EAClB,MAAM,QAAQ,OAAO,KAAK,OAAO;EACjC,IAAI,UAAkC,CAAC;EAEvC,MAAM,uBAAO,IAAI,IAAqB;EACtC,IAAI;EAEJ,IAAI,YAAY;EAEhB,MAAM,gBAAgB;GACpB,IAAI,UAAU,KAAA,GAAW,qBAAqB,KAAK;GACnD,QAAQ,KAAA;GACR,MAAM,MAAM,UAAU,sBAAsB;GAC5C,MAAM,QAAwB,CAAC;GAC/B,KAAK,MAAM,QAAQ,OAAO;IACxB,MAAM,WAAW,SAAS,IAAI;IAC9B,MAAM,SAAS,UAAU,cAAc,QAAQ,KAAM;IACrD,IAAI,WAAW,MAAM;KACnB,UAAU,MAAM,YAAY,SAAS,SAAS,MAAM;KACpD;IACF;IACA,MAAM,KAAK,MAAM;IACjB,MAAM,WAAW,KAAK,IAAI,IAAI;IAC9B,KAAK,IAAI,MAAM,MAAM;IACrB,MAAM,OAAO,OAAO,sBAAsB;IAE1C,MAAM,IAAI,KAAK,OAAO,IAAI,OAAO,UAAU,aAAa,UAAU;IAClE,MAAM,IAAI,KAAK,MAAM,IAAI,MAAM,UAAU,YAAY,UAAU;IAC/D,UAAU,MAAM,YAAY,SAAS,GAAG,GAAG,EAAE,GAAG;IAChD,UAAU,MAAM,YAAY,SAAS,GAAG,GAAG,EAAE,GAAG;IAChD,UAAU,MAAM,YAAY,SAAS,GAAG,GAAG,KAAK,MAAM,GAAG;IACzD,UAAU,MAAM,YAAY,SAAS,GAAG,GAAG,KAAK,OAAO,GAAG;IAC1D,UAAU,MAAM,YAAY,SAAS,SAAS,OAAO;IACrD,UAAU,MAAM,YACd,SAAS,QACT,aAAa,KAAA,KAAa,aAAa,SAAS,MAAM,GACxD;GACF;GAEA,IAAI,MAAM,WAAW,QAAQ,UAAU,MAAM,MAAM,MAAM,OAAO,SAAS,QAAQ,GAAG,GAAG;IACrF,KAAK,MAAM,QAAQ,SAAS,MAAM,UAAU,IAAI;IAChD,KAAK,MAAM,QAAQ,OAAO,MAAM,QAAQ,IAAI;IAC5C,UAAU;GACZ;GACA,IAAI,WAAW;IAEb,YAAY,UACT,cAAc,EAAE,SAAS,KAAK,CAAC,CAAC,CAChC,MAAK,cAAa,UAAU,cAAc,SAAS;IACtD,IAAI,WAAW,KAAK;GACtB;EACF;EACA,MAAM,aAAa;GACjB,IAAI,UAAU,KAAA,GAAW,QAAQ,sBAAsB,OAAO;EAChE;EACA,MAAM,eAAe;GACnB,YAAY;GACZ,KAAK;EACP;EAIA,MAAM,UAAU,IAAI,iBAAiB,OAAO;EAM5C,MAAM,QAAQ,IAAI,eAAe,OAAO;EACxC,SAAS,MAAM,iBAAiB,eAAe,IAAI;EAGnD,QAAQ,QAAQ,WAAW;GAAE,SAAS;GAAM,WAAW;GAAM,YAAY;EAAK,CAAC;EAC/E,MAAM,QAAQ,SAAS;EACvB,UAAU,iBAAiB,UAAU,MAAM,EAAE,SAAS,KAAK,CAAC;EAC5D,OAAO,iBAAiB,UAAU,IAAI;EAEtC,UAAU,iBAAiB,QAAQ,MAAM,EAAE,SAAS,KAAK,CAAC;EAC1D,UAAU,iBAAiB,iBAAiB,MAAM;EAClD,UAAU,iBAAiB,kBAAkB,MAAM;EACnD,QAAQ;EACR,aAAa;GACX,IAAI,UAAU,KAAA,GAAW,qBAAqB,KAAK;GACnD,QAAQ,WAAW;GACnB,MAAM,WAAW;GACjB,UAAU,oBAAoB,UAAU,IAAI;GAC5C,OAAO,oBAAoB,UAAU,IAAI;GACzC,UAAU,oBAAoB,QAAQ,MAAM,EAAE,SAAS,KAAK,CAAC;GAC7D,SAAS,MAAM,oBAAoB,eAAe,IAAI;GACtD,UAAU,oBAAoB,iBAAiB,MAAM;GACrD,UAAU,oBAAoB,kBAAkB,MAAM;EACxD;CACF,CAAC,IACD,SAAQ,OAAO,KAAK,IAAI,CAC1B,CACF;AACJ,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import * as Mount from "foldkit/mount";
|
|
3
|
+
export declare const MoveStarted: Schema.TaggedStruct<"MoveStarted", {
|
|
4
|
+
readonly pointerId: Schema.Number;
|
|
5
|
+
readonly pointerType: Schema.String;
|
|
6
|
+
}>;
|
|
7
|
+
export type MoveStarted = typeof MoveStarted.Type;
|
|
8
|
+
export declare const Moved: Schema.TaggedStruct<"Moved", {
|
|
9
|
+
readonly pointerId: Schema.Number;
|
|
10
|
+
/** From where the pointer went down, in CSS pixels. */
|
|
11
|
+
readonly deltaX: Schema.Number;
|
|
12
|
+
readonly deltaY: Schema.Number;
|
|
13
|
+
readonly pointerType: Schema.String;
|
|
14
|
+
}>;
|
|
15
|
+
export type Moved = typeof Moved.Type;
|
|
16
|
+
export declare const MoveEnded: Schema.TaggedStruct<"MoveEnded", {
|
|
17
|
+
readonly pointerId: Schema.Number;
|
|
18
|
+
/** `false` when the pointer was cancelled or capture was lost. */
|
|
19
|
+
readonly completed: Schema.Boolean;
|
|
20
|
+
}>;
|
|
21
|
+
export type MoveEnded = typeof MoveEnded.Type;
|
|
22
|
+
export type Fact = MoveStarted | Moved | MoveEnded;
|
|
23
|
+
/**
|
|
24
|
+
* Pointer movement on an element as a Mount: a primary-button pointer down
|
|
25
|
+
* captures the pointer, each move reports the delta from where it started,
|
|
26
|
+
* and up, cancel, or lost capture ends it. Nothing is decided here: a drag
|
|
27
|
+
* threshold, a snap, or a constraint is the parent's `update`. Capture is
|
|
28
|
+
* released with the Mount.
|
|
29
|
+
*/
|
|
30
|
+
export declare const Move: Mount.MountDefinitionNoArgs<"Move", {
|
|
31
|
+
readonly _tag: "MoveStarted";
|
|
32
|
+
readonly pointerId: number;
|
|
33
|
+
readonly pointerType: string;
|
|
34
|
+
} | {
|
|
35
|
+
readonly _tag: "Moved";
|
|
36
|
+
readonly pointerId: number;
|
|
37
|
+
readonly deltaX: number;
|
|
38
|
+
readonly deltaY: number;
|
|
39
|
+
readonly pointerType: string;
|
|
40
|
+
} | {
|
|
41
|
+
readonly _tag: "MoveEnded";
|
|
42
|
+
readonly pointerId: number;
|
|
43
|
+
readonly completed: boolean;
|
|
44
|
+
}>;
|
|
45
|
+
//# sourceMappingURL=move.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"move.d.ts","names":[],"sources":["../../src/dom/move.ts"],"mappings":";;qBAGa,aAAW,OAAA;;;;YAIZ,qBAAqB,YAAY;qBAEhC,OAAK,OAAA;;;;;;;YAON,eAAe,MAAM;qBAEpB,WAAS,OAAA;;;;;YAKV,mBAAmB,UAAU;YAE7B,OAAO,cAAc,QAAQ;;;;;;;;qBAsB5B,MAAI,MAAA"}
|
package/dist/dom/move.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Effect, Queue, Schema, Stream } from "effect";
|
|
2
|
+
import * as Mount from "foldkit/mount";
|
|
3
|
+
// src/dom/move.ts
|
|
4
|
+
const MoveStarted = Schema.TaggedStruct("MoveStarted", {
|
|
5
|
+
pointerId: Schema.Number,
|
|
6
|
+
pointerType: Schema.String
|
|
7
|
+
});
|
|
8
|
+
const Moved = Schema.TaggedStruct("Moved", {
|
|
9
|
+
pointerId: Schema.Number,
|
|
10
|
+
/** From where the pointer went down, in CSS pixels. */
|
|
11
|
+
deltaX: Schema.Number,
|
|
12
|
+
deltaY: Schema.Number,
|
|
13
|
+
pointerType: Schema.String
|
|
14
|
+
});
|
|
15
|
+
const MoveEnded = Schema.TaggedStruct("MoveEnded", {
|
|
16
|
+
pointerId: Schema.Number,
|
|
17
|
+
/** `false` when the pointer was cancelled or capture was lost. */
|
|
18
|
+
completed: Schema.Boolean
|
|
19
|
+
});
|
|
20
|
+
/**
|
|
21
|
+
* Pointer movement on an element as a Mount: a primary-button pointer down
|
|
22
|
+
* captures the pointer, each move reports the delta from where it started,
|
|
23
|
+
* and up, cancel, or lost capture ends it. Nothing is decided here: a drag
|
|
24
|
+
* threshold, a snap, or a constraint is the parent's `update`. Capture is
|
|
25
|
+
* released with the Mount.
|
|
26
|
+
*/
|
|
27
|
+
const Move = Mount.defineStream("Move", {
|
|
28
|
+
messages: [
|
|
29
|
+
MoveStarted,
|
|
30
|
+
Moved,
|
|
31
|
+
MoveEnded
|
|
32
|
+
],
|
|
33
|
+
execute: ({ element }) => Stream.callback((queue) => Effect.acquireRelease(Effect.sync(() => {
|
|
34
|
+
const target = element;
|
|
35
|
+
let active = null;
|
|
36
|
+
const offer = (fact) => Queue.offerUnsafe(queue, fact);
|
|
37
|
+
const end = (pointerId, completed) => {
|
|
38
|
+
if (active === null || active.pointerId !== pointerId) return;
|
|
39
|
+
active = null;
|
|
40
|
+
try {
|
|
41
|
+
target.releasePointerCapture?.(pointerId);
|
|
42
|
+
} catch {}
|
|
43
|
+
offer(MoveEnded.make({
|
|
44
|
+
pointerId,
|
|
45
|
+
completed
|
|
46
|
+
}));
|
|
47
|
+
};
|
|
48
|
+
const listeners = [
|
|
49
|
+
["pointerdown", (event) => {
|
|
50
|
+
const pointer = event;
|
|
51
|
+
if ((pointer.button ?? 0) !== 0 || active !== null) return;
|
|
52
|
+
const pointerId = pointer.pointerId ?? 0;
|
|
53
|
+
active = {
|
|
54
|
+
pointerId,
|
|
55
|
+
x: pointer.clientX ?? 0,
|
|
56
|
+
y: pointer.clientY ?? 0
|
|
57
|
+
};
|
|
58
|
+
try {
|
|
59
|
+
target.setPointerCapture?.(pointerId);
|
|
60
|
+
} catch {}
|
|
61
|
+
offer(MoveStarted.make({
|
|
62
|
+
pointerId,
|
|
63
|
+
pointerType: pointer.pointerType ?? "mouse"
|
|
64
|
+
}));
|
|
65
|
+
}],
|
|
66
|
+
["pointermove", (event) => {
|
|
67
|
+
const pointer = event;
|
|
68
|
+
const pointerId = pointer.pointerId ?? 0;
|
|
69
|
+
if (active === null || active.pointerId !== pointerId) return;
|
|
70
|
+
offer(Moved.make({
|
|
71
|
+
pointerId,
|
|
72
|
+
deltaX: (pointer.clientX ?? 0) - active.x,
|
|
73
|
+
deltaY: (pointer.clientY ?? 0) - active.y,
|
|
74
|
+
pointerType: pointer.pointerType ?? "mouse"
|
|
75
|
+
}));
|
|
76
|
+
}],
|
|
77
|
+
["pointerup", (event) => end(event.pointerId ?? 0, true)],
|
|
78
|
+
["pointercancel", (event) => end(event.pointerId ?? 0, false)],
|
|
79
|
+
["lostpointercapture", (event) => end(event.pointerId ?? 0, false)]
|
|
80
|
+
];
|
|
81
|
+
for (const [type, listener] of listeners) element.addEventListener(type, listener);
|
|
82
|
+
return {
|
|
83
|
+
listeners,
|
|
84
|
+
current: () => active
|
|
85
|
+
};
|
|
86
|
+
}), ({ listeners, current }) => Effect.sync(() => {
|
|
87
|
+
for (const [type, listener] of listeners) element.removeEventListener(type, listener);
|
|
88
|
+
const pointer = current();
|
|
89
|
+
if (pointer !== null) try {
|
|
90
|
+
element.releasePointerCapture?.(pointer.pointerId);
|
|
91
|
+
} catch {}
|
|
92
|
+
})))
|
|
93
|
+
});
|
|
94
|
+
export { Move, MoveEnded, MoveStarted, Moved };
|
|
95
|
+
|
|
96
|
+
//# sourceMappingURL=move.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"move.js","names":[],"sources":["../../src/dom/move.ts"],"sourcesContent":["import { Effect, Queue, Schema, Stream } from 'effect'\nimport * as Mount from 'foldkit/mount'\n\nexport const MoveStarted = Schema.TaggedStruct('MoveStarted', {\n pointerId: Schema.Number,\n pointerType: Schema.String,\n})\nexport type MoveStarted = typeof MoveStarted.Type\n\nexport const Moved = Schema.TaggedStruct('Moved', {\n pointerId: Schema.Number,\n /** From where the pointer went down, in CSS pixels. */\n deltaX: Schema.Number,\n deltaY: Schema.Number,\n pointerType: Schema.String,\n})\nexport type Moved = typeof Moved.Type\n\nexport const MoveEnded = Schema.TaggedStruct('MoveEnded', {\n pointerId: Schema.Number,\n /** `false` when the pointer was cancelled or capture was lost. */\n completed: Schema.Boolean,\n})\nexport type MoveEnded = typeof MoveEnded.Type\n\nexport type Fact = MoveStarted | Moved | MoveEnded\n\ntype PointerLike = Event & {\n readonly pointerId?: number\n readonly button?: number\n readonly pointerType?: string\n readonly clientX?: number\n readonly clientY?: number\n}\n\ntype Capturing = Element & {\n setPointerCapture?: (pointerId: number) => void\n releasePointerCapture?: (pointerId: number) => void\n}\n\n/**\n * Pointer movement on an element as a Mount: a primary-button pointer down\n * captures the pointer, each move reports the delta from where it started,\n * and up, cancel, or lost capture ends it. Nothing is decided here: a drag\n * threshold, a snap, or a constraint is the parent's `update`. Capture is\n * released with the Mount.\n */\nexport const Move = Mount.defineStream('Move', {\n messages: [MoveStarted, Moved, MoveEnded],\n execute: ({ element }) =>\n Stream.callback<Fact>(queue =>\n Effect.acquireRelease(\n Effect.sync(() => {\n const target = element as Capturing\n let active: {\n readonly pointerId: number\n readonly x: number\n readonly y: number\n } | null = null\n const offer = (fact: Fact) => Queue.offerUnsafe(queue, fact)\n const end = (pointerId: number, completed: boolean) => {\n if (active === null || active.pointerId !== pointerId) return\n active = null\n try {\n target.releasePointerCapture?.(pointerId)\n } catch {\n // Capture may already be gone; the end is still reported.\n }\n offer(MoveEnded.make({ pointerId, completed }))\n }\n const listeners: ReadonlyArray<readonly [string, (event: Event) => void]> = [\n [\n 'pointerdown',\n event => {\n const pointer = event as PointerLike\n if ((pointer.button ?? 0) !== 0 || active !== null) return\n const pointerId = pointer.pointerId ?? 0\n active = { pointerId, x: pointer.clientX ?? 0, y: pointer.clientY ?? 0 }\n try {\n target.setPointerCapture?.(pointerId)\n } catch {\n // Without capture the move still reports while over the element.\n }\n offer(MoveStarted.make({ pointerId, pointerType: pointer.pointerType ?? 'mouse' }))\n },\n ],\n [\n 'pointermove',\n event => {\n const pointer = event as PointerLike\n const pointerId = pointer.pointerId ?? 0\n if (active === null || active.pointerId !== pointerId) return\n offer(\n Moved.make({\n pointerId,\n deltaX: (pointer.clientX ?? 0) - active.x,\n deltaY: (pointer.clientY ?? 0) - active.y,\n pointerType: pointer.pointerType ?? 'mouse',\n }),\n )\n },\n ],\n ['pointerup', event => end((event as PointerLike).pointerId ?? 0, true)],\n ['pointercancel', event => end((event as PointerLike).pointerId ?? 0, false)],\n ['lostpointercapture', event => end((event as PointerLike).pointerId ?? 0, false)],\n ]\n for (const [type, listener] of listeners) element.addEventListener(type, listener)\n return { listeners, current: () => active }\n }),\n ({ listeners, current }) =>\n Effect.sync(() => {\n for (const [type, listener] of listeners) element.removeEventListener(type, listener)\n const pointer = current()\n if (pointer !== null) {\n try {\n ;(element as Capturing).releasePointerCapture?.(pointer.pointerId)\n } catch {\n // Already released.\n }\n }\n }),\n ),\n ),\n})\n"],"mappings":";;;AAGA,MAAa,cAAc,OAAO,aAAa,eAAe;CAC5D,WAAW,OAAO;CAClB,aAAa,OAAO;AACtB,CAAC;AAGD,MAAa,QAAQ,OAAO,aAAa,SAAS;CAChD,WAAW,OAAO;;CAElB,QAAQ,OAAO;CACf,QAAQ,OAAO;CACf,aAAa,OAAO;AACtB,CAAC;AAGD,MAAa,YAAY,OAAO,aAAa,aAAa;CACxD,WAAW,OAAO;;CAElB,WAAW,OAAO;AACpB,CAAC;;;;;;;;AAyBD,MAAa,OAAO,MAAM,aAAa,QAAQ;CAC7C,UAAU;EAAC;EAAa;EAAO;CAAS;CACxC,UAAU,EAAE,cACV,OAAO,UAAe,UACpB,OAAO,eACL,OAAO,WAAW;EAChB,MAAM,SAAS;EACf,IAAI,SAIO;EACX,MAAM,SAAS,SAAe,MAAM,YAAY,OAAO,IAAI;EAC3D,MAAM,OAAO,WAAmB,cAAuB;GACrD,IAAI,WAAW,QAAQ,OAAO,cAAc,WAAW;GACvD,SAAS;GACT,IAAI;IACF,OAAO,wBAAwB,SAAS;GAC1C,QAAQ,CAER;GACA,MAAM,UAAU,KAAK;IAAE;IAAW;GAAU,CAAC,CAAC;EAChD;EACA,MAAM,YAAsE;GAC1E,CACE,gBACA,UAAS;IACP,MAAM,UAAU;IAChB,KAAK,QAAQ,UAAU,OAAO,KAAK,WAAW,MAAM;IACpD,MAAM,YAAY,QAAQ,aAAa;IACvC,SAAS;KAAE;KAAW,GAAG,QAAQ,WAAW;KAAG,GAAG,QAAQ,WAAW;IAAE;IACvE,IAAI;KACF,OAAO,oBAAoB,SAAS;IACtC,QAAQ,CAER;IACA,MAAM,YAAY,KAAK;KAAE;KAAW,aAAa,QAAQ,eAAe;IAAQ,CAAC,CAAC;GACpF,CACF;GACA,CACE,gBACA,UAAS;IACP,MAAM,UAAU;IAChB,MAAM,YAAY,QAAQ,aAAa;IACvC,IAAI,WAAW,QAAQ,OAAO,cAAc,WAAW;IACvD,MACE,MAAM,KAAK;KACT;KACA,SAAS,QAAQ,WAAW,KAAK,OAAO;KACxC,SAAS,QAAQ,WAAW,KAAK,OAAO;KACxC,aAAa,QAAQ,eAAe;IACtC,CAAC,CACH;GACF,CACF;GACA,CAAC,cAAa,UAAS,IAAK,MAAsB,aAAa,GAAG,IAAI,CAAC;GACvE,CAAC,kBAAiB,UAAS,IAAK,MAAsB,aAAa,GAAG,KAAK,CAAC;GAC5E,CAAC,uBAAsB,UAAS,IAAK,MAAsB,aAAa,GAAG,KAAK,CAAC;EACnF;EACA,KAAK,MAAM,CAAC,MAAM,aAAa,WAAW,QAAQ,iBAAiB,MAAM,QAAQ;EACjF,OAAO;GAAE;GAAW,eAAe;EAAO;CAC5C,CAAC,IACA,EAAE,WAAW,cACZ,OAAO,WAAW;EAChB,KAAK,MAAM,CAAC,MAAM,aAAa,WAAW,QAAQ,oBAAoB,MAAM,QAAQ;EACpF,MAAM,UAAU,QAAQ;EACxB,IAAI,YAAY,MACd,IAAI;GACD,QAAuB,wBAAwB,QAAQ,SAAS;EACnE,QAAQ,CAER;CAEJ,CAAC,CACL,CACF;AACJ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Stream } from "effect";
|
|
2
|
+
import { Url } from "foldkit/url";
|
|
3
|
+
/**
|
|
4
|
+
* The address of the link a reader means to follow, on the page's own
|
|
5
|
+
* origin: for a `Data.preload` before they go (`Site.routing`'s `preload`).
|
|
6
|
+
* A pointer or focus counts once it has rested on a link for 50 ms, and
|
|
7
|
+
* leaving first cancels it, so a pointer sweeping a list reads one page, not
|
|
8
|
+
* every page it crosses. A finger counts at once: the tap that follows is the
|
|
9
|
+
* navigation. Crossing the elements inside a link is staying on it. Without
|
|
10
|
+
* a document the stream is empty.
|
|
11
|
+
*/
|
|
12
|
+
export declare const pointedLinks: () => Stream.Stream<Url>;
|
|
13
|
+
//# sourceMappingURL=pointed-links.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pointed-links.d.ts","names":[],"sources":["../../src/dom/pointed-links.ts"],"mappings":";;;;;;;;;;;qBAiBa,oBAAmB,OAAO,OAAO"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Effect, Option, Queue, Stream } from "effect";
|
|
2
|
+
import { fromString } from "foldkit/url";
|
|
3
|
+
// src/dom/pointed-links.ts
|
|
4
|
+
/** How long, in milliseconds, a pointer or focus rests on a link before it counts as intent. */
|
|
5
|
+
const REST = 50;
|
|
6
|
+
const LISTENED = [
|
|
7
|
+
"pointerover",
|
|
8
|
+
"pointerout",
|
|
9
|
+
"focusin",
|
|
10
|
+
"focusout"
|
|
11
|
+
];
|
|
12
|
+
/**
|
|
13
|
+
* The address of the link a reader means to follow, on the page's own
|
|
14
|
+
* origin: for a `Data.preload` before they go (`Site.routing`'s `preload`).
|
|
15
|
+
* A pointer or focus counts once it has rested on a link for 50 ms, and
|
|
16
|
+
* leaving first cancels it, so a pointer sweeping a list reads one page, not
|
|
17
|
+
* every page it crosses. A finger counts at once: the tap that follows is the
|
|
18
|
+
* navigation. Crossing the elements inside a link is staying on it. Without
|
|
19
|
+
* a document the stream is empty.
|
|
20
|
+
*/
|
|
21
|
+
const pointedLinks = () => {
|
|
22
|
+
if (typeof document === "undefined") return Stream.empty;
|
|
23
|
+
const linkOf = (target) => {
|
|
24
|
+
const link = target instanceof Element ? target.closest("a[href]") : null;
|
|
25
|
+
return link instanceof HTMLAnchorElement && link.origin === window.location.origin ? Option.some(link) : Option.none();
|
|
26
|
+
};
|
|
27
|
+
return Stream.callback((queue) => Effect.acquireRelease(Effect.sync(() => {
|
|
28
|
+
let on;
|
|
29
|
+
let waiting;
|
|
30
|
+
const listener = (event) => {
|
|
31
|
+
const found = linkOf(event.target);
|
|
32
|
+
if (Option.isNone(found)) return;
|
|
33
|
+
const link = found.value;
|
|
34
|
+
if (event.type === "pointerout" || event.type === "focusout") {
|
|
35
|
+
const related = "relatedTarget" in event ? event.relatedTarget : null;
|
|
36
|
+
if (related instanceof Node && link.contains(related)) return;
|
|
37
|
+
if (link.href !== on) return;
|
|
38
|
+
on = void 0;
|
|
39
|
+
clearTimeout(waiting);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (link.href === on) return;
|
|
43
|
+
on = link.href;
|
|
44
|
+
clearTimeout(waiting);
|
|
45
|
+
const url = fromString(link.href);
|
|
46
|
+
if (Option.isNone(url)) return;
|
|
47
|
+
if ("pointerType" in event && event.pointerType === "touch") Queue.offerUnsafe(queue, url.value);
|
|
48
|
+
else waiting = setTimeout(() => Queue.offerUnsafe(queue, url.value), REST);
|
|
49
|
+
};
|
|
50
|
+
for (const type of LISTENED) document.addEventListener(type, listener);
|
|
51
|
+
return () => {
|
|
52
|
+
clearTimeout(waiting);
|
|
53
|
+
for (const type of LISTENED) document.removeEventListener(type, listener);
|
|
54
|
+
};
|
|
55
|
+
}), (release) => Effect.sync(release)));
|
|
56
|
+
};
|
|
57
|
+
export { pointedLinks };
|
|
58
|
+
|
|
59
|
+
//# sourceMappingURL=pointed-links.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pointed-links.js","names":[],"sources":["../../src/dom/pointed-links.ts"],"sourcesContent":["import { Effect, Option, Queue, Stream } from 'effect'\nimport { fromString, type Url } from 'foldkit/url'\n\n/** How long, in milliseconds, a pointer or focus rests on a link before it counts as intent. */\nconst REST = 50\n\nconst LISTENED = ['pointerover', 'pointerout', 'focusin', 'focusout'] as const\n\n/**\n * The address of the link a reader means to follow, on the page's own\n * origin: for a `Data.preload` before they go (`Site.routing`'s `preload`).\n * A pointer or focus counts once it has rested on a link for 50 ms, and\n * leaving first cancels it, so a pointer sweeping a list reads one page, not\n * every page it crosses. A finger counts at once: the tap that follows is the\n * navigation. Crossing the elements inside a link is staying on it. Without\n * a document the stream is empty.\n */\nexport const pointedLinks = (): Stream.Stream<Url> => {\n if (typeof document === 'undefined') return Stream.empty\n const linkOf = (target: EventTarget | null): Option.Option<HTMLAnchorElement> => {\n const link = target instanceof Element ? target.closest('a[href]') : null\n return link instanceof HTMLAnchorElement && link.origin === window.location.origin\n ? Option.some(link)\n : Option.none()\n }\n // One listener for all four events, so they are read in the order they\n // happened: a focus moving between links sends its focusout and focusin in\n // one task, and a merge of four streams could read them the other way round.\n return Stream.callback<Url>(queue =>\n Effect.acquireRelease(\n Effect.sync(() => {\n // The link the reader is on, by its address, and the wait for it to count.\n let on: string | undefined\n let waiting: ReturnType<typeof setTimeout> | undefined\n const listener = (event: Event): void => {\n const found = linkOf(event.target)\n if (Option.isNone(found)) return\n const link = found.value\n if (event.type === 'pointerout' || event.type === 'focusout') {\n const related = 'relatedTarget' in event ? event.relatedTarget : null\n // Leaving for an element inside the same link is staying on it.\n if (related instanceof Node && link.contains(related)) return\n if (link.href !== on) return\n on = undefined\n clearTimeout(waiting)\n return\n }\n if (link.href === on) return\n on = link.href\n clearTimeout(waiting)\n const url = fromString(link.href)\n if (Option.isNone(url)) return\n if ('pointerType' in event && event.pointerType === 'touch')\n Queue.offerUnsafe(queue, url.value)\n else waiting = setTimeout(() => Queue.offerUnsafe(queue, url.value), REST)\n }\n for (const type of LISTENED) document.addEventListener(type, listener)\n return () => {\n clearTimeout(waiting)\n for (const type of LISTENED) document.removeEventListener(type, listener)\n }\n }),\n release => Effect.sync(release),\n ),\n )\n}\n"],"mappings":";;;;AAIA,MAAM,OAAO;AAEb,MAAM,WAAW;CAAC;CAAe;CAAc;CAAW;AAAU;;;;;;;;;;AAWpE,MAAa,qBAAyC;CACpD,IAAI,OAAO,aAAa,aAAa,OAAO,OAAO;CACnD,MAAM,UAAU,WAAiE;EAC/E,MAAM,OAAO,kBAAkB,UAAU,OAAO,QAAQ,SAAS,IAAI;EACrE,OAAO,gBAAgB,qBAAqB,KAAK,WAAW,OAAO,SAAS,SACxE,OAAO,KAAK,IAAI,IAChB,OAAO,KAAK;CAClB;CAIA,OAAO,OAAO,UAAc,UAC1B,OAAO,eACL,OAAO,WAAW;EAEhB,IAAI;EACJ,IAAI;EACJ,MAAM,YAAY,UAAuB;GACvC,MAAM,QAAQ,OAAO,MAAM,MAAM;GACjC,IAAI,OAAO,OAAO,KAAK,GAAG;GAC1B,MAAM,OAAO,MAAM;GACnB,IAAI,MAAM,SAAS,gBAAgB,MAAM,SAAS,YAAY;IAC5D,MAAM,UAAU,mBAAmB,QAAQ,MAAM,gBAAgB;IAEjE,IAAI,mBAAmB,QAAQ,KAAK,SAAS,OAAO,GAAG;IACvD,IAAI,KAAK,SAAS,IAAI;IACtB,KAAK,KAAA;IACL,aAAa,OAAO;IACpB;GACF;GACA,IAAI,KAAK,SAAS,IAAI;GACtB,KAAK,KAAK;GACV,aAAa,OAAO;GACpB,MAAM,MAAM,WAAW,KAAK,IAAI;GAChC,IAAI,OAAO,OAAO,GAAG,GAAG;GACxB,IAAI,iBAAiB,SAAS,MAAM,gBAAgB,SAClD,MAAM,YAAY,OAAO,IAAI,KAAK;QAC/B,UAAU,iBAAiB,MAAM,YAAY,OAAO,IAAI,KAAK,GAAG,IAAI;EAC3E;EACA,KAAK,MAAM,QAAQ,UAAU,SAAS,iBAAiB,MAAM,QAAQ;EACrE,aAAa;GACX,aAAa,OAAO;GACpB,KAAK,MAAM,QAAQ,UAAU,SAAS,oBAAoB,MAAM,QAAQ;EAC1E;CACF,CAAC,IACD,YAAW,OAAO,KAAK,OAAO,CAChC,CACF;AACF"}
|