Convert src/runtime/internal/style_manager.ts to JavaScript

pull/8569/head
S. Elliott Johnson 3 years ago
parent 6ae268c878
commit 792a2215c3

@ -1,95 +1,96 @@
import { append_empty_stylesheet, detach, get_root_for_style } from './dom'; import { append_empty_stylesheet, detach, get_root_for_style } from './dom';
import { raf } from './environment'; import { raf } from './environment';
interface StyleInformation {
stylesheet: CSSStyleSheet;
rules: Record<string, true>;
}
// we need to store the information for multiple documents because a Svelte application could also contain iframes // we need to store the information for multiple documents because a Svelte application could also contain iframes
// https://github.com/sveltejs/svelte/issues/3624 // https://github.com/sveltejs/svelte/issues/3624
const managed_styles = new Map<Document | ShadowRoot, StyleInformation>(); const managed_styles = new Map();
let active = 0; let active = 0;
// https://github.com/darkskyapp/string-hash/blob/master/index.js // https://github.com/darkskyapp/string-hash/blob/master/index.js
function hash(str: string) { /** @param {string} str
let hash = 5381; * @returns {number}
let i = str.length; */
function hash(str) {
while (i--) hash = ((hash << 5) - hash) ^ str.charCodeAt(i); let hash = 5381;
return hash >>> 0; let i = str.length;
while (i--)
hash = ((hash << 5) - hash) ^ str.charCodeAt(i);
return hash >>> 0;
} }
/** @param {Document | ShadowRoot} doc
function create_style_information( * @param {Element & ElementCSSInlineStyle} node
doc: Document | ShadowRoot, * @returns {{ stylesheet: any; rules: {}; }}
node: Element & ElementCSSInlineStyle */
) { function create_style_information(doc, node) {
const info = { stylesheet: append_empty_stylesheet(node), rules: {} }; const info = { stylesheet: append_empty_stylesheet(node), rules: {} };
managed_styles.set(doc, info); managed_styles.set(doc, info);
return info; return info;
} }
/** @param {Element & ElementCSSInlineStyle} node
export function create_rule( * @param {number} a
node: Element & ElementCSSInlineStyle, * @param {number} b
a: number, * @param {number} duration
b: number, * @param {number} delay
duration: number, * @param {(t: number) => number} ease
delay: number, * @param {(t: number, u: number) => string} fn
ease: (t: number) => number, * @param {number} uid
fn: (t: number, u: number) => string, * @returns {string}
uid: number = 0 */
) { export function create_rule(node, a, b, duration, delay, ease, fn, uid = 0) {
const step = 16.666 / duration; const step = 16.666 / duration;
let keyframes = '{\n'; let keyframes = '{\n';
for (let p = 0; p <= 1; p += step) {
for (let p = 0; p <= 1; p += step) { const t = a + (b - a) * ease(p);
const t = a + (b - a) * ease(p); keyframes += p * 100 + `%{${fn(t, 1 - t)}}\n`;
keyframes += p * 100 + `%{${fn(t, 1 - t)}}\n`; }
} const rule = keyframes + `100% {${fn(b, 1 - b)}}\n}`;
const name = `__svelte_${hash(rule)}_${uid}`;
const rule = keyframes + `100% {${fn(b, 1 - b)}}\n}`; const doc = get_root_for_style(node);
const name = `__svelte_${hash(rule)}_${uid}`; const { stylesheet, rules } = managed_styles.get(doc) || create_style_information(doc, node);
const doc = get_root_for_style(node); if (!rules[name]) {
rules[name] = true;
const { stylesheet, rules } = managed_styles.get(doc) || create_style_information(doc, node); stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length);
}
if (!rules[name]) { const animation = node.style.animation || '';
rules[name] = true; node.style.animation = `${animation ? `${animation}, ` : ''}${name} ${duration}ms linear ${delay}ms 1 both`;
stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length); active += 1;
} return name;
const animation = node.style.animation || '';
node.style.animation = `${
animation ? `${animation}, ` : ''
}${name} ${duration}ms linear ${delay}ms 1 both`;
active += 1;
return name;
} }
/** @param {Element & ElementCSSInlineStyle} node
export function delete_rule(node: Element & ElementCSSInlineStyle, name?: string) { * @param {string} name
const previous = (node.style.animation || '').split(', '); * @returns {void}
const next = previous.filter( */
name export function delete_rule(node, name) {
? (anim) => anim.indexOf(name) < 0 // remove specific animation const previous = (node.style.animation || '').split(', ');
: (anim) => anim.indexOf('__svelte') === -1 // remove all Svelte animations const next = previous.filter(name
); ? (anim) => anim.indexOf(name) < 0 // remove specific animation
const deleted = previous.length - next.length; : (anim) => anim.indexOf('__svelte') === -1 // remove all Svelte animations
if (deleted) { );
node.style.animation = next.join(', '); const deleted = previous.length - next.length;
active -= deleted; if (deleted) {
if (!active) clear_rules(); node.style.animation = next.join(', ');
} active -= deleted;
if (!active)
clear_rules();
}
} }
/** @returns {void} */
export function clear_rules() { export function clear_rules() {
raf(() => { raf(() => {
if (active) return; if (active)
managed_styles.forEach((info) => { return;
const { ownerNode } = info.stylesheet; managed_styles.forEach((info) => {
// there is no ownerNode if it runs on jsdom. const { ownerNode } = info.stylesheet;
if (ownerNode) detach(ownerNode); // there is no ownerNode if it runs on jsdom.
}); if (ownerNode)
managed_styles.clear(); detach(ownerNode);
}); });
managed_styles.clear();
});
} }
/** @typedef {Object} StyleInformation
* @property {CSSStyleSheet} stylesheet
* @property {Record<string,true>} rules
*/
Loading…
Cancel
Save