/ node_modules / svelte / src / runtime / internal / style_manager.js
style_manager.js
 1  import { append_empty_stylesheet, detach, get_root_for_style } from './dom.js';
 2  import { raf } from './environment.js';
 3  
 4  // we need to store the information for multiple documents because a Svelte application could also contain iframes
 5  // https://github.com/sveltejs/svelte/issues/3624
 6  /** @type {Map<Document | ShadowRoot, import('./private.d.ts').StyleInformation>} */
 7  const managed_styles = new Map();
 8  
 9  let active = 0;
10  
11  // https://github.com/darkskyapp/string-hash/blob/master/index.js
12  /**
13   * @param {string} str
14   * @returns {number}
15   */
16  function hash(str) {
17  	let hash = 5381;
18  	let i = str.length;
19  	while (i--) hash = ((hash << 5) - hash) ^ str.charCodeAt(i);
20  	return hash >>> 0;
21  }
22  
23  /**
24   * @param {Document | ShadowRoot} doc
25   * @param {Element & ElementCSSInlineStyle} node
26   * @returns {{ stylesheet: any; rules: {}; }}
27   */
28  function create_style_information(doc, node) {
29  	const info = { stylesheet: append_empty_stylesheet(node), rules: {} };
30  	managed_styles.set(doc, info);
31  	return info;
32  }
33  
34  /**
35   * @param {Element & ElementCSSInlineStyle} node
36   * @param {number} a
37   * @param {number} b
38   * @param {number} duration
39   * @param {number} delay
40   * @param {(t: number) => number} ease
41   * @param {(t: number, u: number) => string} fn
42   * @param {number} uid
43   * @returns {string}
44   */
45  export function create_rule(node, a, b, duration, delay, ease, fn, uid = 0) {
46  	const step = 16.666 / duration;
47  	let keyframes = '{\n';
48  	for (let p = 0; p <= 1; p += step) {
49  		const t = a + (b - a) * ease(p);
50  		keyframes += p * 100 + `%{${fn(t, 1 - t)}}\n`;
51  	}
52  	const rule = keyframes + `100% {${fn(b, 1 - b)}}\n}`;
53  	const name = `__svelte_${hash(rule)}_${uid}`;
54  	const doc = get_root_for_style(node);
55  	const { stylesheet, rules } = managed_styles.get(doc) || create_style_information(doc, node);
56  	if (!rules[name]) {
57  		rules[name] = true;
58  		stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length);
59  	}
60  	const animation = node.style.animation || '';
61  	node.style.animation = `${
62  		animation ? `${animation}, ` : ''
63  	}${name} ${duration}ms linear ${delay}ms 1 both`;
64  	active += 1;
65  	return name;
66  }
67  
68  /**
69   * @param {Element & ElementCSSInlineStyle} node
70   * @param {string} [name]
71   * @returns {void}
72   */
73  export function delete_rule(node, name) {
74  	const previous = (node.style.animation || '').split(', ');
75  	const next = previous.filter(
76  		name
77  			? (anim) => anim.indexOf(name) < 0 // remove specific animation
78  			: (anim) => anim.indexOf('__svelte') === -1 // remove all Svelte animations
79  	);
80  	const deleted = previous.length - next.length;
81  	if (deleted) {
82  		node.style.animation = next.join(', ');
83  		active -= deleted;
84  		if (!active) clear_rules();
85  	}
86  }
87  
88  /** @returns {void} */
89  export function clear_rules() {
90  	raf(() => {
91  		if (active) return;
92  		managed_styles.forEach((info) => {
93  			const { ownerNode } = info.stylesheet;
94  			// there is no ownerNode if it runs on jsdom.
95  			if (ownerNode) detach(ownerNode);
96  		});
97  		managed_styles.clear();
98  	});
99  }