UNPKG

4.3 kBJavaScriptView Raw
1/* @flow */
2import stringHash from 'string-hash';
3
4/* ::
5type ObjectMap = { [id:string]: any };
6*/
7
8const UPPERCASE_RE = /([A-Z])/g;
9const UPPERCASE_RE_TO_KEBAB = (match /* : string */) /* : string */ => `-${match.toLowerCase()}`;
10
11export const kebabifyStyleName = (string /* : string */) /* : string */ => {
12 const result = string.replace(UPPERCASE_RE, UPPERCASE_RE_TO_KEBAB);
13 if (result[0] === 'm' && result[1] === 's' && result[2] === '-') {
14 return `-${result}`;
15 }
16 return result;
17};
18
19/**
20 * CSS properties which accept numbers but are not in units of "px".
21 * Taken from React's CSSProperty.js
22 */
23const isUnitlessNumber = {
24 animationIterationCount: true,
25 borderImageOutset: true,
26 borderImageSlice: true,
27 borderImageWidth: true,
28 boxFlex: true,
29 boxFlexGroup: true,
30 boxOrdinalGroup: true,
31 columnCount: true,
32 flex: true,
33 flexGrow: true,
34 flexPositive: true,
35 flexShrink: true,
36 flexNegative: true,
37 flexOrder: true,
38 gridRow: true,
39 gridColumn: true,
40 fontWeight: true,
41 lineClamp: true,
42 lineHeight: true,
43 opacity: true,
44 order: true,
45 orphans: true,
46 tabSize: true,
47 widows: true,
48 zIndex: true,
49 zoom: true,
50
51 // SVG-related properties
52 fillOpacity: true,
53 floodOpacity: true,
54 stopOpacity: true,
55 strokeDasharray: true,
56 strokeDashoffset: true,
57 strokeMiterlimit: true,
58 strokeOpacity: true,
59 strokeWidth: true,
60};
61
62/**
63 * Taken from React's CSSProperty.js
64 *
65 * @param {string} prefix vendor-specific prefix, eg: Webkit
66 * @param {string} key style name, eg: transitionDuration
67 * @return {string} style name prefixed with `prefix`, properly camelCased, eg:
68 * WebkitTransitionDuration
69 */
70function prefixKey(prefix, key) {
71 return prefix + key.charAt(0).toUpperCase() + key.substring(1);
72}
73
74/**
75 * Support style names that may come passed in prefixed by adding permutations
76 * of vendor prefixes.
77 * Taken from React's CSSProperty.js
78 */
79const prefixes = ['Webkit', 'ms', 'Moz', 'O'];
80
81// Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an
82// infinite loop, because it iterates over the newly added props too.
83// Taken from React's CSSProperty.js
84Object.keys(isUnitlessNumber).forEach(function(prop) {
85 prefixes.forEach(function(prefix) {
86 isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop];
87 });
88});
89
90export const stringifyValue = (
91 key /* : string */,
92 prop /* : any */
93) /* : string */ => {
94 if (typeof prop === "number") {
95 if (isUnitlessNumber[key]) {
96 return "" + prop;
97 } else {
98 return prop + "px";
99 }
100 } else {
101 return '' + prop;
102 }
103};
104
105export const stringifyAndImportantifyValue = (
106 key /* : string */,
107 prop /* : any */
108) /* : string */ => importantify(stringifyValue(key, prop));
109
110// Turn a string into a hash string of base-36 values (using letters and numbers)
111// eslint-disable-next-line no-unused-vars
112export const hashString = (string /* : string */, key /* : ?string */) /* string */ => stringHash(string).toString(36);
113
114// Hash a javascript object using JSON.stringify. This is very fast, about 3
115// microseconds on my computer for a sample object:
116// http://jsperf.com/test-hashfnv32a-hash/5
117//
118// Note that this uses JSON.stringify to stringify the objects so in order for
119// this to produce consistent hashes browsers need to have a consistent
120// ordering of objects. Ben Alpert says that Facebook depends on this, so we
121// can probably depend on this too.
122export const hashObject = (object /* : ObjectMap */) /* : string */ => hashString(JSON.stringify(object));
123
124// Given a single style value string like the "b" from "a: b;", adds !important
125// to generate "b !important".
126const importantify = (string /* : string */) /* : string */ => (
127 // Bracket string character access is very fast, and in the default case we
128 // normally don't expect there to be "!important" at the end of the string
129 // so we can use this simple check to take an optimized path. If there
130 // happens to be a "!" in this position, we follow up with a more thorough
131 // check.
132 (string[string.length - 10] === '!' && string.slice(-11) === ' !important')
133 ? string
134 : `${string} !important`
135);