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,291 @@
|
|
|
1
|
+
import { Effect, Queue, Schema, Stream } from "effect";
|
|
2
|
+
import * as Mount from "foldkit/mount";
|
|
3
|
+
// src/dom/swipe.ts
|
|
4
|
+
const SwipeStarted = Schema.TaggedStruct("SwipeStarted", {
|
|
5
|
+
pointerId: Schema.Number,
|
|
6
|
+
pointerType: Schema.String,
|
|
7
|
+
/** The border box of what `measure` names (the element itself by default), as the swipe starts. */
|
|
8
|
+
width: Schema.Number,
|
|
9
|
+
height: Schema.Number
|
|
10
|
+
});
|
|
11
|
+
const Swiped = Schema.TaggedStruct("Swiped", {
|
|
12
|
+
pointerId: Schema.Number,
|
|
13
|
+
/** From where the pointer went down, in CSS pixels. */
|
|
14
|
+
deltaX: Schema.Number,
|
|
15
|
+
deltaY: Schema.Number
|
|
16
|
+
});
|
|
17
|
+
const SwipeEnded = Schema.TaggedStruct("SwipeEnded", {
|
|
18
|
+
pointerId: Schema.Number,
|
|
19
|
+
/** From where the pointer went down to where it came up, in CSS pixels. */
|
|
20
|
+
deltaX: Schema.Number,
|
|
21
|
+
deltaY: Schema.Number,
|
|
22
|
+
/**
|
|
23
|
+
* How fast the pointer was moving as it came up, in pixels a millisecond,
|
|
24
|
+
* over the last `SWIPE_WINDOW` milliseconds: 0 when it rested there first.
|
|
25
|
+
*/
|
|
26
|
+
velocityX: Schema.Number,
|
|
27
|
+
velocityY: Schema.Number,
|
|
28
|
+
/** `false` when the pointer was cancelled or capture was lost. */
|
|
29
|
+
completed: Schema.Boolean
|
|
30
|
+
});
|
|
31
|
+
/** How far back, in milliseconds, the release velocity looks. */
|
|
32
|
+
const SWIPE_WINDOW = 100;
|
|
33
|
+
/**
|
|
34
|
+
* What a swipe never starts on when its element `scrolls`: a field whose own
|
|
35
|
+
* drag selects text or moves a thumb, and anything marked `data-no-swipe`.
|
|
36
|
+
*/
|
|
37
|
+
const NO_SWIPE = "input, textarea, select, [contenteditable=\"\"], [contenteditable=\"true\"], [data-no-swipe]";
|
|
38
|
+
/** Every element a Swipe is mounted on: a touch inside one is that one's, not an outer one's. */
|
|
39
|
+
const owners = /* @__PURE__ */ new WeakSet();
|
|
40
|
+
/**
|
|
41
|
+
* The velocity of the samples within `SWIPE_WINDOW` of the last: the way
|
|
42
|
+
* from the oldest of them to the last over the time between. A pointer that
|
|
43
|
+
* came to rest before the last sample has no recent way to measure, so 0.
|
|
44
|
+
*/
|
|
45
|
+
const velocityOf = (samples) => {
|
|
46
|
+
const last = samples.at(-1);
|
|
47
|
+
if (last === void 0) return {
|
|
48
|
+
x: 0,
|
|
49
|
+
y: 0
|
|
50
|
+
};
|
|
51
|
+
const first = samples.find((sample) => last.at - sample.at <= 100) ?? last;
|
|
52
|
+
const elapsed = last.at - first.at;
|
|
53
|
+
return elapsed <= 0 ? {
|
|
54
|
+
x: 0,
|
|
55
|
+
y: 0
|
|
56
|
+
} : {
|
|
57
|
+
x: (last.x - first.x) / elapsed,
|
|
58
|
+
y: (last.y - first.y) / elapsed
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Whether anything from `target` up to `root` can still scroll along `axis`
|
|
63
|
+
* the way a finger moving `along` (positive is down or right) pulls it: a
|
|
64
|
+
* finger going down scrolls content toward its start.
|
|
65
|
+
*/
|
|
66
|
+
const canScroll = (target, root, axis, along) => {
|
|
67
|
+
for (let at = target; at !== null; at = at.parentElement) {
|
|
68
|
+
const overflow = getComputedStyle(at)[axis === "y" ? "overflowY" : "overflowX"];
|
|
69
|
+
if (overflow === "auto" || overflow === "scroll") {
|
|
70
|
+
const position = Math.abs(axis === "y" ? at.scrollTop : at.scrollLeft);
|
|
71
|
+
const room = axis === "y" ? at.scrollHeight - at.clientHeight : at.scrollWidth - at.clientWidth;
|
|
72
|
+
if (room > 1 && (along > 0 ? position > .5 : position < room - .5)) return true;
|
|
73
|
+
}
|
|
74
|
+
if (at === root) return false;
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* `Move` with the speed of its release, for a gesture that a flick ends
|
|
80
|
+
* (a sheet dragged to dismiss, a card swiped away): a primary-button pointer
|
|
81
|
+
* down (or, given a `slop`, its first move past it) captures the pointer,
|
|
82
|
+
* each move reports the delta from where it was pressed, and up reports the
|
|
83
|
+
* last delta with the velocity over the last `SWIPE_WINDOW` milliseconds, by
|
|
84
|
+
* the events' own time stamps. Cancel or lost capture ends it incomplete.
|
|
85
|
+
* The start reports the size of what is moved, so a distance can be read as
|
|
86
|
+
* a share of it. What a delta or a speed means is the parent's `update`.
|
|
87
|
+
* Capture is released with the Mount.
|
|
88
|
+
*
|
|
89
|
+
* Given `scrolls`, the element holds content that scrolls (a sheet's panel),
|
|
90
|
+
* and a touch is followed by touch events, whose move can still refuse the
|
|
91
|
+
* browser's scroll once the touch is under way, where a pointer's cannot:
|
|
92
|
+
* past the slop, a touch that runs along the axis and finds nothing under it
|
|
93
|
+
* that can scroll further its way swipes, and the browser scrolls any other.
|
|
94
|
+
* A touch inside an element with a Swipe of its own (a handle in the panel)
|
|
95
|
+
* is that one's, and none starts on a field or on `data-no-swipe`
|
|
96
|
+
* (`NO_SWIPE`).
|
|
97
|
+
*/
|
|
98
|
+
const Swipe = Mount.defineStream("Swipe", {
|
|
99
|
+
messages: [
|
|
100
|
+
SwipeStarted,
|
|
101
|
+
Swiped,
|
|
102
|
+
SwipeEnded
|
|
103
|
+
],
|
|
104
|
+
args: {
|
|
105
|
+
/**
|
|
106
|
+
* The nearest ancestor matching this selector is measured as the swipe
|
|
107
|
+
* starts, for what the swipe moves (a sheet's panel, dragged by its
|
|
108
|
+
* handle). Default: the element itself.
|
|
109
|
+
*/
|
|
110
|
+
measure: Schema.optional(Schema.String),
|
|
111
|
+
/**
|
|
112
|
+
* How far, in pixels, a press must move before it is a swipe. Until
|
|
113
|
+
* then nothing is captured or reported, so a press that stays put is
|
|
114
|
+
* still a click on whatever is under it (a tab inside a drag grip).
|
|
115
|
+
* Default 0: the press itself starts the swipe; with `scrolls`, the
|
|
116
|
+
* first move decides.
|
|
117
|
+
*/
|
|
118
|
+
slop: Schema.optional(Schema.Number),
|
|
119
|
+
/** The axis the element's content scrolls along, which a touch yields to; see above. */
|
|
120
|
+
scrolls: Schema.optional(Schema.Literals(["x", "y"])),
|
|
121
|
+
/** Follows touches alone: a mouse or a pen is left to the page (and to a handle's own Swipe). */
|
|
122
|
+
touchOnly: Schema.optional(Schema.Boolean)
|
|
123
|
+
},
|
|
124
|
+
execute: ({ element, measure, slop = 0, scrolls, touchOnly = false }) => Stream.callback((queue) => Effect.acquireRelease(Effect.sync(() => {
|
|
125
|
+
const target = element;
|
|
126
|
+
let active = null;
|
|
127
|
+
const offer = (fact) => Queue.offerUnsafe(queue, fact);
|
|
128
|
+
const sampleOf = (at, x = 0, y = 0) => ({
|
|
129
|
+
at,
|
|
130
|
+
x,
|
|
131
|
+
y
|
|
132
|
+
});
|
|
133
|
+
const end = (pointerId, last, completed, captured) => {
|
|
134
|
+
if (active === null || active.pointerId !== pointerId) return;
|
|
135
|
+
const { samples, started } = active;
|
|
136
|
+
active = null;
|
|
137
|
+
if (!started) return;
|
|
138
|
+
if (captured) try {
|
|
139
|
+
target.releasePointerCapture?.(pointerId);
|
|
140
|
+
} catch {}
|
|
141
|
+
const final = completed ? last : samples.at(-1) ?? last;
|
|
142
|
+
const all = completed ? [...samples, final] : samples;
|
|
143
|
+
const start = all[0] ?? final;
|
|
144
|
+
const velocity = completed ? velocityOf(all) : {
|
|
145
|
+
x: 0,
|
|
146
|
+
y: 0
|
|
147
|
+
};
|
|
148
|
+
offer(SwipeEnded.make({
|
|
149
|
+
pointerId,
|
|
150
|
+
deltaX: final.x - start.x,
|
|
151
|
+
deltaY: final.y - start.y,
|
|
152
|
+
velocityX: velocity.x,
|
|
153
|
+
velocityY: velocity.y,
|
|
154
|
+
completed
|
|
155
|
+
}));
|
|
156
|
+
};
|
|
157
|
+
const begin = (pointer, capture) => {
|
|
158
|
+
pointer.started = true;
|
|
159
|
+
if (capture) try {
|
|
160
|
+
target.setPointerCapture?.(pointer.pointerId);
|
|
161
|
+
} catch {}
|
|
162
|
+
const box = (measure === void 0 ? element : element.closest(measure) ?? element).getBoundingClientRect();
|
|
163
|
+
offer(SwipeStarted.make({
|
|
164
|
+
pointerId: pointer.pointerId,
|
|
165
|
+
pointerType: pointer.pointerType,
|
|
166
|
+
width: box.width,
|
|
167
|
+
height: box.height
|
|
168
|
+
}));
|
|
169
|
+
};
|
|
170
|
+
const moved = (pointer, sample) => {
|
|
171
|
+
const start = pointer.samples[0] ?? sample;
|
|
172
|
+
pointer.samples = [
|
|
173
|
+
start,
|
|
174
|
+
...pointer.samples.slice(1).filter((each) => sample.at - each.at <= 100),
|
|
175
|
+
sample
|
|
176
|
+
];
|
|
177
|
+
offer(Swiped.make({
|
|
178
|
+
pointerId: pointer.pointerId,
|
|
179
|
+
deltaX: sample.x - start.x,
|
|
180
|
+
deltaY: sample.y - start.y
|
|
181
|
+
}));
|
|
182
|
+
};
|
|
183
|
+
const ownTouch = (from) => {
|
|
184
|
+
if (!(from instanceof Element)) return false;
|
|
185
|
+
const field = from.closest(NO_SWIPE);
|
|
186
|
+
if (field !== null && element.contains(field)) return false;
|
|
187
|
+
for (let at = from; at !== null && at !== element; at = at.parentElement) if (owners.has(at)) return false;
|
|
188
|
+
return true;
|
|
189
|
+
};
|
|
190
|
+
const touchOf = (event, id) => Array.from(event.changedTouches).find((touch) => touch.identifier === id);
|
|
191
|
+
let touched = null;
|
|
192
|
+
const pointerListeners = [
|
|
193
|
+
["pointerdown", (event) => {
|
|
194
|
+
const pointer = event;
|
|
195
|
+
const type = pointer.pointerType ?? "mouse";
|
|
196
|
+
if (scrolls !== void 0 && type === "touch") return;
|
|
197
|
+
if (touchOnly && type !== "touch") return;
|
|
198
|
+
if ((pointer.button ?? 0) !== 0 || active !== null) return;
|
|
199
|
+
active = {
|
|
200
|
+
pointerId: pointer.pointerId ?? 0,
|
|
201
|
+
pointerType: type,
|
|
202
|
+
samples: [sampleOf(pointer.timeStamp, pointer.clientX, pointer.clientY)],
|
|
203
|
+
started: false
|
|
204
|
+
};
|
|
205
|
+
if (slop <= 0) begin(active, true);
|
|
206
|
+
}],
|
|
207
|
+
["pointermove", (event) => {
|
|
208
|
+
const pointer = event;
|
|
209
|
+
if (active === null || active.pointerId !== (pointer.pointerId ?? 0)) return;
|
|
210
|
+
const sample = sampleOf(pointer.timeStamp, pointer.clientX, pointer.clientY);
|
|
211
|
+
if (!active.started) {
|
|
212
|
+
const start = active.samples[0] ?? sample;
|
|
213
|
+
if (Math.hypot(sample.x - start.x, sample.y - start.y) < slop) return;
|
|
214
|
+
begin(active, true);
|
|
215
|
+
}
|
|
216
|
+
moved(active, sample);
|
|
217
|
+
}],
|
|
218
|
+
...[
|
|
219
|
+
"pointerup",
|
|
220
|
+
"pointercancel",
|
|
221
|
+
"lostpointercapture"
|
|
222
|
+
].map((type) => [type, (event) => {
|
|
223
|
+
const pointer = event;
|
|
224
|
+
if (active?.pointerType === "touch" && scrolls !== void 0) return;
|
|
225
|
+
end(pointer.pointerId ?? 0, sampleOf(pointer.timeStamp, pointer.clientX, pointer.clientY), type === "pointerup", true);
|
|
226
|
+
}])
|
|
227
|
+
];
|
|
228
|
+
const touchListeners = scrolls === void 0 ? [] : [
|
|
229
|
+
["touchstart", (event) => {
|
|
230
|
+
const touches = event;
|
|
231
|
+
const touch = touches.changedTouches[0];
|
|
232
|
+
if (active !== null || touches.touches.length !== 1 || touch === void 0) return;
|
|
233
|
+
if (!ownTouch(touches.target)) return;
|
|
234
|
+
touched = touches.target;
|
|
235
|
+
active = {
|
|
236
|
+
pointerId: touch.identifier,
|
|
237
|
+
pointerType: "touch",
|
|
238
|
+
samples: [sampleOf(touches.timeStamp, touch.clientX, touch.clientY)],
|
|
239
|
+
started: false
|
|
240
|
+
};
|
|
241
|
+
}],
|
|
242
|
+
["touchmove", (event) => {
|
|
243
|
+
const touches = event;
|
|
244
|
+
if (active === null) return;
|
|
245
|
+
const touch = touchOf(touches, active.pointerId);
|
|
246
|
+
if (touch === void 0) return;
|
|
247
|
+
const sample = sampleOf(touches.timeStamp, touch.clientX, touch.clientY);
|
|
248
|
+
if (!active.started) {
|
|
249
|
+
const start = active.samples[0] ?? sample;
|
|
250
|
+
const dx = sample.x - start.x;
|
|
251
|
+
const dy = sample.y - start.y;
|
|
252
|
+
if (Math.hypot(dx, dy) < Math.max(slop, 1)) return;
|
|
253
|
+
const along = scrolls === "y" ? dy : dx;
|
|
254
|
+
if (Math.abs(scrolls === "y" ? dx : dy) > Math.abs(along) || canScroll(touched ?? element, element, scrolls, along) || !touches.cancelable) {
|
|
255
|
+
active = null;
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
begin(active, false);
|
|
259
|
+
}
|
|
260
|
+
touches.preventDefault();
|
|
261
|
+
moved(active, sample);
|
|
262
|
+
}],
|
|
263
|
+
...["touchend", "touchcancel"].map((type) => [type, (event) => {
|
|
264
|
+
const touches = event;
|
|
265
|
+
if (active === null) return;
|
|
266
|
+
const touch = touchOf(touches, active.pointerId);
|
|
267
|
+
if (touch === void 0) return;
|
|
268
|
+
end(active.pointerId, sampleOf(touches.timeStamp, touch.clientX, touch.clientY), type === "touchend", false);
|
|
269
|
+
}])
|
|
270
|
+
];
|
|
271
|
+
owners.add(element);
|
|
272
|
+
for (const [type, listener] of pointerListeners) element.addEventListener(type, listener);
|
|
273
|
+
for (const [type, listener] of touchListeners) element.addEventListener(type, listener, { passive: false });
|
|
274
|
+
return {
|
|
275
|
+
pointerListeners,
|
|
276
|
+
touchListeners,
|
|
277
|
+
current: () => active
|
|
278
|
+
};
|
|
279
|
+
}), ({ pointerListeners, touchListeners, current }) => Effect.sync(() => {
|
|
280
|
+
owners.delete(element);
|
|
281
|
+
for (const [type, listener] of pointerListeners) element.removeEventListener(type, listener);
|
|
282
|
+
for (const [type, listener] of touchListeners) element.removeEventListener(type, listener);
|
|
283
|
+
const pointer = current();
|
|
284
|
+
if (pointer !== null && !(pointer.pointerType === "touch" && scrolls !== void 0)) try {
|
|
285
|
+
element.releasePointerCapture?.(pointer.pointerId);
|
|
286
|
+
} catch {}
|
|
287
|
+
})))
|
|
288
|
+
});
|
|
289
|
+
export { NO_SWIPE, SWIPE_WINDOW, Swipe, SwipeEnded, SwipeStarted, Swiped, canScroll, velocityOf };
|
|
290
|
+
|
|
291
|
+
//# sourceMappingURL=swipe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swipe.js","names":[],"sources":["../../src/dom/swipe.ts"],"sourcesContent":["import { Effect, Queue, Schema, Stream } from 'effect'\nimport * as Mount from 'foldkit/mount'\n\nexport const SwipeStarted = Schema.TaggedStruct('SwipeStarted', {\n pointerId: Schema.Number,\n pointerType: Schema.String,\n /** The border box of what `measure` names (the element itself by default), as the swipe starts. */\n width: Schema.Number,\n height: Schema.Number,\n})\nexport type SwipeStarted = typeof SwipeStarted.Type\n\nexport const Swiped = Schema.TaggedStruct('Swiped', {\n pointerId: Schema.Number,\n /** From where the pointer went down, in CSS pixels. */\n deltaX: Schema.Number,\n deltaY: Schema.Number,\n})\nexport type Swiped = typeof Swiped.Type\n\nexport const SwipeEnded = Schema.TaggedStruct('SwipeEnded', {\n pointerId: Schema.Number,\n /** From where the pointer went down to where it came up, in CSS pixels. */\n deltaX: Schema.Number,\n deltaY: Schema.Number,\n /**\n * How fast the pointer was moving as it came up, in pixels a millisecond,\n * over the last `SWIPE_WINDOW` milliseconds: 0 when it rested there first.\n */\n velocityX: Schema.Number,\n velocityY: Schema.Number,\n /** `false` when the pointer was cancelled or capture was lost. */\n completed: Schema.Boolean,\n})\nexport type SwipeEnded = typeof SwipeEnded.Type\n\nexport type SwipeFact = SwipeStarted | Swiped | SwipeEnded\n\n/** How far back, in milliseconds, the release velocity looks. */\nexport const SWIPE_WINDOW = 100\n\n/**\n * What a swipe never starts on when its element `scrolls`: a field whose own\n * drag selects text or moves a thumb, and anything marked `data-no-swipe`.\n */\nexport const NO_SWIPE =\n 'input, textarea, select, [contenteditable=\"\"], [contenteditable=\"true\"], [data-no-swipe]'\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\ninterface Sample {\n readonly at: number\n readonly x: number\n readonly y: number\n}\n\n/** A pointer or a touch being followed, from where it went down. */\ninterface Active {\n readonly pointerId: number\n readonly pointerType: string\n samples: Array<Sample>\n started: boolean\n}\n\n/** Every element a Swipe is mounted on: a touch inside one is that one's, not an outer one's. */\nconst owners = new WeakSet<Element>()\n\n/**\n * The velocity of the samples within `SWIPE_WINDOW` of the last: the way\n * from the oldest of them to the last over the time between. A pointer that\n * came to rest before the last sample has no recent way to measure, so 0.\n */\nexport const velocityOf = (samples: ReadonlyArray<Sample>): { x: number; y: number } => {\n const last = samples.at(-1)\n if (last === undefined) return { x: 0, y: 0 }\n const first = samples.find(sample => last.at - sample.at <= SWIPE_WINDOW) ?? last\n const elapsed = last.at - first.at\n return elapsed <= 0\n ? { x: 0, y: 0 }\n : { x: (last.x - first.x) / elapsed, y: (last.y - first.y) / elapsed }\n}\n\n/**\n * Whether anything from `target` up to `root` can still scroll along `axis`\n * the way a finger moving `along` (positive is down or right) pulls it: a\n * finger going down scrolls content toward its start.\n */\nexport const canScroll = (\n target: Element,\n root: Element,\n axis: 'x' | 'y',\n along: number,\n): boolean => {\n for (let at: Element | null = target; at !== null; at = at.parentElement) {\n const overflow = getComputedStyle(at)[axis === 'y' ? 'overflowY' : 'overflowX']\n if (overflow === 'auto' || overflow === 'scroll') {\n const position = Math.abs(axis === 'y' ? at.scrollTop : at.scrollLeft)\n const room =\n axis === 'y' ? at.scrollHeight - at.clientHeight : at.scrollWidth - at.clientWidth\n if (room > 1 && (along > 0 ? position > 0.5 : position < room - 0.5)) return true\n }\n if (at === root) return false\n }\n return false\n}\n\n/**\n * `Move` with the speed of its release, for a gesture that a flick ends\n * (a sheet dragged to dismiss, a card swiped away): a primary-button pointer\n * down (or, given a `slop`, its first move past it) captures the pointer,\n * each move reports the delta from where it was pressed, and up reports the\n * last delta with the velocity over the last `SWIPE_WINDOW` milliseconds, by\n * the events' own time stamps. Cancel or lost capture ends it incomplete.\n * The start reports the size of what is moved, so a distance can be read as\n * a share of it. What a delta or a speed means is the parent's `update`.\n * Capture is released with the Mount.\n *\n * Given `scrolls`, the element holds content that scrolls (a sheet's panel),\n * and a touch is followed by touch events, whose move can still refuse the\n * browser's scroll once the touch is under way, where a pointer's cannot:\n * past the slop, a touch that runs along the axis and finds nothing under it\n * that can scroll further its way swipes, and the browser scrolls any other.\n * A touch inside an element with a Swipe of its own (a handle in the panel)\n * is that one's, and none starts on a field or on `data-no-swipe`\n * (`NO_SWIPE`).\n */\nexport const Swipe = Mount.defineStream('Swipe', {\n messages: [SwipeStarted, Swiped, SwipeEnded],\n args: {\n /**\n * The nearest ancestor matching this selector is measured as the swipe\n * starts, for what the swipe moves (a sheet's panel, dragged by its\n * handle). Default: the element itself.\n */\n measure: Schema.optional(Schema.String),\n /**\n * How far, in pixels, a press must move before it is a swipe. Until\n * then nothing is captured or reported, so a press that stays put is\n * still a click on whatever is under it (a tab inside a drag grip).\n * Default 0: the press itself starts the swipe; with `scrolls`, the\n * first move decides.\n */\n slop: Schema.optional(Schema.Number),\n /** The axis the element's content scrolls along, which a touch yields to; see above. */\n scrolls: Schema.optional(Schema.Literals(['x', 'y'])),\n /** Follows touches alone: a mouse or a pen is left to the page (and to a handle's own Swipe). */\n touchOnly: Schema.optional(Schema.Boolean),\n },\n execute: ({ element, measure, slop = 0, scrolls, touchOnly = false }) =>\n Stream.callback<SwipeFact>(queue =>\n Effect.acquireRelease(\n Effect.sync(() => {\n const target = element as Capturing\n let active: Active | null = null\n const offer = (fact: SwipeFact) => Queue.offerUnsafe(queue, fact)\n const sampleOf = (at: number, x = 0, y = 0): Sample => ({ at, x, y })\n const end = (pointerId: number, last: Sample, completed: boolean, captured: boolean) => {\n if (active === null || active.pointerId !== pointerId) return\n const { samples, started } = active\n active = null\n // A press that never moved past the slop was a click, not a swipe.\n if (!started) return\n if (captured) {\n try {\n target.releasePointerCapture?.(pointerId)\n } catch {\n // Capture may already be gone; the end is still reported.\n }\n }\n // A lost capture or a cancel carries no place: the last move's stands.\n const final = completed ? last : (samples.at(-1) ?? last)\n const all = completed ? [...samples, final] : samples\n const start = all[0] ?? final\n const velocity = completed ? velocityOf(all) : { x: 0, y: 0 }\n offer(\n SwipeEnded.make({\n pointerId,\n deltaX: final.x - start.x,\n deltaY: final.y - start.y,\n velocityX: velocity.x,\n velocityY: velocity.y,\n completed,\n }),\n )\n }\n // Report the size of what is moved; a pointer's swipe also captures it.\n const begin = (pointer: Active, capture: boolean) => {\n pointer.started = true\n if (capture) {\n try {\n target.setPointerCapture?.(pointer.pointerId)\n } catch {\n // Without capture the move still reports while over the element.\n }\n }\n const moved = measure === undefined ? element : (element.closest(measure) ?? element)\n const box = moved.getBoundingClientRect()\n offer(\n SwipeStarted.make({\n pointerId: pointer.pointerId,\n pointerType: pointer.pointerType,\n width: box.width,\n height: box.height,\n }),\n )\n }\n const moved = (pointer: Active, sample: Sample) => {\n const start = pointer.samples[0] ?? sample\n // Only what the velocity can still read, and where it began.\n pointer.samples = [\n start,\n ...pointer.samples.slice(1).filter(each => sample.at - each.at <= SWIPE_WINDOW),\n sample,\n ]\n offer(\n Swiped.make({\n pointerId: pointer.pointerId,\n deltaX: sample.x - start.x,\n deltaY: sample.y - start.y,\n }),\n )\n }\n // A touch this Swipe follows: started in it, on nothing of another Swipe's or NO_SWIPE.\n const ownTouch = (from: EventTarget | null): from is Element => {\n if (!(from instanceof Element)) return false\n const field = from.closest(NO_SWIPE)\n if (field !== null && element.contains(field)) return false\n for (\n let at: Element | null = from;\n at !== null && at !== element;\n at = at.parentElement\n )\n if (owners.has(at)) return false\n return true\n }\n const touchOf = (event: TouchEvent, id: number) =>\n Array.from(event.changedTouches).find(touch => touch.identifier === id)\n let touched: Element | null = null\n const pointerListeners: ReadonlyArray<readonly [string, (event: Event) => void]> = [\n [\n 'pointerdown',\n event => {\n const pointer = event as PointerLike\n const type = pointer.pointerType ?? 'mouse'\n // Touch events follow a touch when the content scrolls.\n if (scrolls !== undefined && type === 'touch') return\n if (touchOnly && type !== 'touch') return\n if ((pointer.button ?? 0) !== 0 || active !== null) return\n active = {\n pointerId: pointer.pointerId ?? 0,\n pointerType: type,\n samples: [sampleOf(pointer.timeStamp, pointer.clientX, pointer.clientY)],\n started: false,\n }\n if (slop <= 0) begin(active, true)\n },\n ],\n [\n 'pointermove',\n event => {\n const pointer = event as PointerLike\n if (active === null || active.pointerId !== (pointer.pointerId ?? 0)) return\n const sample = sampleOf(pointer.timeStamp, pointer.clientX, pointer.clientY)\n if (!active.started) {\n const start = active.samples[0] ?? sample\n if (Math.hypot(sample.x - start.x, sample.y - start.y) < slop) return\n begin(active, true)\n }\n moved(active, sample)\n },\n ],\n ...(['pointerup', 'pointercancel', 'lostpointercapture'] as const).map(\n type =>\n [\n type,\n (event: Event) => {\n const pointer = event as PointerLike\n if (active?.pointerType === 'touch' && scrolls !== undefined) return\n end(\n pointer.pointerId ?? 0,\n sampleOf(pointer.timeStamp, pointer.clientX, pointer.clientY),\n type === 'pointerup',\n true,\n )\n },\n ] as const,\n ),\n ]\n const touchListeners: ReadonlyArray<readonly [string, (event: Event) => void]> =\n scrolls === undefined\n ? []\n : [\n [\n 'touchstart',\n event => {\n const touches = event as TouchEvent\n const touch = touches.changedTouches[0]\n if (active !== null || touches.touches.length !== 1 || touch === undefined)\n return\n if (!ownTouch(touches.target)) return\n touched = touches.target as Element\n active = {\n pointerId: touch.identifier,\n pointerType: 'touch',\n samples: [sampleOf(touches.timeStamp, touch.clientX, touch.clientY)],\n started: false,\n }\n },\n ],\n [\n 'touchmove',\n event => {\n const touches = event as TouchEvent\n if (active === null) return\n const touch = touchOf(touches, active.pointerId)\n if (touch === undefined) return\n const sample = sampleOf(touches.timeStamp, touch.clientX, touch.clientY)\n if (!active.started) {\n const start = active.samples[0] ?? sample\n const dx = sample.x - start.x\n const dy = sample.y - start.y\n if (Math.hypot(dx, dy) < Math.max(slop, 1)) return\n const along = scrolls === 'y' ? dy : dx\n const across = scrolls === 'y' ? dx : dy\n // Across the axis, into content that scrolls, or already\n // the browser's: the page's, and followed no further.\n if (\n Math.abs(across) > Math.abs(along) ||\n canScroll(touched ?? element, element, scrolls, along) ||\n !touches.cancelable\n ) {\n active = null\n return\n }\n begin(active, false)\n }\n touches.preventDefault()\n moved(active, sample)\n },\n ],\n ...(['touchend', 'touchcancel'] as const).map(\n type =>\n [\n type,\n (event: Event) => {\n const touches = event as TouchEvent\n if (active === null) return\n const touch = touchOf(touches, active.pointerId)\n if (touch === undefined) return\n end(\n active.pointerId,\n sampleOf(touches.timeStamp, touch.clientX, touch.clientY),\n type === 'touchend',\n false,\n )\n },\n ] as const,\n ),\n ]\n owners.add(element)\n for (const [type, listener] of pointerListeners) element.addEventListener(type, listener)\n // Not passive: a move that swipes refuses the browser's scroll.\n for (const [type, listener] of touchListeners)\n element.addEventListener(type, listener, { passive: false })\n return { pointerListeners, touchListeners, current: () => active }\n }),\n ({ pointerListeners, touchListeners, current }) =>\n Effect.sync(() => {\n owners.delete(element)\n for (const [type, listener] of pointerListeners)\n element.removeEventListener(type, listener)\n for (const [type, listener] of touchListeners)\n element.removeEventListener(type, listener)\n const pointer = current()\n if (pointer !== null && !(pointer.pointerType === 'touch' && scrolls !== undefined)) {\n try {\n ;(element as Capturing).releasePointerCapture?.(pointer.pointerId)\n } catch {\n // Already released.\n }\n }\n }),\n ),\n ),\n})\n"],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import * as Mount from "foldkit/mount";
|
|
3
|
+
export declare const TargetHovered: Schema.TaggedStruct<"TargetHovered", {
|
|
4
|
+
/** The marked descendant under the pointer, or `null` when there is none. */
|
|
5
|
+
readonly id: Schema.NullOr<Schema.String>;
|
|
6
|
+
}>;
|
|
7
|
+
export type TargetHovered = typeof TargetHovered.Type;
|
|
8
|
+
export declare const TargetPressed: Schema.TaggedStruct<"TargetPressed", {
|
|
9
|
+
readonly id: Schema.String;
|
|
10
|
+
readonly shiftKey: Schema.Boolean;
|
|
11
|
+
readonly altKey: Schema.Boolean;
|
|
12
|
+
readonly ctrlKey: Schema.Boolean;
|
|
13
|
+
readonly metaKey: Schema.Boolean;
|
|
14
|
+
}>;
|
|
15
|
+
export type TargetPressed = typeof TargetPressed.Type;
|
|
16
|
+
export type TargetFact = TargetHovered | TargetPressed;
|
|
17
|
+
/** The id of the nearest marked element at or above `from`, inside `container`. */
|
|
18
|
+
export declare const targetOf: (container: Element, from: EventTarget | null, attribute: string) => string | null;
|
|
19
|
+
/**
|
|
20
|
+
* Which marked descendant of an element the pointer is over, and which one was
|
|
21
|
+
* pressed, as a Mount: one set of listeners on the container, not one per item.
|
|
22
|
+
* A descendant is marked by an attribute whose value is its id, such as a
|
|
23
|
+
* canvas's `data-composition-node` or a table's `data-row`. The nearest marked
|
|
24
|
+
* ancestor of the event's target, inside the container, is the one reported.
|
|
25
|
+
*
|
|
26
|
+
* What hovering or pressing means (a selection, an outline, a menu) is the
|
|
27
|
+
* parent's `update`.
|
|
28
|
+
*/
|
|
29
|
+
export declare const Targets: Mount.MountDefinitionWithArgs<"Targets", {
|
|
30
|
+
/** The attribute that marks a descendant, holding its id. */
|
|
31
|
+
attribute: Schema.String;
|
|
32
|
+
/** Prevent a press's default, such as a link navigating inside an editor's canvas. */
|
|
33
|
+
preventDefault: Schema.Boolean;
|
|
34
|
+
}, {
|
|
35
|
+
readonly _tag: "TargetHovered";
|
|
36
|
+
readonly id: string | null;
|
|
37
|
+
} | {
|
|
38
|
+
readonly _tag: "TargetPressed";
|
|
39
|
+
readonly id: string;
|
|
40
|
+
readonly shiftKey: boolean;
|
|
41
|
+
readonly altKey: boolean;
|
|
42
|
+
readonly ctrlKey: boolean;
|
|
43
|
+
readonly metaKey: boolean;
|
|
44
|
+
}>;
|
|
45
|
+
//# sourceMappingURL=targets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"targets.d.ts","names":[],"sources":["../../src/dom/targets.ts"],"mappings":";;qBAGa,eAAa,OAAA;;;;YAId,uBAAuB,cAAc;qBAEpC,eAAa,OAAA;;;;;;;YAOd,uBAAuB,cAAc;YAErC,aAAa,gBAAgB;;qBAU5B,WAAQ,WAAe,SAAO,MAAQ,oBAAkB;;;;;;;;;;;qBAgBxD,SAAO,MAAA"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Effect, Queue, Schema, Stream } from "effect";
|
|
2
|
+
import * as Mount from "foldkit/mount";
|
|
3
|
+
// src/dom/targets.ts
|
|
4
|
+
const TargetHovered = Schema.TaggedStruct("TargetHovered", {
|
|
5
|
+
/** The marked descendant under the pointer, or `null` when there is none. */
|
|
6
|
+
id: Schema.NullOr(Schema.String) });
|
|
7
|
+
const TargetPressed = Schema.TaggedStruct("TargetPressed", {
|
|
8
|
+
id: Schema.String,
|
|
9
|
+
shiftKey: Schema.Boolean,
|
|
10
|
+
altKey: Schema.Boolean,
|
|
11
|
+
ctrlKey: Schema.Boolean,
|
|
12
|
+
metaKey: Schema.Boolean
|
|
13
|
+
});
|
|
14
|
+
/** The id of the nearest marked element at or above `from`, inside `container`. */
|
|
15
|
+
const targetOf = (container, from, attribute) => {
|
|
16
|
+
if (!(from instanceof Element)) return null;
|
|
17
|
+
const marked = from.closest(`[${attribute}]`);
|
|
18
|
+
return marked !== null && container.contains(marked) ? marked.getAttribute(attribute) : null;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Which marked descendant of an element the pointer is over, and which one was
|
|
22
|
+
* pressed, as a Mount: one set of listeners on the container, not one per item.
|
|
23
|
+
* A descendant is marked by an attribute whose value is its id, such as a
|
|
24
|
+
* canvas's `data-composition-node` or a table's `data-row`. The nearest marked
|
|
25
|
+
* ancestor of the event's target, inside the container, is the one reported.
|
|
26
|
+
*
|
|
27
|
+
* What hovering or pressing means (a selection, an outline, a menu) is the
|
|
28
|
+
* parent's `update`.
|
|
29
|
+
*/
|
|
30
|
+
const Targets = Mount.defineStream("Targets", {
|
|
31
|
+
messages: [TargetHovered, TargetPressed],
|
|
32
|
+
args: {
|
|
33
|
+
/** The attribute that marks a descendant, holding its id. */
|
|
34
|
+
attribute: Schema.String,
|
|
35
|
+
/** Prevent a press's default, such as a link navigating inside an editor's canvas. */
|
|
36
|
+
preventDefault: Schema.Boolean
|
|
37
|
+
},
|
|
38
|
+
execute: ({ element, attribute, preventDefault }) => Stream.callback((queue) => Effect.acquireRelease(Effect.sync(() => {
|
|
39
|
+
let hovered = null;
|
|
40
|
+
const hover = (id) => {
|
|
41
|
+
if (id === hovered) return;
|
|
42
|
+
hovered = id;
|
|
43
|
+
Queue.offerUnsafe(queue, TargetHovered.make({ id }));
|
|
44
|
+
};
|
|
45
|
+
const listeners = [
|
|
46
|
+
["pointerover", (event) => hover(targetOf(element, event.target, attribute))],
|
|
47
|
+
["pointerleave", () => hover(null)],
|
|
48
|
+
["click", (event) => {
|
|
49
|
+
const id = targetOf(element, event.target, attribute);
|
|
50
|
+
if (id === null) return;
|
|
51
|
+
if (preventDefault) event.preventDefault();
|
|
52
|
+
const modified = event;
|
|
53
|
+
Queue.offerUnsafe(queue, TargetPressed.make({
|
|
54
|
+
id,
|
|
55
|
+
shiftKey: modified.shiftKey === true,
|
|
56
|
+
altKey: modified.altKey === true,
|
|
57
|
+
ctrlKey: modified.ctrlKey === true,
|
|
58
|
+
metaKey: modified.metaKey === true
|
|
59
|
+
}));
|
|
60
|
+
}]
|
|
61
|
+
];
|
|
62
|
+
for (const [type, listener] of listeners) element.addEventListener(type, listener);
|
|
63
|
+
return listeners;
|
|
64
|
+
}), (listeners) => Effect.sync(() => {
|
|
65
|
+
for (const [type, listener] of listeners) element.removeEventListener(type, listener);
|
|
66
|
+
})))
|
|
67
|
+
});
|
|
68
|
+
export { TargetHovered, TargetPressed, Targets, targetOf };
|
|
69
|
+
|
|
70
|
+
//# sourceMappingURL=targets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"targets.js","names":[],"sources":["../../src/dom/targets.ts"],"sourcesContent":["import { Effect, Queue, Schema, Stream } from 'effect'\nimport * as Mount from 'foldkit/mount'\n\nexport const TargetHovered = Schema.TaggedStruct('TargetHovered', {\n /** The marked descendant under the pointer, or `null` when there is none. */\n id: Schema.NullOr(Schema.String),\n})\nexport type TargetHovered = typeof TargetHovered.Type\n\nexport const TargetPressed = Schema.TaggedStruct('TargetPressed', {\n id: Schema.String,\n shiftKey: Schema.Boolean,\n altKey: Schema.Boolean,\n ctrlKey: Schema.Boolean,\n metaKey: Schema.Boolean,\n})\nexport type TargetPressed = typeof TargetPressed.Type\n\nexport type TargetFact = TargetHovered | TargetPressed\n\ntype Modified = Event & {\n readonly shiftKey?: boolean\n readonly altKey?: boolean\n readonly ctrlKey?: boolean\n readonly metaKey?: boolean\n}\n\n/** The id of the nearest marked element at or above `from`, inside `container`. */\nexport const targetOf = (container: Element, from: EventTarget | null, attribute: string) => {\n if (!(from instanceof Element)) return null\n const marked = from.closest(`[${attribute}]`)\n return marked !== null && container.contains(marked) ? marked.getAttribute(attribute) : null\n}\n\n/**\n * Which marked descendant of an element the pointer is over, and which one was\n * pressed, as a Mount: one set of listeners on the container, not one per item.\n * A descendant is marked by an attribute whose value is its id, such as a\n * canvas's `data-composition-node` or a table's `data-row`. The nearest marked\n * ancestor of the event's target, inside the container, is the one reported.\n *\n * What hovering or pressing means (a selection, an outline, a menu) is the\n * parent's `update`.\n */\nexport const Targets = Mount.defineStream('Targets', {\n messages: [TargetHovered, TargetPressed],\n args: {\n /** The attribute that marks a descendant, holding its id. */\n attribute: Schema.String,\n /** Prevent a press's default, such as a link navigating inside an editor's canvas. */\n preventDefault: Schema.Boolean,\n },\n execute: ({ element, attribute, preventDefault }) =>\n Stream.callback<TargetFact>(queue =>\n Effect.acquireRelease(\n Effect.sync(() => {\n let hovered: string | null = null\n const hover = (id: string | null) => {\n if (id === hovered) return\n hovered = id\n Queue.offerUnsafe(queue, TargetHovered.make({ id }))\n }\n const listeners: ReadonlyArray<readonly [string, (event: Event) => void]> = [\n ['pointerover', event => hover(targetOf(element, event.target, attribute))],\n ['pointerleave', () => hover(null)],\n [\n 'click',\n event => {\n const id = targetOf(element, event.target, attribute)\n if (id === null) return\n if (preventDefault) event.preventDefault()\n const modified = event as Modified\n Queue.offerUnsafe(\n queue,\n TargetPressed.make({\n id,\n shiftKey: modified.shiftKey === true,\n altKey: modified.altKey === true,\n ctrlKey: modified.ctrlKey === true,\n metaKey: modified.metaKey === true,\n }),\n )\n },\n ],\n ]\n for (const [type, listener] of listeners) element.addEventListener(type, listener)\n return listeners\n }),\n listeners =>\n Effect.sync(() => {\n for (const [type, listener] of listeners) element.removeEventListener(type, listener)\n }),\n ),\n ),\n})\n"],"mappings":";;;AAGA,MAAa,gBAAgB,OAAO,aAAa,iBAAiB;;AAEhE,IAAI,OAAO,OAAO,OAAO,MAAM,EACjC,CAAC;AAGD,MAAa,gBAAgB,OAAO,aAAa,iBAAiB;CAChE,IAAI,OAAO;CACX,UAAU,OAAO;CACjB,QAAQ,OAAO;CACf,SAAS,OAAO;CAChB,SAAS,OAAO;AAClB,CAAC;;AAaD,MAAa,YAAY,WAAoB,MAA0B,cAAsB;CAC3F,IAAI,EAAE,gBAAgB,UAAU,OAAO;CACvC,MAAM,SAAS,KAAK,QAAQ,IAAI,UAAU,EAAE;CAC5C,OAAO,WAAW,QAAQ,UAAU,SAAS,MAAM,IAAI,OAAO,aAAa,SAAS,IAAI;AAC1F;;;;;;;;;;;AAYA,MAAa,UAAU,MAAM,aAAa,WAAW;CACnD,UAAU,CAAC,eAAe,aAAa;CACvC,MAAM;;EAEJ,WAAW,OAAO;;EAElB,gBAAgB,OAAO;CACzB;CACA,UAAU,EAAE,SAAS,WAAW,qBAC9B,OAAO,UAAqB,UAC1B,OAAO,eACL,OAAO,WAAW;EAChB,IAAI,UAAyB;EAC7B,MAAM,SAAS,OAAsB;GACnC,IAAI,OAAO,SAAS;GACpB,UAAU;GACV,MAAM,YAAY,OAAO,cAAc,KAAK,EAAE,GAAG,CAAC,CAAC;EACrD;EACA,MAAM,YAAsE;GAC1E,CAAC,gBAAe,UAAS,MAAM,SAAS,SAAS,MAAM,QAAQ,SAAS,CAAC,CAAC;GAC1E,CAAC,sBAAsB,MAAM,IAAI,CAAC;GAClC,CACE,UACA,UAAS;IACP,MAAM,KAAK,SAAS,SAAS,MAAM,QAAQ,SAAS;IACpD,IAAI,OAAO,MAAM;IACjB,IAAI,gBAAgB,MAAM,eAAe;IACzC,MAAM,WAAW;IACjB,MAAM,YACJ,OACA,cAAc,KAAK;KACjB;KACA,UAAU,SAAS,aAAa;KAChC,QAAQ,SAAS,WAAW;KAC5B,SAAS,SAAS,YAAY;KAC9B,SAAS,SAAS,YAAY;IAChC,CAAC,CACH;GACF,CACF;EACF;EACA,KAAK,MAAM,CAAC,MAAM,aAAa,WAAW,QAAQ,iBAAiB,MAAM,QAAQ;EACjF,OAAO;CACT,CAAC,IACD,cACE,OAAO,WAAW;EAChB,KAAK,MAAM,CAAC,MAAM,aAAa,WAAW,QAAQ,oBAAoB,MAAM,QAAQ;CACtF,CAAC,CACL,CACF;AACJ,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import * as Mount from "foldkit/mount";
|
|
3
|
+
/** The attribute on the mounted element naming the element `TrackBox` follows, by id. */
|
|
4
|
+
export declare const TRACK_TARGET_ATTRIBUTE = "data-target";
|
|
5
|
+
/** The custom properties `TrackBox` writes for `name`, as `measured` names `Measure`'s. */
|
|
6
|
+
export declare const trackedBox: (name: string) => {
|
|
7
|
+
readonly x: `--fk-${string}-x`;
|
|
8
|
+
readonly y: `--fk-${string}-y`;
|
|
9
|
+
readonly w: `--fk-${string}-w`;
|
|
10
|
+
readonly h: `--fk-${string}-h`;
|
|
11
|
+
readonly radius: `--fk-${string}-radius`;
|
|
12
|
+
};
|
|
13
|
+
/** Sent as the followed element changes: its id, and whether it is on the page. */
|
|
14
|
+
export declare const TrackedTarget: Schema.TaggedStruct<"TrackedTarget", {
|
|
15
|
+
readonly target: Schema.String;
|
|
16
|
+
readonly found: Schema.Boolean;
|
|
17
|
+
}>;
|
|
18
|
+
export type TrackedTarget = typeof TrackedTarget.Type;
|
|
19
|
+
/**
|
|
20
|
+
* The radius a box of `width` by `height` draws for a computed
|
|
21
|
+
* `border-top-left-radius` of `value` (`8px`, `50%`, `8px 4px`): its
|
|
22
|
+
* horizontal length in pixels, a share of the width for a percentage, and
|
|
23
|
+
* never more than half the shorter side, where the browser stops it.
|
|
24
|
+
*/
|
|
25
|
+
export declare const cornerRadius: (value: string, width: number, height: number) => number;
|
|
26
|
+
/**
|
|
27
|
+
* Follows another element's box, as a Mount: the element whose id is the
|
|
28
|
+
* mounted element's `data-target` (`TRACK_TARGET_ATTRIBUTE`). Its box in
|
|
29
|
+
* viewport pixels is written on the mounted element as `--fk-<name>-x`,
|
|
30
|
+
* `-y`, `-w` and `-h`, and with `radius`, its top left corner's radius as
|
|
31
|
+
* `--fk-<name>-radius` (`cornerRadius`), so a fixed overlay drawn from them
|
|
32
|
+
* (a tour's cutout, a coach mark's ring) sits over it. While nothing is
|
|
33
|
+
* followed they are removed, so a stylist's fallbacks apply.
|
|
34
|
+
*
|
|
35
|
+
* The box is read again on a scroll anywhere (heard on the way down, since
|
|
36
|
+
* `scroll` does not bubble), a window resize, the followed element's own
|
|
37
|
+
* resize, and any change to the document's elements, which is also how a
|
|
38
|
+
* target drawn anew under the same id is found. `data-target` is read again
|
|
39
|
+
* whenever it changes, so one element can glide from target to target
|
|
40
|
+
* without being drawn again, which a Mount's args, read once, could not do.
|
|
41
|
+
* What moves the target by any other way (a stylesheet added) is seen at the
|
|
42
|
+
* next of these.
|
|
43
|
+
*
|
|
44
|
+
* Whether a target is on the page is the parent's to act on (a tour shows
|
|
45
|
+
* the step in the middle instead), so each change of the target or of
|
|
46
|
+
* whether it is found sends `TrackedTarget`, starting with the first one.
|
|
47
|
+
* Since it sends a Message, a component may draw it by default: a Scene test
|
|
48
|
+
* resolves it with what the parent maps it to. An empty `data-target` follows
|
|
49
|
+
* nothing and sends nothing.
|
|
50
|
+
*/
|
|
51
|
+
export declare const TrackBox: Mount.MountDefinitionWithArgs<"TrackBox", {
|
|
52
|
+
/** The custom properties' prefix: `tour` writes `--fk-tour-x`. */
|
|
53
|
+
name: Schema.String;
|
|
54
|
+
/** Whether to write the target's corner radius too; it reads the target's computed style. */
|
|
55
|
+
radius: Schema.optional<Schema.Boolean>;
|
|
56
|
+
}, {
|
|
57
|
+
readonly _tag: "TrackedTarget";
|
|
58
|
+
readonly target: string;
|
|
59
|
+
readonly found: boolean;
|
|
60
|
+
}>;
|
|
61
|
+
//# sourceMappingURL=track-box.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"track-box.d.ts","names":[],"sources":["../../src/dom/track-box.ts"],"mappings":";;;qBAIa;;qBAGA,aAAU;WAEnB;WACA;WACA;WACA;WACA;;;qBAIS,eAAa,OAAA;;;;YAId,uBAAuB,cAAc;;;;;;;qBAQpC,eAAY,eAAiB,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;qBAiC5C,UAAQ,MAAA"}
|