react-native-reanimated
Version:
More powerful alternative to Animated library for React Native.
113 lines (108 loc) • 3.66 kB
JavaScript
;
import { IS_WINDOW_AVAILABLE, logger } from "../../common/index.js";
const CSS_STYLE_TAG_ID = 'ReanimatedCSSStyleTag';
export function configureWebCSS() {
if (!IS_WINDOW_AVAILABLE ||
// Without this check SSR crashes because document is undefined (NextExample on CI)
document.getElementById(CSS_STYLE_TAG_ID) !== null) {
return;
}
const cssStyleTag = document.createElement('style');
cssStyleTag.id = CSS_STYLE_TAG_ID;
cssStyleTag.onload = () => {
if (!cssStyleTag.sheet) {
logger.error('Failed to create CSS stylesheet.');
}
};
document.head.append(cssStyleTag);
}
function getStyleSheet() {
return document.getElementById(CSS_STYLE_TAG_ID)?.sheet ?? null;
}
// Shared registry over the single Reanimated stylesheet. Rules are appended in
// insertion order (callers that depend on source order - e.g. pseudo-selector
// cascade priority - get it) and tracked by a string key. We keep the CSSRule
// object itself and resolve its index at removal time, so the registry cannot
// drift from the actual sheet (rule indices shift when earlier rules are
// deleted, and the style tag can be replaced externally, e.g. by HMR).
const ruleByKey = new Map();
function insertSheetRule(key, cssText) {
// Without window availability check SSR crashes because document is undefined
// (NextExample on CI).
if (!IS_WINDOW_AVAILABLE || ruleByKey.has(key)) {
return false;
}
const sheet = getStyleSheet();
if (!sheet) {
logger.error('Failed to create CSS stylesheet.');
return false;
}
let index;
try {
index = sheet.insertRule(cssText, sheet.cssRules.length);
} catch {
// Browsers throw on rules they can't parse (e.g. unsupported selectors).
// Stay lenient and skip the rule instead of breaking the whole update.
logger.warn(`Failed to insert CSS rule: ${cssText}`);
return false;
}
ruleByKey.set(key, sheet.cssRules[index]);
return true;
}
function removeSheetRule(key) {
// Without this check SSR crashes because document is undefined (NextExample on CI).
if (!IS_WINDOW_AVAILABLE) {
return;
}
const rule = ruleByKey.get(key);
if (rule === undefined) {
return;
}
ruleByKey.delete(key);
const sheet = getStyleSheet();
if (!sheet) {
return;
}
// Resolve the rule's current index at removal time - it shifts when earlier
// rules are deleted (and the rule may be gone if the sheet was replaced).
const index = Array.prototype.indexOf.call(sheet.cssRules, rule);
if (index !== -1) {
sheet.deleteRule(index);
}
}
export function insertCSSAnimation(animationName, keyframes) {
insertSheetRule(animationName, `@keyframes ${animationName} { ${keyframes} }`);
}
export function removeCSSAnimation(animationName) {
removeSheetRule(animationName);
}
// Each owner (a view's `data-rps` id) maps to the keys of the rules it inserted,
// so an update can replace them and unmount can remove them.
const pseudoRuleKeysByOwner = new Map();
export function insertPseudoSelectorCSS(owner, rules) {
if (!IS_WINDOW_AVAILABLE) {
return;
}
// Replace any rules previously inserted for this owner.
removePseudoSelectorCSS(owner);
const keys = [];
rules.forEach((rule, i) => {
const key = `${owner}#${i}`;
if (insertSheetRule(key, rule)) {
keys.push(key);
}
});
pseudoRuleKeysByOwner.set(owner, keys);
}
export function removePseudoSelectorCSS(owner) {
if (!IS_WINDOW_AVAILABLE) {
return;
}
const keys = pseudoRuleKeysByOwner.get(owner);
if (!keys) {
return;
}
keys.forEach(removeSheetRule);
pseudoRuleKeysByOwner.delete(owner);
}
//# sourceMappingURL=domUtils.js.map