UNPKG

lisn.js

Version:

Simply handle user gestures and actions. Includes widgets.

1 lines 63.2 kB
{"version":3,"file":"view-watcher.cjs","names":["MC","_interopRequireWildcard","require","MH","_cssAlter","_domEvents","_domOptimize","_log","_misc","_overlays","_scroll","_size","_text","_views","_callback","_xMap","_xIntersectionObserver","_domWatcher","_scrollWatcher","_sizeWatcher","_debug","_interopRequireDefault","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","_defineProperty","_toPropertyKey","value","enumerable","configurable","writable","_toPrimitive","Symbol","toPrimitive","TypeError","String","Number","ViewWatcher","create","config","getConfig","CONSTRUCTOR_KEY","reuse","_instances$get","myConfig","configStrKey","objToStrKey","omitKeys","_root","instance","instances","sGet","constructor","key","illegalConstructorError","logger","debug","Logger","name","logAtCreation","allViewData","newWeakMap","allCallbacks","newXWeakMap","newMap","intersectionHandler","entries","debug9","length","entry","processEntry","observeOptions","root","threshold","_threshold","rootMargin","_rootMargin","xObserver","XIntersectionObserver","fetchCurrentView","element","realtime","fetchData","entryOrElement","intersection","fetchIntersectionData","data","fetchViewData","newPromise","resolve","observer","newIntersectionObserver","promise","disconnect","then","observe","createCallback","handler","options","trackType","_allCallbacks$get","_element","remove","debug5","callback","wrapCallback","onRemove","deleteHandler","_trackType","_options","setupOnView","target","userOptions","fetchOptions","waitForInteractive","viewData","rootBounds","S_WIDTH","S_HEIGHT","waitForSubsequentMeasureTime","TRACK_FULL","setupInviewTrack","isRemoved","observeLater","skipInitial","viewsToBitmask","views","_viewsBitmask","invokeCallback","removeOnView","_allCallbacks$get2","currEntry","setViewCssProps","deleteKey","prune","debug4","unobserve","targetOf","latestData","viewsBitmask","_allCallbacks$get3","values","viewCallback","debug8","sizeWatcher","SizeWatcher","scrollWatcher","ScrollWatcher","_debounceWindow","domWatcher","DOMWatcher","subtree","isInview","removeTrackCallback","scrollableAncestors","fetchScrollableAncestors","addTrackCallback","_config$_root","trackCallback","prevData","changed","viewChanged","onMutation","categories","S_ATTRIBUTE","S_SKIP_INITIAL","onResize","S_DEBOUNCE_WINDOW","_resizeThreshold","getWindow","ancestor","onScroll","scrollable","_scrollThreshold","enterOrLeaveCallback","target__ignored","S_AT","assign","VIEWS_SPACE","bitmask","TRACK_REGULAR","fetchElement","trackView","noTrackView","onView","offView","exports","SYMBOL","newXMap","_config$root","_config$rootMargin","_config$threshold","getViewsBitmask","debounceWindow","resizeThreshold","scrollThreshold","waitForMeasureTime","getClosestScrollable","active","push","compareValuesIn","copyBoundingRectProps","targetBounds","relative","bit","entryOrTarget","vpSize","fetchViewportSize","rootMargins","toMargins","isIntersecting","isCrossOrigin","isInstanceOf","IntersectionObserverEntry","boundingClientRect","fetchBounds","_target","_targetBounds","_rootMargins","_rootBounds","_isIntersecting","_isCrossOrigin","rect","getBoundingClientRect","width","height","x","left","right","y","top","bottom","S_LEFT","S_RIGHT","S_TOP","S_BOTTOM","_intersection$_isInte","vpHeight","vpWidth","fetchViews","merge","hMiddle","NaN","vMiddle","useScrollingAncestor","delta","_left","_right","_top","_bottom","xView","yView","S_BELOW","S_ABOVE","scrollingAncestor","_viewData$relative","props","setNumericStyleJsVars","_prefix","_numDecimal","isElement","isString","usageError","overlayOptions","getOverlayOptions","createOverlay","reference","parseScrollOffset","ovrDimension","parent","isHTMLElement","undefined","style","invoke","copyObject","catch","logError"],"sources":["../../../src/ts/watchers/view-watcher.ts"],"sourcesContent":["/**\n * @module Watchers/ViewWatcher\n */\n\nimport * as MC from \"@lisn/globals/minification-constants\";\nimport * as MH from \"@lisn/globals/minification-helpers\";\n\nimport {\n ViewTarget,\n View,\n BoundingRect,\n CommaSeparatedStr,\n} from \"@lisn/globals/types\";\n\nimport { setNumericStyleJsVars } from \"@lisn/utils/css-alter\";\nimport { waitForInteractive } from \"@lisn/utils/dom-events\";\nimport {\n waitForMeasureTime,\n waitForSubsequentMeasureTime,\n} from \"@lisn/utils/dom-optimize\";\nimport { logError } from \"@lisn/utils/log\";\nimport { omitKeys, compareValuesIn } from \"@lisn/utils/misc\";\nimport { createOverlay, OverlayOptions } from \"@lisn/utils/overlays\";\nimport { getClosestScrollable } from \"@lisn/utils/scroll\";\nimport { fetchViewportSize } from \"@lisn/utils/size\";\nimport { toMargins, objToStrKey } from \"@lisn/utils/text\";\nimport {\n VIEWS_SPACE,\n getViewsBitmask,\n parseScrollOffset,\n} from \"@lisn/utils/views\";\n\nimport {\n CallbackHandler,\n Callback,\n wrapCallback,\n} from \"@lisn/modules/callback\";\nimport { newXMap, newXWeakMap } from \"@lisn/modules/x-map\";\nimport { XIntersectionObserver } from \"@lisn/modules/x-intersection-observer\";\n\nimport { DOMWatcher } from \"@lisn/watchers/dom-watcher\";\nimport { ScrollWatcher } from \"@lisn/watchers/scroll-watcher\";\nimport { SizeWatcher } from \"@lisn/watchers/size-watcher\";\n\nimport debug from \"@lisn/debug/debug\";\n\n/**\n * {@link ViewWatcher} monitors the position of a given target relative to the\n * given {@link ViewWatcherConfig.root | root} or the viewport.\n *\n * It's built on top of\n * {@link https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver | IntersectionObserver}.\n *\n * It manages registered callbacks globally and reuses IntersectionObservers\n * for more efficient performance.\n */\nexport class ViewWatcher {\n /**\n * Call the given handler whenever the {@link ViewWatcherConfig.root | root}'s\n * view relative to the target position changes, i.e. when the target enters\n * or leaves the root.\n *\n * Unless {@link OnViewOptions.skipInitial} is true, the handler is also\n * called (almost) immediately with the current view if it matches this\n * set of options*.\n *\n * **IMPORTANT:** The same handler can _not_ be added multiple times for the\n * same target, even if the options differ. If the handler has already been\n * added for this target, either using {@link trackView} or using\n * {@link onView}, then it will be removed and re-added with the current\n * options. So if previously it was also tracking position across root\n * using {@link trackView}, it will no longer do so.\n *\n * @throws {@link Errors.LisnUsageError | LisnUsageError}\n * If the target or the options are invalid.\n */\n readonly onView: (\n target: ViewTarget,\n handler: OnViewHandler,\n options?: OnViewOptions,\n ) => Promise<void>;\n\n /**\n * Removes a previously added handler.\n *\n * @throws {@link Errors.LisnUsageError | LisnUsageError}\n * If the target is invalid.\n */\n readonly offView: (target: ViewTarget, handler: OnViewHandler) => void;\n\n /**\n * This does more than just {@link onView}. The difference is that in\n * addition to a change of {@link View}, such as the target entering or\n * leaving the ViewWatcher's {@link ViewWatcherConfig.root | root} (by\n * default the viewport), the handler is also called each time the target's\n * relative view changes _while inside the root_.\n *\n * A change of relative position happens when:\n * - the target is resized\n * - the root is resized\n * - the any of the target's scrollable ancestors is scrolled\n * - the target's attributes changed that resulted in a change of position\n *\n * All of the above are accounted for. Internally it uses\n * {@link ScrollWatcher}, {@link DOMWatcher} and {@link SizeWatcher} to keep\n * track of all of this.\n *\n * If the target is leaves the ViewWatcher's\n * {@link ViewWatcherConfig.root | root}, the handler will be called with\n * the {@link ViewData}, and the above events will stop being tracked until\n * the target enters the watcher's root again.\n *\n * **IMPORTANT:** The same handler can _not_ be added multiple times for the\n * same target, even if the options differ. If the handler has already been\n * added for this target, either using {@link trackView} or using\n * {@link onView}, then it will be removed and re-added with the current\n * options.\n *\n * ------\n *\n * If `handler` is not given, then it defaults to an internal handler that\n * updates the following set of CSS variables on the target's style and\n * represent its relative position:\n *\n * - `--lisn-js--r-top`\n * - `--lisn-js--r-bottom`\n * - `--lisn-js--r-left`\n * - `--lisn-js--r-right`\n * - `--lisn-js--r-width`\n * - `--lisn-js--r-height`\n * - `--lisn-js--r-h-middle`\n * - `--lisn-js--r-v-middle`\n *\n * See {@link ViewData.relative} for an explanation of each.\n *\n * Note that only Element targets are supported here and not offsets.\n *\n * @throws {@link Errors.LisnUsageError | LisnUsageError}\n * If the target or \"views\" are invalid.\n */\n readonly trackView: (\n element: Element,\n handler?: OnViewHandler | null,\n options?: TrackViewOptions,\n ) => Promise<void>;\n\n /**\n * Removes a previously added handler for {@link trackView}.\n *\n * @throws {@link Errors.LisnUsageError | LisnUsageError}\n * If the target is invalid.\n */\n readonly noTrackView: (\n element: Element,\n handler?: OnViewHandler | null,\n ) => void;\n\n /**\n * Get the current view relative to the target. By default, it will\n * {@link waitForMeasureTime} and so will be delayed by one frame.\n *\n * @param realtime If true, it will not {@link waitForMeasureTime}. Use\n * this only when doing realtime scroll-based animations\n * as it may cause a forced layout.\n */\n readonly fetchCurrentView: (\n target: ViewTarget,\n realtime?: boolean,\n ) => Promise<ViewData>;\n\n /**\n * Creates a new instance of ViewWatcher with the given\n * {@link ViewWatcherConfig}. It does not save it for future reuse.\n */\n static create(config?: ViewWatcherConfig) {\n return new ViewWatcher(getConfig(config), CONSTRUCTOR_KEY);\n }\n\n /**\n * Returns an existing instance of ViewWatcher with the given\n * {@link ViewWatcherConfig}, or creates a new one.\n *\n * **NOTE:** It saves it for future reuse, so don't use this for temporary\n * short-lived watchers.\n */\n static reuse(config?: ViewWatcherConfig) {\n const myConfig = getConfig(config);\n const configStrKey = objToStrKey(omitKeys(myConfig, { _root: null }));\n\n let instance = instances.get(myConfig._root)?.get(configStrKey);\n if (!instance) {\n instance = new ViewWatcher(myConfig, CONSTRUCTOR_KEY);\n instances.sGet(myConfig._root).set(configStrKey, instance);\n }\n\n return instance;\n }\n\n private constructor(\n config: ViewWatcherConfigInternal,\n key: typeof CONSTRUCTOR_KEY,\n ) {\n if (key !== CONSTRUCTOR_KEY) {\n throw MH.illegalConstructorError(\"ViewWatcher.create\");\n }\n\n const logger = debug\n ? new debug.Logger({ name: \"ViewWatcher\", logAtCreation: config })\n : null;\n\n const allViewData = MH.newWeakMap<Element, ViewData>();\n\n const allCallbacks = newXWeakMap<\n Element,\n Map<\n OnViewHandler,\n {\n _callback: OnViewCallback;\n _trackType: TrackType;\n _options: OnViewOptionsInternal;\n }\n >\n >(() => MH.newMap());\n\n const intersectionHandler = (entries: IntersectionObserverEntry[]) => {\n debug: logger?.debug9(`Got ${entries.length} new entries`, entries);\n\n for (const entry of entries) {\n processEntry(entry);\n }\n };\n\n const observeOptions = {\n root: config._root,\n threshold: config._threshold,\n rootMargin: config._rootMargin,\n };\n\n const xObserver = new XIntersectionObserver(\n intersectionHandler,\n observeOptions,\n );\n\n // ----------\n\n const fetchCurrentView = (\n element: Element,\n realtime = false,\n ): Promise<ViewData> => {\n const fetchData = async (\n entryOrElement: IntersectionObserverEntry | Element,\n ) => {\n const intersection = await fetchIntersectionData(\n config,\n entryOrElement,\n realtime,\n );\n const data = await fetchViewData(intersection, realtime);\n return data;\n };\n\n if (realtime) {\n return fetchData(element);\n }\n\n return MH.newPromise((resolve) => {\n // Use a temp IntersectionObserver\n const observer = MH.newIntersectionObserver((entries) => {\n const promise = fetchData(entries[0]);\n observer.disconnect();\n promise.then(resolve);\n }, observeOptions);\n\n observer.observe(element);\n });\n };\n\n // ----------\n\n const createCallback = (\n handler: OnViewHandler,\n options: OnViewOptionsInternal,\n trackType: TrackType,\n ): OnViewCallback => {\n const element = options._element;\n MH.remove(allCallbacks.get(element)?.get(handler)?._callback);\n\n debug: logger?.debug5(\"Adding/updating handler\", options);\n const callback = wrapCallback(handler);\n callback.onRemove(() => {\n deleteHandler(handler, options);\n });\n\n allCallbacks.sGet(element).set(handler, {\n _callback: callback,\n _trackType: trackType,\n _options: options,\n });\n\n return callback;\n };\n\n // ----------\n\n const setupOnView = async (\n target: ViewTarget,\n handler: OnViewHandler,\n userOptions: (OnViewOptions & TrackViewOptions) | undefined,\n trackType: TrackType,\n ) => {\n const options = await fetchOptions(config._root, target, userOptions);\n const element = options._element;\n\n const callback = createCallback(handler, options, trackType);\n\n // View watcher should be used before the DOM is loaded since the initial\n // size of the root may be 0 or close to 0 and would lead to premature\n // triggering.\n await waitForInteractive();\n\n // Initial call doesn't need to be realtime, and best to use an actual\n // IntersectionObserverEntry for that one.\n let viewData = await fetchCurrentView(element);\n\n if (\n viewData.rootBounds[MC.S_WIDTH] === 0 &&\n viewData.rootBounds[MC.S_HEIGHT] === 0\n ) {\n // Possibly the root is being setup now, wait for one AF\n debug: logger?.debug5(\n \"Got zero root size, deferring for a bit\",\n config._root,\n );\n await waitForSubsequentMeasureTime();\n viewData = await fetchCurrentView(element);\n }\n\n if (trackType === TRACK_FULL) {\n // Detect resize or scroll\n await setupInviewTrack(options, callback, viewData);\n }\n\n if (callback.isRemoved()) {\n return;\n }\n\n // Always use observeLater to skip the initial call from the\n // IntersectionObserver, and call callbacks that have skipInitial: false\n // here. Otherwise, we can't tell from inside the intersectionHandler whether\n // a callback wants to skip its initial call or not.\n //\n // It's ok if already observed, won't do anything.\n xObserver.observeLater(element);\n\n if (!userOptions?.skipInitial) {\n debug: logger?.debug5(\"Calling initially with\", element, viewData);\n if (viewsToBitmask(viewData.views) & options._viewsBitmask) {\n await invokeCallback(callback, element, viewData);\n }\n }\n };\n\n // ----------\n\n const removeOnView = async (\n target: ViewTarget,\n handler: OnViewHandler,\n trackType: TrackType,\n ) => {\n // For time sync, so that if called immediately after onView without\n // awaiting, it will remove the callback that is about to be added.\n // But if no such handler has been added we may unnecessarily\n // create an overlay... TODO\n const options = await fetchOptions(config._root, target, {});\n const element = options._element;\n\n const currEntry = allCallbacks.get(element)?.get(handler);\n if (currEntry?._trackType === trackType) {\n MH.remove(currEntry._callback);\n\n if (handler === setViewCssProps) {\n // delete the properties\n setViewCssProps(element, null);\n }\n }\n };\n\n // ----------\n\n const deleteHandler = (\n handler: OnViewHandler,\n options: OnViewOptionsInternal,\n ) => {\n const element = options._element;\n\n MH.deleteKey(allCallbacks.get(element), handler);\n allCallbacks.prune(element);\n\n if (!allCallbacks.has(element)) {\n debug: logger?.debug4(\n \"No more callbacks for target; unobserving\",\n element,\n );\n\n xObserver.unobserve(element);\n MH.deleteKey(allViewData, element);\n }\n };\n\n // ----------\n\n const processEntry = async (entry: IntersectionObserverEntry) => {\n // In reality, it can't be just a base Element\n const element = MH.targetOf(entry);\n\n // This doesn't need to be \"realtime\", since IntersectionObserver alone\n // introduces a delay.\n const intersection = await fetchIntersectionData(config, entry);\n const latestData = await fetchViewData(intersection);\n debug: logger?.debug9(\"Got ViewData\", element, latestData);\n\n const viewsBitmask = viewsToBitmask(latestData.views);\n\n for (const entry of allCallbacks.get(element)?.values() || []) {\n if (viewsBitmask & entry._options._viewsBitmask) {\n invokeCallback(entry._callback, element, latestData);\n }\n }\n };\n\n // ----------\n\n const setupInviewTrack = async (\n options: OnViewOptionsInternal,\n viewCallback: OnViewCallback,\n viewData: ViewData,\n ) => {\n const element = options._element;\n debug: logger?.debug8(\n \"Setting up size, scroll and attribute tracking\",\n element,\n );\n\n const sizeWatcher = SizeWatcher.reuse();\n const scrollWatcher = ScrollWatcher.reuse();\n const realtime = options._debounceWindow === 0;\n\n // Detect when target's class or style attribute change\n const domWatcher = DOMWatcher.create({\n root: element,\n // only direct children\n subtree: false,\n });\n\n // We need to remove the tracking callback when target leaves view and re-add\n // it when it enters view. But the OnViewCallback that is associated may have\n // already been added prior, by calling onView with this handler, so we can't\n // always wrap around it, in order to detect when it's called with a change\n // of view. So we setup another OnViewCallback tied to the tracking callback.\n let isInview = false;\n\n let removeTrackCallback: (() => void) | null = null;\n\n // Finds any scrollable ancestors of the element and detect scroll on them.\n const scrollableAncestors = await fetchScrollableAncestors(\n element,\n realtime,\n );\n if (viewCallback.isRemoved()) {\n return;\n }\n\n const addTrackCallback = () => {\n const trackCallback = wrapCallback(async () => {\n const prevData = allViewData.get(element);\n\n // Get the latest view data for the target\n const latestData = await fetchCurrentView(element, realtime);\n debug: logger?.debug9(\"Got ViewData\", element, latestData);\n\n const changed = viewChanged(latestData, prevData);\n if (changed) {\n // When comparing for changes, we round the numbers to certain number\n // of decimal places, and allViewData serves as a \"last threshold\"\n // state, so only update it if there was a significant change.\n // Otherwise very quick changes in small increments would get\n // rejected as \"no change\".\n allViewData.set(element, latestData);\n\n if (isInview && !viewCallback.isRemoved()) {\n // Could have been removed during the debounce window\n await invokeCallback(viewCallback, element, latestData);\n }\n } else {\n debug: logger?.debug9(\"ViewData same as last\");\n }\n });\n\n // TODO Is there a better way to detect when it's moved?\n viewCallback.onRemove(trackCallback.remove);\n removeTrackCallback = trackCallback.remove;\n\n // Detect when target's class or style attribute change\n domWatcher.onMutation(trackCallback, {\n categories: [MC.S_ATTRIBUTE],\n [MC.S_SKIP_INITIAL]: true,\n });\n\n // Detect when target is resized\n sizeWatcher.onResize(trackCallback, {\n target: element,\n [MC.S_DEBOUNCE_WINDOW]: options._debounceWindow,\n threshold: options._resizeThreshold,\n [MC.S_SKIP_INITIAL]: true,\n });\n\n // Detect when the root is resized\n sizeWatcher.onResize(trackCallback, {\n target: config._root ?? MH.getWindow(),\n [MC.S_DEBOUNCE_WINDOW]: options._debounceWindow,\n threshold: options._resizeThreshold,\n [MC.S_SKIP_INITIAL]: true,\n });\n\n // Detect when the target's scrollable ancestors are scrolled (this\n // will almost certainly include the main scrollable element).\n for (const ancestor of scrollableAncestors) {\n scrollWatcher.onScroll(trackCallback, {\n scrollable: ancestor,\n [MC.S_DEBOUNCE_WINDOW]: options._debounceWindow,\n threshold: options._scrollThreshold,\n [MC.S_SKIP_INITIAL]: true,\n });\n }\n };\n\n const enterOrLeaveCallback = createCallback(\n (target__ignored, viewData) => {\n if (viewData.views[0] === MC.S_AT) {\n if (!isInview) {\n isInview = true;\n addTrackCallback();\n }\n } else if (removeTrackCallback) {\n isInview = false;\n removeTrackCallback();\n removeTrackCallback = null;\n }\n },\n MH.assign(options, {\n _viewsBitmask: VIEWS_SPACE.bitmask,\n }),\n TRACK_REGULAR,\n );\n\n viewCallback.onRemove(enterOrLeaveCallback.remove);\n\n allViewData.set(element, viewData); // to avoid duplicate initial call\n // Setup the track and the \"inView\" state\n if (!enterOrLeaveCallback.isRemoved()) {\n invokeCallback(enterOrLeaveCallback, element, viewData);\n }\n };\n\n // ----------\n\n this.fetchCurrentView = (target, realtime = false) =>\n fetchElement(config._root, target).then((element) =>\n fetchCurrentView(element, realtime),\n );\n\n // ----------\n\n this.trackView = (element, handler?, options?) => {\n if (!handler) {\n handler = setViewCssProps;\n }\n\n return setupOnView(element, handler, options, TRACK_FULL);\n };\n\n // ----------\n\n this.noTrackView = (element, handler?) => {\n if (!handler) {\n handler = setViewCssProps;\n }\n\n removeOnView(element, handler, TRACK_FULL); // no need to await\n };\n\n // ----------\n\n this.onView = (target, handler, options?) =>\n setupOnView(target, handler, options, TRACK_REGULAR);\n\n // ----------\n\n this.offView = (target, handler) =>\n removeOnView(target, handler, TRACK_REGULAR); // no need to await\n }\n}\n\n/**\n * @interface\n */\nexport type ViewWatcherConfig = {\n /**\n * The root element to use for the IntersectionObserver.\n *\n * **NOTE:** If the target you want to observe (via\n * {@link ViewWatcher.onView}) is inside a scrolling element, then you should\n * probably set the watcher's root to be that scrolling element or a wrapper\n * around it. However, even if you don't or can't do that, the watcher will\n * try to be smart about that, and when the target is no longer intercepting\n * because it's scrolled outside its scrolling container, and yet its\n * bounding box is still inside the watcher root (e.g. the viewport) the\n * watcher will determine the relative view based on the scrolling container\n * and not the actual watcher root.\n *\n * @defaultValue null\n */\n root?: Element | null;\n\n /**\n * The rootMargin to pass to the IntersectionObserver constructor options.\n *\n * @defaultValue \"0px 0px 0px 0px\"\n */\n rootMargin?: string;\n\n /**\n * The threshold to pass to the IntersectionObserver constructor options.\n *\n * @defaultValue 0\n */\n threshold?: number | number[];\n};\n\n/**\n * @interface\n */\nexport type OnViewOptions = {\n /**\n * Specifies a list of {@link View}s to target for.\n *\n * The handler will only be called if there is a change of view relative to\n * the target that matches the given ones.\n *\n * It can be a comma-separated list of \"views\" or an array of such names.\n *\n * @defaultValue undefined\n */\n views?: CommaSeparatedStr<View> | View[];\n\n /**\n * Do not call the handler until there's a future resize of the element.\n *\n * By default we call the handler (almost) immediately with the current size\n * data for the target.\n *\n * @defaultValue false\n */\n skipInitial?: boolean;\n};\n\n/**\n * @interface\n */\nexport type TrackViewOptions = {\n /**\n * Use this debounce window for the {@link ScrollWatcher} and\n * {@link SizeWatcher} involved in the view tracking.\n *\n * **IMPORTANT:**\n * If the debounce window is non-0 (default), then the callback is always\n * delayed by at least an animation frame following a scroll event to allow\n * for optimized `scrollTop`/`scrollLeft` measurements via\n * {@link waitForMeasureTime}.\n *\n * If you set this is 0, this indicates that the callback should be\n * \"realtime\" and will therefore skip {@link waitForMeasureTime}, which could\n * lead to forced re-layouts during scroll, but you probably need this when\n * doing scroll-based animations.\n *\n * @defaultValue undefined // ScrollWatcher and SizeWatcher defaults\n */\n debounceWindow?: number;\n\n /**\n * Use this resize threshold for the {@link SizeWatcher} involved in the view\n * tracking.\n *\n * @defaultValue undefined // SizeWatcher default\n */\n resizeThreshold?: number;\n\n /**\n * Use this scroll threshold for the {@link ScrollWatcher} involved in the\n * view tracking.\n *\n * @defaultValue undefined // ScrollWatcher default\n */\n scrollThreshold?: number;\n\n /**\n * Do not call the handler until there's a future resize of the element.\n *\n * By default we call the handler (almost) immediately with the current size\n * data for the target.\n *\n * @defaultValue false\n */\n skipInitial?: boolean;\n};\n\n/**\n * The handler is invoked with two arguments:\n *\n * - The element that is the target of the IntersectionObserver. If the call to\n * {@link ViewWatcher.onView} specified an element as the target, it will be\n * the same. If it specified an offset, then the element passed to the\n * callback will be an absolutely positioned trigger overlay that's created\n * as a result.\n * - the {@link ViewData} for relative to the target\n */\nexport type OnViewHandlerArgs = [Element, ViewData];\nexport type OnViewCallback = Callback<OnViewHandlerArgs>;\nexport type OnViewHandler = CallbackHandler<OnViewHandlerArgs> | OnViewCallback;\n\nexport type ViewData = {\n isIntersecting: boolean;\n\n targetBounds: BoundingRect;\n\n rootBounds: BoundingRect;\n\n /**\n * The current view or views of the target. There would be two views given\n * only if the target is _not_ in view and it's diagonally across from the\n * root, e.g. both below and to the right.\n */\n views: [View, View?];\n\n /**\n * This is the target's position relative to the\n * {@link ViewWatcherConfig.root | root} with values relative to the root\n * size.\n *\n * It is like the {@link targetBounds} except that each quantity is scaled by\n * the root's width or height, and having two additional computed values.\n */\n relative: {\n x: number;\n y: number;\n top: number;\n bottom: number;\n left: number;\n right: number;\n width: number;\n height: number;\n\n /**\n * Average of the relative left and right.\n */\n hMiddle: number;\n\n /**\n * Average of the relative top and bottom.\n */\n vMiddle: number;\n };\n};\n\n// ----------------------------------------\n\ntype ViewWatcherConfigInternal = {\n _root: Element | null;\n _rootMargin: string;\n _threshold: number | number[];\n};\n\ntype OnViewOptionsInternal = {\n _element: Element;\n _viewsBitmask: number;\n _debounceWindow: number | undefined;\n _resizeThreshold: number | undefined;\n _scrollThreshold: number | undefined;\n};\n\ntype IntersectionData = {\n _target: Element;\n _targetBounds: BoundingRect;\n _root: Element | null;\n _rootMargins: [number, number, number, number];\n _rootBounds: BoundingRect;\n _isIntersecting: boolean | null; // null means unknown, no IntersectionObserverEntry\n _isCrossOrigin: boolean | null; // null means unknown, no IntersectionObserverEntry\n};\n\ntype TrackType = typeof TRACK_REGULAR | typeof TRACK_FULL;\n\nconst CONSTRUCTOR_KEY: unique symbol = MC.SYMBOL() as typeof CONSTRUCTOR_KEY;\nconst instances = newXMap<Element | null, Map<string, ViewWatcher>>(() =>\n MH.newMap(),\n);\n\nconst getConfig = (\n config: ViewWatcherConfig | undefined,\n): ViewWatcherConfigInternal => {\n return {\n _root: config?.root ?? null,\n _rootMargin: config?.rootMargin ?? \"0px 0px 0px 0px\",\n _threshold: config?.threshold ?? 0,\n };\n};\n\nconst TRACK_REGULAR = 1; // only entering/leaving root\nconst TRACK_FULL = 2; // entering/leaving + moving across (fine-grained)\n\n// --------------------\n\nconst fetchOptions = async (\n root: Element | null,\n target: ViewTarget,\n options: (OnViewOptions & TrackViewOptions) | undefined,\n): Promise<OnViewOptionsInternal> => {\n return {\n _element: await fetchElement(root, target),\n _viewsBitmask: getViewsBitmask(options?.views),\n _debounceWindow: options?.debounceWindow,\n _resizeThreshold: options?.resizeThreshold,\n _scrollThreshold: options?.scrollThreshold,\n };\n};\n\nconst fetchScrollableAncestors = async (\n element: Element,\n realtime: boolean,\n) => {\n if (!realtime) {\n await waitForMeasureTime();\n }\n\n const scrollableAncestors = [];\n let ancestor: Element | null | undefined = element;\n while ((ancestor = getClosestScrollable(ancestor, { active: true }))) {\n scrollableAncestors.push(ancestor);\n }\n\n return scrollableAncestors;\n};\n\nconst viewChanged = (latestData: ViewData, prevData: ViewData | undefined) =>\n !prevData ||\n viewsToBitmask(prevData.views) !== viewsToBitmask(latestData.views) ||\n !compareValuesIn(\n MH.copyBoundingRectProps(prevData.targetBounds),\n MH.copyBoundingRectProps(latestData.targetBounds),\n ) ||\n !compareValuesIn(prevData.rootBounds, latestData.rootBounds) ||\n !compareValuesIn(prevData.relative, latestData.relative);\n\nconst viewsToBitmask = (views: [View, View?]) =>\n VIEWS_SPACE.bit[views[0]] | (views[1] ? VIEWS_SPACE.bit[views[1]] : 0);\n\nconst fetchIntersectionData = async (\n config: ViewWatcherConfigInternal,\n entryOrTarget: IntersectionObserverEntry | Element,\n realtime = false,\n): Promise<IntersectionData> => {\n const root = config._root;\n const vpSize = await fetchViewportSize(realtime);\n const rootMargins = toMargins(config._rootMargin, vpSize);\n\n let target: Element;\n let targetBounds: BoundingRect;\n let rootBounds: BoundingRect | null = null;\n let isIntersecting: boolean | null = null;\n let isCrossOrigin: boolean | null = null;\n\n if (MH.isInstanceOf(entryOrTarget, IntersectionObserverEntry)) {\n target = entryOrTarget.target;\n targetBounds = entryOrTarget.boundingClientRect;\n rootBounds = entryOrTarget.rootBounds;\n isIntersecting = entryOrTarget.isIntersecting;\n isCrossOrigin = !entryOrTarget.rootBounds;\n } else {\n target = entryOrTarget;\n targetBounds = await fetchBounds(target, realtime);\n }\n\n if (!rootBounds) {\n rootBounds = await fetchBounds(root, realtime, rootMargins);\n }\n\n return {\n _target: target,\n _targetBounds: targetBounds,\n _root: root,\n _rootMargins: rootMargins,\n _rootBounds: rootBounds,\n _isIntersecting: isIntersecting,\n _isCrossOrigin: isCrossOrigin,\n };\n};\n\nconst fetchBounds = async (\n root: Element | null,\n realtime: boolean,\n rootMargins?: [number, number, number, number],\n): Promise<BoundingRect> => {\n let rect: BoundingRect;\n\n if (root) {\n if (!realtime) {\n await waitForMeasureTime();\n }\n\n rect = MH.copyBoundingRectProps(MH.getBoundingClientRect(root));\n } else {\n const { width, height } = await fetchViewportSize(realtime);\n rect = {\n x: 0,\n left: 0,\n right: width,\n width,\n y: 0,\n top: 0,\n bottom: height,\n height,\n };\n }\n\n if (rootMargins) {\n rect.x = rect[MC.S_LEFT] -= rootMargins[3];\n rect[MC.S_RIGHT] += rootMargins[1];\n rect[MC.S_WIDTH] += rootMargins[1] + rootMargins[3];\n\n rect.y = rect[MC.S_TOP] -= rootMargins[0];\n rect[MC.S_BOTTOM] += rootMargins[2];\n rect[MC.S_HEIGHT] += rootMargins[0] + rootMargins[2];\n }\n\n return rect;\n};\n\nconst fetchViewData = async (\n intersection: IntersectionData,\n realtime = false,\n): Promise<ViewData> => {\n const vpSize = await fetchViewportSize(realtime);\n const vpHeight = vpSize[MC.S_HEIGHT];\n const vpWidth = vpSize[MC.S_WIDTH];\n\n const views = await fetchViews(intersection, realtime);\n\n const relative = MH.merge(\n { hMiddle: NaN, vMiddle: NaN },\n MH.copyBoundingRectProps(intersection._targetBounds),\n );\n\n relative.y /= vpHeight;\n relative[MC.S_TOP] /= vpHeight;\n relative[MC.S_BOTTOM] /= vpHeight;\n relative[MC.S_HEIGHT] /= vpHeight;\n\n relative.x /= vpWidth;\n relative[MC.S_LEFT] /= vpWidth;\n relative[MC.S_RIGHT] /= vpWidth;\n relative[MC.S_WIDTH] /= vpWidth;\n\n relative.hMiddle = (relative[MC.S_LEFT] + relative[MC.S_RIGHT]) / 2;\n relative.vMiddle = (relative[MC.S_TOP] + relative[MC.S_BOTTOM]) / 2;\n\n const viewData: ViewData = {\n isIntersecting: intersection._isIntersecting ?? views[0] === MC.S_AT,\n targetBounds: intersection._targetBounds,\n rootBounds: intersection._rootBounds,\n views,\n relative,\n };\n\n return viewData;\n};\n\nconst fetchViews = async (\n intersection: IntersectionData,\n realtime: boolean,\n useScrollingAncestor?: Element,\n): Promise<[View, View?]> => {\n if (intersection._isIntersecting) {\n return [MC.S_AT];\n }\n\n let rootBounds: BoundingRect;\n if (useScrollingAncestor) {\n rootBounds = await fetchBounds(\n useScrollingAncestor,\n realtime,\n intersection._rootMargins,\n );\n } else {\n rootBounds = intersection._rootBounds;\n }\n\n const targetBounds = intersection._targetBounds;\n const delta = {\n _left: rootBounds[MC.S_LEFT] - targetBounds[MC.S_LEFT],\n _right: targetBounds[MC.S_RIGHT] - rootBounds[MC.S_RIGHT],\n _top: rootBounds[MC.S_TOP] - targetBounds[MC.S_TOP],\n _bottom: targetBounds[MC.S_BOTTOM] - rootBounds[MC.S_BOTTOM],\n };\n\n let xView: View | null = null;\n let yView: View | null = null;\n if (delta._left > 0 && delta._right > 0) {\n // Target is wider than root: use greater delta to determine position.\n // Remember, the view is the _root_ position relative to target.\n xView = delta._left > delta._right ? MC.S_RIGHT : MC.S_LEFT;\n } else if (delta._left > 0) {\n // Target is to the left of the root\n xView = MC.S_RIGHT;\n } else if (delta._right > 0) {\n // Target is to the right of the root\n xView = MC.S_LEFT;\n } // else target is horizontally contained in root, see below\n\n if (delta._top > 0 && delta._bottom > 0) {\n // Target is taller than root: use greater delta to determine position.\n // Remember, the view is the _root_ position relative to target.\n yView = delta._top > delta._bottom ? MC.S_BELOW : MC.S_ABOVE;\n } else if (delta._top > 0) {\n // Target is above the root\n yView = MC.S_BELOW;\n } else if (delta._bottom > 0) {\n // Target is below the root\n yView = MC.S_ABOVE;\n } // else target is vertically contained in root, see below\n\n if (xView && yView) {\n // diagonally out of vide\n return [xView, yView];\n } else if (xView) {\n // horizontally out of vide\n return [xView];\n } else if (yView) {\n // vertically out of vide\n return [yView];\n }\n\n // The target is contained in the root bounds and yet isIntersecting was\n // not true. This means that either:\n //\n // 1. It may be intersecting, but we didn't get an actual\n // IntersectionObserverEntry and we don't know if it's intersecting\n // or not\n // 2. The target is inside a scrolling element that is _not_ being used as\n // the observer root, and the target has scrolled out of the scrollable\n // bounds but still inside the viewport\n // 3. We're inside a cross-origin iFrame and the iFrame is partially or\n // fully not-intersecting\n\n if (!intersection._isCrossOrigin) {\n // This is case 1. or 2. => get the views relative to the closest\n // scrollable ancestor relative to which it is _not_ intersecting, if\n // any. If it's nested inside several scrolling elements, we'll end up\n // looping over each one until we find the one for which the target is\n // outside its box.\n //\n // It is too risky to use active isScrollable check here since we could be\n // inside an onScroll handler, so just use passive.\n const scrollingAncestor = getClosestScrollable(\n useScrollingAncestor ?? intersection._target,\n );\n\n if (scrollingAncestor) {\n return fetchViews(intersection, realtime, scrollingAncestor);\n }\n }\n\n // Either case 3. (cross-origin iframe outside the viewport) or case 1. and\n // the target is actually intersecting the root. Either way, it's to be\n // considered in-view of its root.\n return [MC.S_AT];\n};\n\nconst setViewCssProps = (\n element: Element,\n viewData: ViewData | undefined | null,\n) => {\n const relative: Record<string, number> = viewData?.relative ?? {};\n const props = {\n top: relative.top,\n bottom: relative.bottom,\n left: relative.left,\n right: relative.right,\n [MC.S_WIDTH]: relative[MC.S_WIDTH],\n [MC.S_HEIGHT]: relative[MC.S_HEIGHT],\n hMiddle: relative.hMiddle,\n vMiddle: relative.vMiddle,\n };\n setNumericStyleJsVars(element, props, { _prefix: \"r-\", _numDecimal: 4 }); // don't await here\n};\n\nconst fetchElement = async (\n root: Element | null,\n target: ViewTarget,\n): Promise<Element> => {\n if (MH.isElement(target)) {\n return target;\n } else if (!MH.isString(target)) {\n throw MH.usageError(\n \"'target' must be an offset string or an HTMLElement | SVGElement | MathMLElement\",\n );\n }\n\n const overlayOptions = getOverlayOptions(root, target);\n return await createOverlay(overlayOptions);\n};\n\nconst getOverlayOptions = (\n root: Element | null,\n target: string,\n): OverlayOptions => {\n const { reference, value } = parseScrollOffset(target);\n\n let ovrDimension: \"width\" | \"height\";\n if (reference === MC.S_TOP || reference === MC.S_BOTTOM) {\n ovrDimension = MC.S_WIDTH;\n } else if (reference === MC.S_LEFT || reference === MC.S_RIGHT) {\n ovrDimension = MC.S_HEIGHT;\n } else {\n throw MH.usageError(`Invalid offset reference: '${reference}'`);\n }\n\n return {\n parent: MH.isHTMLElement(root) ? root : undefined,\n style: {\n [reference]: value,\n [ovrDimension]: \"100%\",\n },\n };\n};\n\nconst invokeCallback = (\n callback: OnViewCallback,\n element: Element,\n viewData: ViewData,\n) => callback.invoke(element, MH.copyObject(viewData)).catch(logError);\n"],"mappings":"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