@uifabric/merge-styles
Version:
Style loading utilities.
261 lines • 10.5 kB
JavaScript
import { __spreadArrays } from "tslib";
import { Stylesheet } from './Stylesheet';
import { kebabRules } from './transforms/kebabRules';
import { prefixRules } from './transforms/prefixRules';
import { provideUnits } from './transforms/provideUnits';
import { rtlifyRules } from './transforms/rtlifyRules';
var DISPLAY_NAME = 'displayName';
function getDisplayName(rules) {
var rootStyle = rules && rules['&'];
return rootStyle ? rootStyle.displayName : undefined;
}
var globalSelectorRegExp = /\:global\((.+?)\)/g;
/**
* Finds comma separated selectors in a :global() e.g. ":global(.class1, .class2, .class3)"
* and wraps them each in their own global ":global(.class1), :global(.class2), :global(.class3)"
*
* @param selectorWithGlobals The selector to process
* @returns The updated selector
*/
function expandCommaSeparatedGlobals(selectorWithGlobals) {
// We the selector does not have a :global() we can shortcut
if (!globalSelectorRegExp.test(selectorWithGlobals)) {
return selectorWithGlobals;
}
var replacementInfo = [];
var findGlobal = /\:global\((.+?)\)/g;
var match = null;
// Create a result list for global selectors so we can replace them.
while ((match = findGlobal.exec(selectorWithGlobals))) {
// Only if the found selector is a comma separated list we'll process it.
if (match[1].indexOf(',') > -1) {
replacementInfo.push([
match.index,
match.index + match[0].length,
// Wrap each of the found selectors in :global()
match[1]
.split(',')
.map(function (v) { return ":global(" + v.trim() + ")"; })
.join(', '),
]);
}
}
// Replace the found selectors with their wrapped variants in reverse order
return replacementInfo
.reverse()
.reduce(function (selector, _a) {
var matchIndex = _a[0], matchEndIndex = _a[1], replacement = _a[2];
var prefix = selector.slice(0, matchIndex);
var suffix = selector.slice(matchEndIndex);
return prefix + replacement + suffix;
}, selectorWithGlobals);
}
function expandSelector(newSelector, currentSelector) {
if (newSelector.indexOf(':global(') >= 0) {
return newSelector.replace(globalSelectorRegExp, '$1');
}
else if (newSelector.indexOf(':') === 0) {
return currentSelector + newSelector;
}
else if (newSelector.indexOf('&') < 0) {
return currentSelector + ' ' + newSelector;
}
return newSelector;
}
function extractSelector(currentSelector, rules, selector, value) {
if (rules === void 0) { rules = { __order: [] }; }
if (selector.indexOf('@') === 0) {
selector = selector + '{' + currentSelector;
extractRules([value], rules, selector);
}
else if (selector.indexOf(',') > -1) {
expandCommaSeparatedGlobals(selector)
.split(',')
.map(function (s) { return s.trim(); })
.forEach(function (separatedSelector) {
return extractRules([value], rules, expandSelector(separatedSelector, currentSelector));
});
}
else {
extractRules([value], rules, expandSelector(selector, currentSelector));
}
}
function extractRules(args, rules, currentSelector) {
if (rules === void 0) { rules = { __order: [] }; }
if (currentSelector === void 0) { currentSelector = '&'; }
var stylesheet = Stylesheet.getInstance();
var currentRules = rules[currentSelector];
if (!currentRules) {
currentRules = {};
rules[currentSelector] = currentRules;
rules.__order.push(currentSelector);
}
for (var _i = 0, args_1 = args; _i < args_1.length; _i++) {
var arg = args_1[_i];
// If the arg is a string, we need to look up the class map and merge.
if (typeof arg === 'string') {
var expandedRules = stylesheet.argsFromClassName(arg);
if (expandedRules) {
extractRules(expandedRules, rules, currentSelector);
}
// Else if the arg is an array, we need to recurse in.
}
else if (Array.isArray(arg)) {
extractRules(arg, rules, currentSelector);
}
else {
for (var prop in arg) {
if (arg.hasOwnProperty(prop)) {
var propValue = arg[prop];
if (prop === 'selectors') {
// every child is a selector.
var selectors = arg.selectors;
for (var newSelector in selectors) {
if (selectors.hasOwnProperty(newSelector)) {
extractSelector(currentSelector, rules, newSelector, selectors[newSelector]);
}
}
}
else if (typeof propValue === 'object') {
// prop is a selector.
if (propValue !== null) {
extractSelector(currentSelector, rules, prop, propValue);
}
}
else {
if (propValue !== undefined) {
// Else, add the rule to the currentSelector.
if (prop === 'margin' || prop === 'padding') {
expandQuads(currentRules, prop, propValue);
}
else {
currentRules[prop] = propValue;
}
}
}
}
}
}
}
return rules;
}
function expandQuads(currentRules, name, value) {
var parts = typeof value === 'string' ? value.split(' ') : [value];
currentRules[name + 'Top'] = parts[0];
currentRules[name + 'Right'] = parts[1] || parts[0];
currentRules[name + 'Bottom'] = parts[2] || parts[0];
currentRules[name + 'Left'] = parts[3] || parts[1] || parts[0];
}
function getKeyForRules(options, rules) {
var serialized = [options.rtl ? 'rtl' : 'ltr'];
var hasProps = false;
for (var _i = 0, _a = rules.__order; _i < _a.length; _i++) {
var selector = _a[_i];
serialized.push(selector);
var rulesForSelector = rules[selector];
for (var propName in rulesForSelector) {
if (rulesForSelector.hasOwnProperty(propName) && rulesForSelector[propName] !== undefined) {
hasProps = true;
serialized.push(propName, rulesForSelector[propName]);
}
}
}
return hasProps ? serialized.join('') : undefined;
}
function repeatString(target, count) {
if (count <= 0) {
return '';
}
if (count === 1) {
return target;
}
return target + repeatString(target, count - 1);
}
export function serializeRuleEntries(options, ruleEntries) {
if (!ruleEntries) {
return '';
}
var allEntries = [];
for (var entry in ruleEntries) {
if (ruleEntries.hasOwnProperty(entry) && entry !== DISPLAY_NAME && ruleEntries[entry] !== undefined) {
allEntries.push(entry, ruleEntries[entry]);
}
}
// Apply transforms.
for (var i = 0; i < allEntries.length; i += 2) {
kebabRules(allEntries, i);
provideUnits(allEntries, i);
rtlifyRules(options, allEntries, i);
prefixRules(allEntries, i);
}
// Apply punctuation.
for (var i = 1; i < allEntries.length; i += 4) {
allEntries.splice(i, 1, ':', allEntries[i], ';');
}
return allEntries.join('');
}
export function styleToRegistration(options) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
var rules = extractRules(args);
var key = getKeyForRules(options, rules);
if (key) {
var stylesheet = Stylesheet.getInstance();
var registration = {
className: stylesheet.classNameFromKey(key),
key: key,
args: args,
};
if (!registration.className) {
registration.className = stylesheet.getClassName(getDisplayName(rules));
var rulesToInsert = [];
for (var _a = 0, _b = rules.__order; _a < _b.length; _a++) {
var selector = _b[_a];
rulesToInsert.push(selector, serializeRuleEntries(options, rules[selector]));
}
registration.rulesToInsert = rulesToInsert;
}
return registration;
}
return undefined;
}
/**
* Insert style to stylesheet.
* @param registration Style registration.
* @param specificityMultiplier Number of times classname selector is repeated in the css rule.
* This is to increase css specificity in case it's needed. Default to 1.
*/
export function applyRegistration(registration, specificityMultiplier) {
if (specificityMultiplier === void 0) { specificityMultiplier = 1; }
var stylesheet = Stylesheet.getInstance();
var className = registration.className, key = registration.key, args = registration.args, rulesToInsert = registration.rulesToInsert;
if (rulesToInsert) {
// rulesToInsert is an ordered array of selector/rule pairs.
for (var i = 0; i < rulesToInsert.length; i += 2) {
var rules = rulesToInsert[i + 1];
if (rules) {
var selector = rulesToInsert[i];
selector = selector.replace(/&/g, repeatString("." + registration.className, specificityMultiplier));
// Insert. Note if a media query, we must close the query with a final bracket.
var processedRule = selector + "{" + rules + "}" + (selector.indexOf('@') === 0 ? '}' : '');
stylesheet.insertRule(processedRule);
}
}
stylesheet.cacheClassName(className, key, args, rulesToInsert);
}
}
export function styleToClassName(options) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
var registration = styleToRegistration.apply(void 0, __spreadArrays([options], args));
if (registration) {
applyRegistration(registration, options.specificityMultiplier);
return registration.className;
}
return '';
}
//# sourceMappingURL=styleToClassName.js.map