169 lines
6.1 KiB
JavaScript
169 lines
6.1 KiB
JavaScript
function run(fn) {
|
|
return fn();
|
|
}
|
|
function run_all(fns) {
|
|
fns.forEach(run);
|
|
}
|
|
|
|
const dirty_components = [];
|
|
const binding_callbacks = [];
|
|
const render_callbacks = [];
|
|
const flush_callbacks = [];
|
|
const resolved_promise = Promise.resolve();
|
|
let update_scheduled = false;
|
|
function schedule_update() {
|
|
if (!update_scheduled) {
|
|
update_scheduled = true;
|
|
resolved_promise.then(flush);
|
|
}
|
|
}
|
|
function tick() {
|
|
schedule_update();
|
|
return resolved_promise;
|
|
}
|
|
function add_render_callback(fn) {
|
|
render_callbacks.push(fn);
|
|
}
|
|
// flush() calls callbacks in this order:
|
|
// 1. All beforeUpdate callbacks, in order: parents before children
|
|
// 2. All bind:this callbacks, in reverse order: children before parents.
|
|
// 3. All afterUpdate callbacks, in order: parents before children. EXCEPT
|
|
// for afterUpdates called during the initial onMount, which are called in
|
|
// reverse order: children before parents.
|
|
// Since callbacks might update component values, which could trigger another
|
|
// call to flush(), the following steps guard against this:
|
|
// 1. During beforeUpdate, any updated components will be added to the
|
|
// dirty_components array and will cause a reentrant call to flush(). Because
|
|
// the flush index is kept outside the function, the reentrant call will pick
|
|
// up where the earlier call left off and go through all dirty components. The
|
|
// current_component value is saved and restored so that the reentrant call will
|
|
// not interfere with the "parent" flush() call.
|
|
// 2. bind:this callbacks cannot trigger new flush() calls.
|
|
// 3. During afterUpdate, any updated components will NOT have their afterUpdate
|
|
// callback called a second time; the seen_callbacks set, outside the flush()
|
|
// function, guarantees this behavior.
|
|
const seen_callbacks = new Set();
|
|
let flushidx = 0; // Do *not* move this inside the flush() function
|
|
function flush() {
|
|
do {
|
|
// first, call beforeUpdate functions
|
|
// and update components
|
|
while (flushidx < dirty_components.length) {
|
|
const component = dirty_components[flushidx];
|
|
flushidx++;
|
|
update(component.$$);
|
|
}
|
|
dirty_components.length = 0;
|
|
flushidx = 0;
|
|
while (binding_callbacks.length)
|
|
binding_callbacks.pop()();
|
|
// then, once components are updated, call
|
|
// afterUpdate functions. This may cause
|
|
// subsequent updates...
|
|
for (let i = 0; i < render_callbacks.length; i += 1) {
|
|
const callback = render_callbacks[i];
|
|
if (!seen_callbacks.has(callback)) {
|
|
// ...so guard against infinite loops
|
|
seen_callbacks.add(callback);
|
|
callback();
|
|
}
|
|
}
|
|
render_callbacks.length = 0;
|
|
} while (dirty_components.length);
|
|
while (flush_callbacks.length) {
|
|
flush_callbacks.pop()();
|
|
}
|
|
update_scheduled = false;
|
|
seen_callbacks.clear();
|
|
}
|
|
function update($$) {
|
|
if ($$.fragment !== null) {
|
|
$$.update();
|
|
run_all($$.before_update);
|
|
const dirty = $$.dirty;
|
|
$$.dirty = [-1];
|
|
$$.fragment && $$.fragment.p($$.ctx, dirty);
|
|
$$.after_update.forEach(add_render_callback);
|
|
}
|
|
}
|
|
|
|
const defaultOptions = {
|
|
root: null,
|
|
rootMargin: '0px',
|
|
threshold: 0,
|
|
unobserveOnEnter: false,
|
|
};
|
|
const createEvent = (name, detail) => new CustomEvent(name, { detail });
|
|
function inview(node, options = {}) {
|
|
const { root, rootMargin, threshold, unobserveOnEnter } = Object.assign(Object.assign({}, defaultOptions), options);
|
|
let prevPos = {
|
|
x: undefined,
|
|
y: undefined,
|
|
};
|
|
let scrollDirection = {
|
|
vertical: undefined,
|
|
horizontal: undefined,
|
|
};
|
|
if (typeof IntersectionObserver !== 'undefined' && node) {
|
|
const observer = new IntersectionObserver((entries, _observer) => {
|
|
entries.forEach((singleEntry) => {
|
|
if (prevPos.y > singleEntry.boundingClientRect.y) {
|
|
scrollDirection.vertical = 'up';
|
|
}
|
|
else {
|
|
scrollDirection.vertical = 'down';
|
|
}
|
|
if (prevPos.x > singleEntry.boundingClientRect.x) {
|
|
scrollDirection.horizontal = 'left';
|
|
}
|
|
else {
|
|
scrollDirection.horizontal = 'right';
|
|
}
|
|
prevPos = {
|
|
y: singleEntry.boundingClientRect.y,
|
|
x: singleEntry.boundingClientRect.x,
|
|
};
|
|
const detail = {
|
|
inView: singleEntry.isIntersecting,
|
|
entry: singleEntry,
|
|
scrollDirection,
|
|
node,
|
|
observer: _observer,
|
|
};
|
|
node.dispatchEvent(createEvent('inview_change', detail));
|
|
//@ts-expect-error only for backward compatibility
|
|
node.dispatchEvent(createEvent('change', detail));
|
|
if (singleEntry.isIntersecting) {
|
|
node.dispatchEvent(createEvent('inview_enter', detail));
|
|
//@ts-expect-error only for backward compatibility
|
|
node.dispatchEvent(createEvent('enter', detail));
|
|
unobserveOnEnter && _observer.unobserve(node);
|
|
}
|
|
else {
|
|
node.dispatchEvent(createEvent('inview_leave', detail));
|
|
//@ts-expect-error only for backward compatibility
|
|
node.dispatchEvent(createEvent('leave', detail));
|
|
}
|
|
});
|
|
}, {
|
|
root,
|
|
rootMargin,
|
|
threshold,
|
|
});
|
|
tick().then(() => {
|
|
node.dispatchEvent(createEvent('inview_init', { observer, node }));
|
|
node.dispatchEvent(
|
|
//@ts-expect-error only for backward compatibility
|
|
createEvent('init', { observer, node }));
|
|
});
|
|
observer.observe(node);
|
|
return {
|
|
destroy() {
|
|
observer.unobserve(node);
|
|
},
|
|
};
|
|
}
|
|
}
|
|
|
|
export { inview };
|