UNPKG

12.4 kBJavaScriptView Raw
1var canUseDOM = !!(
2 typeof window !== 'undefined' &&
3 window.document &&
4 window.document.createElement
5);
6var styleElement;
7if (
8 typeof module !== 'undefined' &&
9 module.hot &&
10 typeof module.hot.addDisposeHandler === 'function'
11) {
12 // gross
13 var hot = module.hot;
14 if (typeof hot.data === 'object') {
15 styleElement = hot.data.styleElement;
16 }
17 hot.addDisposeHandler(function(data) {
18 data.styleElement = styleElement;
19 });
20}
21if (canUseDOM && !styleElement) {
22 styleElement = document.createElement('style');
23 styleElement.type = 'text/css';
24 styleElement.appendChild(document.createTextNode('/* jsxstyle */'));
25 document.head.appendChild(styleElement);
26}
27function addStyleToHead(rule) {
28 if (styleElement) {
29 var sheet = styleElement.sheet;
30 try {
31 sheet.insertRule(rule, sheet.cssRules.length);
32 } catch (insertError) {
33 // insertRule will fail for rules with pseudoelements the browser doesn't support.
34 // see: https://github.com/smyte/jsxstyle/issues/75
35 if (process.env.NODE_ENV !== 'production') {
36 console.error(
37 '[jsxstyle] Could not insert rule at position ' +
38 sheet.cssRules.length +
39 ': `' +
40 rule +
41 '`'
42 );
43 }
44 }
45 }
46}
47
48var componentStyles = {
49 Block: { display: 'block' },
50 Box: null,
51 Col: { display: 'flex', flexDirection: 'column' },
52 Grid: { display: 'grid' },
53 Inline: { display: 'inline' },
54 InlineBlock: { display: 'inline-block' },
55 Row: { display: 'flex', flexDirection: 'row' },
56};
57
58/**
59 * Copyright 2013-present, Facebook, Inc.
60 * All rights reserved.
61 *
62 * This source code is licensed under the BSD-style license found in the
63 * LICENSE file in the root directory of this source tree. An additional grant
64 * of patent rights can be found in the PATENTS file in the same directory.
65 *
66 */
67var isUnitlessNumber = {
68 animationIterationCount: true,
69 borderImageOutset: true,
70 borderImageSlice: true,
71 borderImageWidth: true,
72 boxFlex: true,
73 boxFlexGroup: true,
74 boxOrdinalGroup: true,
75 columnCount: true,
76 columns: true,
77 flex: true,
78 flexGrow: true,
79 flexNegative: true,
80 flexOrder: true,
81 flexPositive: true,
82 flexShrink: true,
83 fontWeight: true,
84 gridColumn: true,
85 gridColumnEnd: true,
86 gridColumnSpan: true,
87 gridColumnStart: true,
88 gridRow: true,
89 gridRowEnd: true,
90 gridRowSpan: true,
91 gridRowStart: true,
92 lineClamp: true,
93 lineHeight: true,
94 opacity: true,
95 order: true,
96 orphans: true,
97 tabSize: true,
98 widows: true,
99 zIndex: true,
100 zoom: true,
101 // SVG-related properties
102 fillOpacity: true,
103 floodOpacity: true,
104 stopOpacity: true,
105 strokeDasharray: true,
106 strokeDashoffset: true,
107 strokeMiterlimit: true,
108 strokeOpacity: true,
109 strokeWidth: true,
110};
111function prefixKey(prefix, key) {
112 return prefix + key.charAt(0).toUpperCase() + key.substring(1);
113}
114var prefixes = ['Webkit', 'ms', 'Moz', 'O'];
115Object.keys(isUnitlessNumber).forEach(function(prop) {
116 prefixes.forEach(function(prefix) {
117 isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop];
118 });
119});
120function dangerousStyleValue(name, value) {
121 var isEmpty = value == null || typeof value === 'boolean' || value === '';
122 if (isEmpty) {
123 return '';
124 }
125 if (
126 typeof value === 'number' &&
127 value !== 0 &&
128 !(isUnitlessNumber.hasOwnProperty(name) && isUnitlessNumber[name])
129 ) {
130 if (value > -1 && value < 1) {
131 return Math.round(value * 1e6) / 1e4 + '%';
132 }
133 return value + 'px';
134 }
135 if (!value.toString) {
136 // values that lack a toString method on their prototype will throw a TypeError
137 // see https://github.com/smyte/jsxstyle/issues/112
138 if (process.env.NODE_ENV === 'development') {
139 console.error(
140 'Value for prop `%s` (`%o`) cannot be stringified.',
141 name,
142 value
143 );
144 }
145 return '';
146 }
147 return ('' + value).trim();
148}
149
150var uppercasePattern = /([A-Z])/g;
151var msPattern = /^ms-/;
152var hyphenateCache = {};
153function hyphenateStyleName(styleName) {
154 if (hyphenateCache.hasOwnProperty(styleName)) {
155 return hyphenateCache[styleName];
156 }
157 var hyphenatedString = styleName
158 .replace(uppercasePattern, '-$1')
159 .toLowerCase()
160 .replace(msPattern, '-ms-');
161 hyphenateCache[styleName] = hyphenatedString;
162 return hyphenateCache[styleName];
163}
164
165// global flag makes subsequent calls of capRegex.test advance to the next match
166var capRegex = /[A-Z]/g;
167var pseudoelements = {
168 after: true,
169 before: true,
170 placeholder: true,
171 selection: true,
172};
173var pseudoclasses = {
174 active: true,
175 checked: true,
176 disabled: true,
177 empty: true,
178 enabled: true,
179 focus: true,
180 hover: true,
181 invalid: true,
182 required: true,
183 target: true,
184 valid: true,
185};
186var specialCaseProps = {
187 children: true,
188 class: true,
189 className: true,
190 component: true,
191 mediaQueries: true,
192 props: true,
193 style: true,
194};
195function getStyleKeysForProps(props, pretty) {
196 if (pretty === void 0) {
197 pretty = false;
198 }
199 if (typeof props !== 'object' || props === null) {
200 return null;
201 }
202 var propKeys = Object.keys(props).sort();
203 var keyCount = propKeys.length;
204 if (keyCount === 0) {
205 return null;
206 }
207 var mediaQueries = props.mediaQueries;
208 var hasMediaQueries = typeof mediaQueries === 'object';
209 var usesMediaQueries = false;
210 var styleKeyObj = {};
211 var classNameKey = '';
212 var seenMQs = {};
213 var mqSortKeys = {};
214 if (hasMediaQueries) {
215 var idx = -1;
216 for (var k in mediaQueries) {
217 mqSortKeys[k] = '@' + (1000 + ++idx);
218 }
219 }
220 for (var idx = -1; ++idx < keyCount; ) {
221 var originalPropName = propKeys[idx];
222 if (
223 specialCaseProps.hasOwnProperty(originalPropName) ||
224 !props.hasOwnProperty(originalPropName)
225 ) {
226 continue;
227 }
228 var propName = originalPropName;
229 var propSansMQ = void 0;
230 var pseudoelement = void 0;
231 var pseudoclass = void 0;
232 var mqKey = void 0;
233 capRegex.lastIndex = 0;
234 var splitIndex = 0;
235 var prefix =
236 capRegex.test(originalPropName) &&
237 capRegex.lastIndex > 1 &&
238 originalPropName.slice(0, capRegex.lastIndex - 1);
239 // check for media query prefix
240 if (prefix && hasMediaQueries && mediaQueries.hasOwnProperty(prefix)) {
241 usesMediaQueries = true;
242 mqKey = prefix;
243 splitIndex = capRegex.lastIndex - 1;
244 propSansMQ =
245 originalPropName[splitIndex].toLowerCase() +
246 originalPropName.slice(splitIndex + 1);
247 prefix =
248 capRegex.test(originalPropName) &&
249 propSansMQ.slice(0, capRegex.lastIndex - splitIndex - 1);
250 }
251 // check for pseudoelement prefix
252 if (prefix && pseudoelements.hasOwnProperty(prefix)) {
253 pseudoelement = prefix;
254 splitIndex = capRegex.lastIndex - 1;
255 prefix =
256 capRegex.test(originalPropName) &&
257 originalPropName[splitIndex].toLowerCase() +
258 originalPropName.slice(splitIndex + 1, capRegex.lastIndex - 1);
259 }
260 // check for pseudoclass prefix
261 if (prefix && pseudoclasses.hasOwnProperty(prefix)) {
262 pseudoclass = prefix;
263 splitIndex = capRegex.lastIndex - 1;
264 }
265 // trim prefixes off propName
266 if (splitIndex > 0) {
267 propName =
268 originalPropName[splitIndex].toLowerCase() +
269 originalPropName.slice(splitIndex + 1);
270 }
271 var styleValue = dangerousStyleValue(propName, props[originalPropName]);
272 if (styleValue === '') {
273 continue;
274 }
275 var mediaQuery = mqKey && mediaQueries[mqKey];
276 var mqSortKey = mqKey && mqSortKeys[mqKey];
277 var key =
278 '.' +
279 (mqSortKey || '') +
280 (pseudoclass ? ':' + pseudoclass : '') +
281 (pseudoelement ? '::' + pseudoelement : '');
282 if (!styleKeyObj.hasOwnProperty(key)) {
283 styleKeyObj[key] = { styles: pretty ? '\n' : '' };
284 if (mediaQuery) {
285 styleKeyObj[key].mediaQuery = mediaQuery;
286 }
287 if (pseudoclass) {
288 styleKeyObj[key].pseudoclass = pseudoclass;
289 }
290 if (pseudoelement) {
291 styleKeyObj[key].pseudoelement = pseudoelement;
292 }
293 }
294 if (mediaQuery) {
295 seenMQs[mediaQuery] = seenMQs[mediaQuery] || '';
296 seenMQs[mediaQuery] += propSansMQ + ':' + styleValue + ';';
297 } else {
298 classNameKey += originalPropName + ':' + styleValue + ';';
299 }
300 styleKeyObj[key].styles +=
301 (pretty ? ' ' : '') +
302 hyphenateStyleName(propName) +
303 (pretty ? ': ' : ':') +
304 styleValue +
305 (pretty ? ';\n' : ';');
306 }
307 // append media query key
308 if (usesMediaQueries) {
309 var mqKeys = Object.keys(seenMQs).sort();
310 for (var idx = -1, len = mqKeys.length; ++idx < len; ) {
311 var mediaQuery = mqKeys[idx];
312 classNameKey += '@' + mediaQuery + '~' + seenMQs[mediaQuery];
313 }
314 }
315 if (classNameKey === '') {
316 return null;
317 }
318 styleKeyObj.classNameKey = classNameKey;
319 return styleKeyObj;
320}
321
322/* tslint:disable no-bitwise */
323// thx darksky: https://git.io/v9kWO
324function stringHash(str) {
325 var hash = 5381;
326 var i = str.length;
327 while (i) {
328 hash = (hash * 33) ^ str.charCodeAt(--i);
329 }
330 /* JavaScript does bitwise operations (like XOR, above) on 32-bit signed
331 * integers. Since we want the results to be always positive, convert the
332 * signed int to an unsigned by doing an unsigned bitshift. */
333 return hash >>> 0;
334}
335
336function cannotInject() {
337 throw new Error(
338 'jsxstyle error: `injectOptions` must be called before any jsxstyle components mount.'
339 );
340}
341function alreadyInjected() {
342 throw new Error(
343 'jsxstyle error: `injectOptions` should be called once and only once.'
344 );
345}
346function getStringHash(key, props) {
347 return '_' + stringHash(key).toString(36);
348}
349function getStyleCache() {
350 var _classNameCache = {};
351 var getClassNameForKey = getStringHash;
352 var onInsertRule;
353 var pretty = false;
354 var styleCache = {
355 reset: function() {
356 _classNameCache = {};
357 },
358 injectOptions: function(options) {
359 if (options) {
360 if (options.getClassName) {
361 getClassNameForKey = options.getClassName;
362 }
363 if (options.onInsertRule) {
364 onInsertRule = options.onInsertRule;
365 }
366 if (options.pretty) {
367 pretty = options.pretty;
368 }
369 }
370 styleCache.injectOptions = alreadyInjected;
371 },
372 getClassName: function(props, classNameProp) {
373 styleCache.injectOptions = cannotInject;
374 var styleObj = getStyleKeysForProps(props, pretty);
375 if (typeof styleObj !== 'object' || styleObj === null) {
376 return classNameProp || null;
377 }
378 var key = styleObj.classNameKey;
379 if (key && !_classNameCache.hasOwnProperty(key)) {
380 _classNameCache[key] = getClassNameForKey(key, props);
381 delete styleObj.classNameKey;
382 Object.keys(styleObj)
383 .sort()
384 .forEach(function(k) {
385 var selector = '.' + _classNameCache[key];
386 // prettier-ignore
387 var _a = styleObj[k], pseudoclass = _a.pseudoclass, pseudoelement = _a.pseudoelement, mediaQuery = _a.mediaQuery, styles = _a.styles;
388 var rule =
389 selector +
390 (pseudoclass ? ':' + pseudoclass : '') +
391 (pseudoelement ? '::' + pseudoelement : '') +
392 (' {' + styles + '}');
393 if (mediaQuery) {
394 rule = '@media ' + mediaQuery + ' { ' + rule + ' }';
395 }
396 if (
397 onInsertRule &&
398 // if the function returns false, bail.
399 onInsertRule(rule, props) === false
400 ) {
401 return;
402 }
403 addStyleToHead(rule);
404 });
405 }
406 return _classNameCache[key] && classNameProp
407 ? classNameProp + ' ' + _classNameCache[key]
408 : _classNameCache[key] || classNameProp || null;
409 },
410 };
411 return styleCache;
412}
413
414export {
415 addStyleToHead,
416 componentStyles,
417 dangerousStyleValue,
418 getStyleCache,
419 getStyleKeysForProps,
420 pseudoelements,
421 pseudoclasses,
422 hyphenateStyleName,
423 stringHash,
424};