summaryrefslogtreecommitdiff
path: root/node_modules/svelte/src/runtime/internal/transitions.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/svelte/src/runtime/internal/transitions.js')
-rw-r--r--node_modules/svelte/src/runtime/internal/transitions.js461
1 files changed, 461 insertions, 0 deletions
diff --git a/node_modules/svelte/src/runtime/internal/transitions.js b/node_modules/svelte/src/runtime/internal/transitions.js
new file mode 100644
index 0000000..4575e18
--- /dev/null
+++ b/node_modules/svelte/src/runtime/internal/transitions.js
@@ -0,0 +1,461 @@
+import { identity as linear, is_function, noop, run_all } from './utils.js';
+import { now } from './environment.js';
+import { loop } from './loop.js';
+import { create_rule, delete_rule } from './style_manager.js';
+import { custom_event } from './dom.js';
+import { add_render_callback } from './scheduler.js';
+
+/**
+ * @type {Promise<void> | null}
+ */
+let promise;
+
+/**
+ * @returns {Promise<void>}
+ */
+function wait() {
+ if (!promise) {
+ promise = Promise.resolve();
+ promise.then(() => {
+ promise = null;
+ });
+ }
+ return promise;
+}
+
+/**
+ * @param {Element} node
+ * @param {INTRO | OUTRO | boolean} direction
+ * @param {'start' | 'end'} kind
+ * @returns {void}
+ */
+function dispatch(node, direction, kind) {
+ node.dispatchEvent(custom_event(`${direction ? 'intro' : 'outro'}${kind}`));
+}
+
+const outroing = new Set();
+
+/**
+ * @type {Outro}
+ */
+let outros;
+
+/**
+ * @returns {void} */
+export function group_outros() {
+ outros = {
+ r: 0,
+ c: [],
+ p: outros // parent group
+ };
+}
+
+/**
+ * @returns {void} */
+export function check_outros() {
+ if (!outros.r) {
+ run_all(outros.c);
+ }
+ outros = outros.p;
+}
+
+/**
+ * @param {import('./private.js').Fragment} block
+ * @param {0 | 1} [local]
+ * @returns {void}
+ */
+export function transition_in(block, local) {
+ if (block && block.i) {
+ outroing.delete(block);
+ block.i(local);
+ }
+}
+
+/**
+ * @param {import('./private.js').Fragment} block
+ * @param {0 | 1} local
+ * @param {0 | 1} [detach]
+ * @param {() => void} [callback]
+ * @returns {void}
+ */
+export function transition_out(block, local, detach, callback) {
+ if (block && block.o) {
+ if (outroing.has(block)) return;
+ outroing.add(block);
+ outros.c.push(() => {
+ outroing.delete(block);
+ if (callback) {
+ if (detach) block.d(1);
+ callback();
+ }
+ });
+ block.o(local);
+ } else if (callback) {
+ callback();
+ }
+}
+
+/**
+ * @type {import('../transition/public.js').TransitionConfig}
+ */
+const null_transition = { duration: 0 };
+
+/**
+ * @param {Element & ElementCSSInlineStyle} node
+ * @param {TransitionFn} fn
+ * @param {any} params
+ * @returns {{ start(): void; invalidate(): void; end(): void; }}
+ */
+export function create_in_transition(node, fn, params) {
+ /**
+ * @type {TransitionOptions} */
+ const options = { direction: 'in' };
+ let config = fn(node, params, options);
+ let running = false;
+ let animation_name;
+ let task;
+ let uid = 0;
+
+ /**
+ * @returns {void} */
+ function cleanup() {
+ if (animation_name) delete_rule(node, animation_name);
+ }
+
+ /**
+ * @returns {void} */
+ function go() {
+ const {
+ delay = 0,
+ duration = 300,
+ easing = linear,
+ tick = noop,
+ css
+ } = config || null_transition;
+ if (css) animation_name = create_rule(node, 0, 1, duration, delay, easing, css, uid++);
+ tick(0, 1);
+ const start_time = now() + delay;
+ const end_time = start_time + duration;
+ if (task) task.abort();
+ running = true;
+ add_render_callback(() => dispatch(node, true, 'start'));
+ task = loop((now) => {
+ if (running) {
+ if (now >= end_time) {
+ tick(1, 0);
+ dispatch(node, true, 'end');
+ cleanup();
+ return (running = false);
+ }
+ if (now >= start_time) {
+ const t = easing((now - start_time) / duration);
+ tick(t, 1 - t);
+ }
+ }
+ return running;
+ });
+ }
+ let started = false;
+ return {
+ start() {
+ if (started) return;
+ started = true;
+ delete_rule(node);
+ if (is_function(config)) {
+ config = config(options);
+ wait().then(go);
+ } else {
+ go();
+ }
+ },
+ invalidate() {
+ started = false;
+ },
+ end() {
+ if (running) {
+ cleanup();
+ running = false;
+ }
+ }
+ };
+}
+
+/**
+ * @param {Element & ElementCSSInlineStyle} node
+ * @param {TransitionFn} fn
+ * @param {any} params
+ * @returns {{ end(reset: any): void; }}
+ */
+export function create_out_transition(node, fn, params) {
+ /** @type {TransitionOptions} */
+ const options = { direction: 'out' };
+ let config = fn(node, params, options);
+ let running = true;
+ let animation_name;
+ const group = outros;
+ group.r += 1;
+ /** @type {boolean} */
+ let original_inert_value;
+
+ /**
+ * @returns {void} */
+ function go() {
+ const {
+ delay = 0,
+ duration = 300,
+ easing = linear,
+ tick = noop,
+ css
+ } = config || null_transition;
+
+ if (css) animation_name = create_rule(node, 1, 0, duration, delay, easing, css);
+
+ const start_time = now() + delay;
+ const end_time = start_time + duration;
+ add_render_callback(() => dispatch(node, false, 'start'));
+
+ if ('inert' in node) {
+ original_inert_value = /** @type {HTMLElement} */ (node).inert;
+ node.inert = true;
+ }
+
+ loop((now) => {
+ if (running) {
+ if (now >= end_time) {
+ tick(0, 1);
+ dispatch(node, false, 'end');
+ if (!--group.r) {
+ // this will result in `end()` being called,
+ // so we don't need to clean up here
+ run_all(group.c);
+ }
+ return false;
+ }
+ if (now >= start_time) {
+ const t = easing((now - start_time) / duration);
+ tick(1 - t, t);
+ }
+ }
+ return running;
+ });
+ }
+
+ if (is_function(config)) {
+ wait().then(() => {
+ // @ts-ignore
+ config = config(options);
+ go();
+ });
+ } else {
+ go();
+ }
+
+ return {
+ end(reset) {
+ if (reset && 'inert' in node) {
+ node.inert = original_inert_value;
+ }
+ if (reset && config.tick) {
+ config.tick(1, 0);
+ }
+ if (running) {
+ if (animation_name) delete_rule(node, animation_name);
+ running = false;
+ }
+ }
+ };
+}
+
+/**
+ * @param {Element & ElementCSSInlineStyle} node
+ * @param {TransitionFn} fn
+ * @param {any} params
+ * @param {boolean} intro
+ * @returns {{ run(b: 0 | 1): void; end(): void; }}
+ */
+export function create_bidirectional_transition(node, fn, params, intro) {
+ /**
+ * @type {TransitionOptions} */
+ const options = { direction: 'both' };
+ let config = fn(node, params, options);
+ let t = intro ? 0 : 1;
+
+ /**
+ * @type {Program | null} */
+ let running_program = null;
+
+ /**
+ * @type {PendingProgram | null} */
+ let pending_program = null;
+ let animation_name = null;
+
+ /** @type {boolean} */
+ let original_inert_value;
+
+ /**
+ * @returns {void} */
+ function clear_animation() {
+ if (animation_name) delete_rule(node, animation_name);
+ }
+
+ /**
+ * @param {PendingProgram} program
+ * @param {number} duration
+ * @returns {Program}
+ */
+ function init(program, duration) {
+ const d = /** @type {Program['d']} */ (program.b - t);
+ duration *= Math.abs(d);
+ return {
+ a: t,
+ b: program.b,
+ d,
+ duration,
+ start: program.start,
+ end: program.start + duration,
+ group: program.group
+ };
+ }
+
+ /**
+ * @param {INTRO | OUTRO} b
+ * @returns {void}
+ */
+ function go(b) {
+ const {
+ delay = 0,
+ duration = 300,
+ easing = linear,
+ tick = noop,
+ css
+ } = config || null_transition;
+
+ /**
+ * @type {PendingProgram} */
+ const program = {
+ start: now() + delay,
+ b
+ };
+
+ if (!b) {
+ // @ts-ignore todo: improve typings
+ program.group = outros;
+ outros.r += 1;
+ }
+
+ if ('inert' in node) {
+ if (b) {
+ if (original_inert_value !== undefined) {
+ // aborted/reversed outro — restore previous inert value
+ node.inert = original_inert_value;
+ }
+ } else {
+ original_inert_value = /** @type {HTMLElement} */ (node).inert;
+ node.inert = true;
+ }
+ }
+
+ if (running_program || pending_program) {
+ pending_program = program;
+ } else {
+ // if this is an intro, and there's a delay, we need to do
+ // an initial tick and/or apply CSS animation immediately
+ if (css) {
+ clear_animation();
+ animation_name = create_rule(node, t, b, duration, delay, easing, css);
+ }
+ if (b) tick(0, 1);
+ running_program = init(program, duration);
+ add_render_callback(() => dispatch(node, b, 'start'));
+ loop((now) => {
+ if (pending_program && now > pending_program.start) {
+ running_program = init(pending_program, duration);
+ pending_program = null;
+ dispatch(node, running_program.b, 'start');
+ if (css) {
+ clear_animation();
+ animation_name = create_rule(
+ node,
+ t,
+ running_program.b,
+ running_program.duration,
+ 0,
+ easing,
+ config.css
+ );
+ }
+ }
+ if (running_program) {
+ if (now >= running_program.end) {
+ tick((t = running_program.b), 1 - t);
+ dispatch(node, running_program.b, 'end');
+ if (!pending_program) {
+ // we're done
+ if (running_program.b) {
+ // intro — we can tidy up immediately
+ clear_animation();
+ } else {
+ // outro — needs to be coordinated
+ if (!--running_program.group.r) run_all(running_program.group.c);
+ }
+ }
+ running_program = null;
+ } else if (now >= running_program.start) {
+ const p = now - running_program.start;
+ t = running_program.a + running_program.d * easing(p / running_program.duration);
+ tick(t, 1 - t);
+ }
+ }
+ return !!(running_program || pending_program);
+ });
+ }
+ }
+ return {
+ run(b) {
+ if (is_function(config)) {
+ wait().then(() => {
+ const opts = { direction: b ? 'in' : 'out' };
+ // @ts-ignore
+ config = config(opts);
+ go(b);
+ });
+ } else {
+ go(b);
+ }
+ },
+ end() {
+ clear_animation();
+ running_program = pending_program = null;
+ }
+ };
+}
+
+/** @typedef {1} INTRO */
+/** @typedef {0} OUTRO */
+/** @typedef {{ direction: 'in' | 'out' | 'both' }} TransitionOptions */
+/** @typedef {(node: Element, params: any, options: TransitionOptions) => import('../transition/public.js').TransitionConfig} TransitionFn */
+
+/**
+ * @typedef {Object} Outro
+ * @property {number} r
+ * @property {Function[]} c
+ * @property {Object} p
+ */
+
+/**
+ * @typedef {Object} PendingProgram
+ * @property {number} start
+ * @property {INTRO|OUTRO} b
+ * @property {Outro} [group]
+ */
+
+/**
+ * @typedef {Object} Program
+ * @property {number} a
+ * @property {INTRO|OUTRO} b
+ * @property {1|-1} d
+ * @property {number} duration
+ * @property {number} start
+ * @property {number} end
+ * @property {Outro} [group]
+ */