Skip to content

Commit c10c97f

Browse files
committed
applyAnimatedValues
1 parent 267772e commit c10c97f

File tree

1 file changed

+128
-0
lines changed

1 file changed

+128
-0
lines changed

spring/src/applyAnimatedValues.ts

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import { Lookup } from "./utils";
2+
3+
const isCustomPropRE = /^--/;
4+
5+
type Value = string | number | boolean | null;
6+
7+
function dangerousStyleValue(name: string, value: Value) {
8+
if (value == null || typeof value === "boolean" || value === "") return "";
9+
if (
10+
typeof value === "number" &&
11+
value !== 0 &&
12+
!isCustomPropRE.test(name) &&
13+
!(isUnitlessNumber.hasOwnProperty(name) && isUnitlessNumber[name])
14+
)
15+
return value + "px";
16+
// Presumes implicit 'px' suffix for unitless numbers
17+
return ("" + value).trim();
18+
}
19+
20+
const attributeCache: Lookup<string> = {};
21+
22+
type Instance = HTMLDivElement & { style?: Lookup };
23+
24+
export function applyAnimatedValues(instance: Instance, props: Lookup) {
25+
if (!instance.nodeType || !instance.setAttribute) {
26+
return false;
27+
}
28+
29+
const isFilterElement =
30+
instance.nodeName === "filter" ||
31+
(instance.parentNode && instance.parentNode.nodeName === "filter");
32+
33+
const { style, children, scrollTop, scrollLeft, ...attributes } = props!;
34+
35+
const values = Object.values(attributes);
36+
const names = Object.keys(attributes).map((name) =>
37+
isFilterElement || instance.hasAttribute(name)
38+
? name
39+
: attributeCache[name] ||
40+
(attributeCache[name] = name.replace(
41+
/([A-Z])/g,
42+
// Attributes are written in dash case
43+
(n) => "-" + n.toLowerCase()
44+
))
45+
);
46+
47+
if (children !== void 0) {
48+
instance.textContent = children;
49+
}
50+
51+
// Apply CSS styles
52+
for (let name in style) {
53+
if (style.hasOwnProperty(name)) {
54+
const value = dangerousStyleValue(name, style[name]);
55+
if (isCustomPropRE.test(name)) {
56+
instance.style.setProperty(name, value);
57+
} else {
58+
instance.style[name] = value;
59+
}
60+
}
61+
}
62+
63+
// Apply DOM attributes
64+
names.forEach((name, i) => {
65+
instance.setAttribute(name, values[i]);
66+
});
67+
68+
if (scrollTop !== void 0) {
69+
instance.scrollTop = scrollTop;
70+
}
71+
if (scrollLeft !== void 0) {
72+
instance.scrollLeft = scrollLeft;
73+
}
74+
}
75+
76+
let isUnitlessNumber: { [key: string]: true } = {
77+
animationIterationCount: true,
78+
borderImageOutset: true,
79+
borderImageSlice: true,
80+
borderImageWidth: true,
81+
boxFlex: true,
82+
boxFlexGroup: true,
83+
boxOrdinalGroup: true,
84+
columnCount: true,
85+
columns: true,
86+
flex: true,
87+
flexGrow: true,
88+
flexPositive: true,
89+
flexShrink: true,
90+
flexNegative: true,
91+
flexOrder: true,
92+
gridRow: true,
93+
gridRowEnd: true,
94+
gridRowSpan: true,
95+
gridRowStart: true,
96+
gridColumn: true,
97+
gridColumnEnd: true,
98+
gridColumnSpan: true,
99+
gridColumnStart: true,
100+
fontWeight: true,
101+
lineClamp: true,
102+
lineHeight: true,
103+
opacity: true,
104+
order: true,
105+
orphans: true,
106+
tabSize: true,
107+
widows: true,
108+
zIndex: true,
109+
zoom: true,
110+
// SVG-related properties
111+
fillOpacity: true,
112+
floodOpacity: true,
113+
stopOpacity: true,
114+
strokeDasharray: true,
115+
strokeDashoffset: true,
116+
strokeMiterlimit: true,
117+
strokeOpacity: true,
118+
strokeWidth: true,
119+
};
120+
121+
const prefixKey = (prefix: string, key: string) =>
122+
prefix + key.charAt(0).toUpperCase() + key.substring(1);
123+
const prefixes = ["Webkit", "Ms", "Moz", "O"];
124+
125+
isUnitlessNumber = Object.keys(isUnitlessNumber).reduce((acc, prop) => {
126+
prefixes.forEach((prefix) => (acc[prefixKey(prefix, prop)] = acc[prop]));
127+
return acc;
128+
}, isUnitlessNumber);

0 commit comments

Comments
 (0)