UNPKG

jsxstyle-utils

Version:

Utilities used by jsxstyle and tooling built for jsxstyle

608 lines (528 loc) 15.2 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var canUseDOM = !!( typeof window !== 'undefined' && window.document && window.document.createElement ); var styleElement; if ( typeof module !== 'undefined' && module.hot && typeof module.hot.addDisposeHandler === 'function' ) { // gross var _module = module, hot = _module.hot; if (hot.data != null) { styleElement = hot.data.styleElement; } hot.addDisposeHandler(function (data) { data.styleElement = styleElement; }); } if (canUseDOM && !styleElement) { styleElement = document.createElement('style'); styleElement.type = 'text/css'; if (typeof __webpack_nonce__ !== 'undefined') { styleElement.nonce = __webpack_nonce__; } styleElement.appendChild(document.createTextNode('/* jsxstyle */')); document.head.appendChild(styleElement); } function addStyleToHead(rule) { if (styleElement && styleElement.sheet) { var sheet = styleElement.sheet; try { sheet.insertRule(rule, sheet.cssRules.length); } catch (insertError) { // insertRule will fail for rules with pseudoelements the browser doesn't support. // see: https://github.com/jsxstyle/jsxstyle/issues/75 if (process.env.NODE_ENV !== 'production') { console.error( '[jsxstyle] Could not insert rule at position ' + sheet.cssRules.length + ': `' + rule + '`' ); } } } } var componentStyles = { Block: { display: 'block', }, Box: null, Col: { display: 'flex', flexDirection: 'column', }, Grid: { display: 'grid', }, Inline: { display: 'inline', }, InlineBlock: { display: 'inline-block', }, InlineCol: { display: 'inline-flex', flexDirection: 'column', }, InlineRow: { display: 'inline-flex', flexDirection: 'row', }, Row: { display: 'flex', flexDirection: 'row', }, // deprecated Flex: { display: 'flex', }, InlineFlex: { display: 'inline-flex', }, Table: { display: 'table', }, TableCell: { display: 'table-cell', }, TableRow: { display: 'table-row', }, }; /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * */ // A hearty blend of the following two files: // https://github.com/facebook/react/blob/master/packages/react-dom/src/shared/CSSProperty.js // https://github.com/facebook/react/blob/master/packages/react-dom/src/shared/dangerousStyleValue.js var isUnitlessNumber = { animationIterationCount: true, borderImageOutset: true, borderImageSlice: true, borderImageWidth: true, boxFlex: true, boxFlexGroup: true, boxOrdinalGroup: true, columnCount: true, columns: true, flex: true, flexGrow: true, flexNegative: true, flexOrder: true, flexPositive: true, flexShrink: true, fontWeight: true, gridArea: true, gridColumn: true, gridColumnEnd: true, gridColumnSpan: true, gridColumnStart: true, gridRow: true, gridRowEnd: true, gridRowSpan: true, gridRowStart: true, lineClamp: true, lineHeight: true, opacity: true, order: true, orphans: true, tabSize: true, widows: true, zIndex: true, zoom: true, // SVG-related properties fillOpacity: true, floodOpacity: true, stopOpacity: true, strokeDasharray: true, strokeDashoffset: true, strokeMiterlimit: true, strokeOpacity: true, strokeWidth: true, }; function prefixKey(prefix, key) { return prefix + key.charAt(0).toUpperCase() + key.substring(1); } var prefixes = ['Webkit', 'ms', 'Moz', 'O']; Object.keys(isUnitlessNumber).forEach(function (prop) { prefixes.forEach(function (prefix) { isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop]; }); }); function dangerousStyleValue(name, value) { var isEmpty = value == null || typeof value === 'boolean' || value === ''; if (isEmpty) { return ''; } if ( typeof value === 'number' && value !== 0 && !(isUnitlessNumber.hasOwnProperty(name) && isUnitlessNumber[name]) ) { if (value > -1 && value < 1) { return Math.round(value * 1e6) / 1e4 + '%'; } return value + 'px'; } if (!value.toString) { // values that lack a toString method on their prototype will throw a TypeError // see https://github.com/jsxstyle/jsxstyle/issues/112 if (process.env.NODE_ENV === 'development') { console.error( 'Value for prop `%s` (`%o`) cannot be stringified.', name, value ); } return ''; } return ('' + value).trim(); } var uppercasePattern = /([A-Z])/g; var msPattern = /^ms-/; var hyphenateCache = {}; function hyphenateStyleName(styleName) { if (hyphenateCache.hasOwnProperty(styleName)) { return hyphenateCache[styleName]; } var hyphenatedString = styleName .replace(uppercasePattern, '-$1') .toLowerCase() .replace(msPattern, '-ms-'); hyphenateCache[styleName] = hyphenatedString; return hyphenateCache[styleName]; } /* tslint:disable no-bitwise */ // thx darksky: https://git.io/v9kWO function stringHash(str) { var hash = 5381; var i = str.length; while (i) { hash = (hash * 33) ^ str.charCodeAt(--i); } /* JavaScript does bitwise operations (like XOR, above) on 32-bit signed * integers. Since we want the results to be always positive, convert the * signed int to an unsigned by doing an unsigned bitshift. */ return hash >>> 0; } var capRegex = /[A-Z]/g; var pseudoelements = { after: true, before: true, placeholder: true, selection: true, }; var pseudoclasses = { active: true, checked: true, disabled: true, empty: true, enabled: true, focus: true, hover: true, invalid: true, link: true, required: true, target: true, valid: true, }; var specialCaseProps = { children: true, class: true, className: true, component: true, mediaQueries: true, props: true, style: true, }; var sameAxisPropNames = { paddingH: ['paddingLeft', 'paddingRight'], paddingV: ['paddingTop', 'paddingBottom'], marginH: ['marginLeft', 'marginRight'], marginV: ['marginTop', 'marginBottom'], }; function getStyleKeysForProps(props, pretty) { if (pretty === void 0) { pretty = false; } if (typeof props !== 'object' || props === null) { return null; } var propKeys = Object.keys(props).sort(); var keyCount = propKeys.length; if (keyCount === 0) { return null; } var mediaQueries = props.mediaQueries; var usesMediaQueries = false; var stylesByKey = {}; var styleKeyObj = { classNameKey: '', stylesByKey: stylesByKey, }; var classNameKey = ''; var animations; var seenMQs = {}; var mqSortKeys = {}; if (mediaQueries != null) { var idx = -1; for (var k in mediaQueries) { mqSortKeys[k] = '@' + (1000 + ++idx); } } for (var _idx = -1; ++_idx < keyCount; ) { var originalPropName = propKeys[_idx]; if ( specialCaseProps.hasOwnProperty(originalPropName) || !props.hasOwnProperty(originalPropName) ) { continue; } var propName = originalPropName; var propSansMQ = void 0; var pseudoelement = void 0; var pseudoclass = void 0; var mqKey = void 0; capRegex.lastIndex = 0; var splitIndex = 0; var prefix = capRegex.test(originalPropName) && capRegex.lastIndex > 1 && originalPropName.slice(0, capRegex.lastIndex - 1); // check for media query prefix if (prefix && mediaQueries != null && mediaQueries.hasOwnProperty(prefix)) { usesMediaQueries = true; mqKey = prefix; splitIndex = capRegex.lastIndex - 1; propSansMQ = originalPropName[splitIndex].toLowerCase() + originalPropName.slice(splitIndex + 1); prefix = capRegex.test(originalPropName) && propSansMQ.slice(0, capRegex.lastIndex - splitIndex - 1); } // check for pseudoelement prefix if (prefix && pseudoelements.hasOwnProperty(prefix)) { pseudoelement = prefix; splitIndex = capRegex.lastIndex - 1; prefix = capRegex.test(originalPropName) && originalPropName[splitIndex].toLowerCase() + originalPropName.slice(splitIndex + 1, capRegex.lastIndex - 1); } // check for pseudoclass prefix if (prefix && pseudoclasses.hasOwnProperty(prefix)) { pseudoclass = prefix; splitIndex = capRegex.lastIndex - 1; } // trim prefixes off propName if (splitIndex > 0) { propName = originalPropName[splitIndex].toLowerCase() + originalPropName.slice(splitIndex + 1); } var styleValue = props[originalPropName]; var space = pretty ? ' ' : ''; var colon = ':' + space; var newline = pretty ? '\n' : ''; var semicolon = ';' + newline; var indent = pretty ? ' ' : ''; if ( propName === 'animation' && styleValue && typeof styleValue === 'object' ) { var animationValue = newline; for (var _k in styleValue) { var obj = styleValue[_k]; animationValue += _k + space + '{' + newline; for (var childPropName in obj) { var _value = dangerousStyleValue(childPropName, obj[childPropName]); animationValue += indent + hyphenateStyleName(childPropName) + colon + _value + semicolon; } animationValue += '}' + newline; } var animationKey = 'jsxstyle_' + stringHash(animationValue).toString(36); propName = 'animationName'; styleValue = animationKey; animations = animations || {}; animations[animationKey] = animationValue; } else { styleValue = dangerousStyleValue(propName, props[originalPropName]); if (styleValue === '') { continue; } } var mediaQuery = mqKey && mediaQueries[mqKey]; var mqSortKey = mqKey && mqSortKeys[mqKey]; var key = '.' + (mqSortKey || '') + (pseudoclass ? ':' + pseudoclass : '') + (pseudoelement ? '::' + pseudoelement : ''); if (!stylesByKey.hasOwnProperty(key)) { stylesByKey[key] = { styles: newline, }; if (mediaQuery) { stylesByKey[key].mediaQuery = mediaQuery; } if (pseudoclass) { stylesByKey[key].pseudoclass = pseudoclass; } if (pseudoelement) { stylesByKey[key].pseudoelement = pseudoelement; } } if (mediaQuery) { seenMQs[mediaQuery] = seenMQs[mediaQuery] || ''; seenMQs[mediaQuery] += propSansMQ + ':' + styleValue + ';'; } else { classNameKey += originalPropName + ':' + styleValue + ';'; } var value = colon + styleValue + semicolon; var propArray = sameAxisPropNames[propName]; if (propArray) { stylesByKey[key].styles += indent + hyphenateStyleName(propArray[0]) + value + indent + hyphenateStyleName(propArray[1]) + value; } else { stylesByKey[key].styles += indent + hyphenateStyleName(propName) + value; } } // append media query key if (usesMediaQueries) { var mqKeys = Object.keys(seenMQs).sort(); for (var _idx2 = -1, len = mqKeys.length; ++_idx2 < len; ) { var _mediaQuery = mqKeys[_idx2]; classNameKey += '@' + _mediaQuery + '~' + seenMQs[_mediaQuery]; } } if (classNameKey === '') { return null; } if (animations) { styleKeyObj.animations = animations; } styleKeyObj.classNameKey = classNameKey; return styleKeyObj; } function cannotInject() { throw new Error( 'jsxstyle error: `injectOptions` must be called before any jsxstyle components mount.' ); } function alreadyInjected() { throw new Error( 'jsxstyle error: `injectOptions` should be called once and only once.' ); } var getStringHash = function getStringHash(key) { return '_' + stringHash(key).toString(36); }; function getStyleCache() { var _classNameCache = {}; var getClassNameForKey = getStringHash; var onInsertRule; var pretty = false; var styleCache = { reset: function reset() { _classNameCache = {}; }, injectOptions: function injectOptions(options) { if (options) { if (options.getClassName) { getClassNameForKey = options.getClassName; } if (options.onInsertRule) { onInsertRule = options.onInsertRule; } if (options.pretty) { pretty = options.pretty; } } styleCache.injectOptions = alreadyInjected; }, getClassName: function getClassName(props, classNameProp) { styleCache.injectOptions = cannotInject; var styleObj = getStyleKeysForProps(props, pretty); if (styleObj == null) { return classNameProp || null; } var key = styleObj.classNameKey; if (key && !_classNameCache.hasOwnProperty(key)) { _classNameCache[key] = getClassNameForKey(key, props); Object.keys(styleObj.stylesByKey) .sort() .forEach(function (k) { var selector = '.' + _classNameCache[key]; // prettier-ignore var _styleObj$stylesByKey = styleObj.stylesByKey[k], pseudoclass = _styleObj$stylesByKey.pseudoclass, pseudoelement = _styleObj$stylesByKey.pseudoelement, mediaQuery = _styleObj$stylesByKey.mediaQuery, styles = _styleObj$stylesByKey.styles; var rule = selector + (pseudoclass ? ':' + pseudoclass : '') + (pseudoelement ? '::' + pseudoelement : '') + (' {' + styles + '}'); if (mediaQuery) { rule = '@media ' + mediaQuery + ' { ' + rule + ' }'; } if ( onInsertRule && // if the function returns false, bail. onInsertRule(rule, props) === false ) { return; } addStyleToHead(rule); }); } var animations = styleObj.animations; if (animations) { for (var animationKey in animations) { var rule = '@keyframes ' + animationKey + ' {' + animations[animationKey] + '}'; if (!onInsertRule || onInsertRule(rule, props) !== false) { addStyleToHead(rule); } } } return _classNameCache[key] && classNameProp ? classNameProp + ' ' + _classNameCache[key] : _classNameCache[key] || classNameProp || null; }, }; return styleCache; } exports.addStyleToHead = addStyleToHead; exports.componentStyles = componentStyles; exports.dangerousStyleValue = dangerousStyleValue; exports.getStyleCache = getStyleCache; exports.getStyleKeysForProps = getStyleKeysForProps; exports.hyphenateStyleName = hyphenateStyleName; exports.pseudoclasses = pseudoclasses; exports.pseudoelements = pseudoelements; exports.stringHash = stringHash;