jsxstyle-utils
Version:
Utilities used by jsxstyle and tooling built for jsxstyle
616 lines (536 loc) • 16.4 kB
JavaScript
define('jsxstyle-utils', ['exports'], function (exports) {
'use strict';
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;
Object.defineProperty(exports, '__esModule', { value: true });
});