|
| 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