@uifabric/merge-styles
Version:
Style loading utilities.
236 lines • 8.81 kB
JavaScript
import { __assign } from "tslib";
export var InjectionMode = {
/**
* Avoids style injection, use getRules() to read the styles.
*/
none: 0,
/**
* Inserts rules using the insertRule api.
*/
insertNode: 1,
/**
* Appends rules using appendChild.
*/
appendChild: 2,
};
var STYLESHEET_SETTING = '__stylesheet__';
/**
* MSIE 11 doesn't cascade styles based on DOM ordering, but rather on the order that each style node
* is created. As such, to maintain consistent priority, IE11 should reuse a single style node.
*/
var REUSE_STYLE_NODE = typeof navigator !== 'undefined' && /rv:11.0/.test(navigator.userAgent);
var _global = {};
// Grab window.
try {
_global = window;
}
catch (_a) {
/* leave as blank object */
}
var _stylesheet;
/**
* Represents the state of styles registered in the page. Abstracts
* the surface for adding styles to the stylesheet, exposes helpers
* for reading the styles registered in server rendered scenarios.
*
* @public
*/
var Stylesheet = /** @class */ (function () {
function Stylesheet(config) {
this._rules = [];
this._preservedRules = [];
this._rulesToInsert = [];
this._counter = 0;
this._keyToClassName = {};
this._onResetCallbacks = [];
this._classNameToArgs = {};
this._config = __assign({ injectionMode: InjectionMode.insertNode, defaultPrefix: 'css', namespace: undefined, cspSettings: undefined }, config);
this._keyToClassName = this._config.classNameCache || {};
}
/**
* Gets the singleton instance.
*/
Stylesheet.getInstance = function () {
var _a;
_stylesheet = _global[STYLESHEET_SETTING];
if (!_stylesheet || (_stylesheet._lastStyleElement && _stylesheet._lastStyleElement.ownerDocument !== document)) {
var fabricConfig = ((_a = _global) === null || _a === void 0 ? void 0 : _a.FabricConfig) || {};
_stylesheet = _global[STYLESHEET_SETTING] = new Stylesheet(fabricConfig.mergeStyles);
}
return _stylesheet;
};
/**
* Configures the stylesheet.
*/
Stylesheet.prototype.setConfig = function (config) {
this._config = __assign(__assign({}, this._config), config);
};
/**
* Configures a reset callback.
*
* @param callback - A callback which will be called when the Stylesheet is reset.
*/
Stylesheet.prototype.onReset = function (callback) {
this._onResetCallbacks.push(callback);
};
/**
* Generates a unique classname.
*
* @param displayName - Optional value to use as a prefix.
*/
Stylesheet.prototype.getClassName = function (displayName) {
var namespace = this._config.namespace;
var prefix = displayName || this._config.defaultPrefix;
return "" + (namespace ? namespace + '-' : '') + prefix + "-" + this._counter++;
};
/**
* Used internally to cache information about a class which was
* registered with the stylesheet.
*/
Stylesheet.prototype.cacheClassName = function (className, key, args, rules) {
this._keyToClassName[key] = className;
this._classNameToArgs[className] = {
args: args,
rules: rules,
};
};
/**
* Gets the appropriate classname given a key which was previously
* registered using cacheClassName.
*/
Stylesheet.prototype.classNameFromKey = function (key) {
return this._keyToClassName[key];
};
/**
* Gets all classnames cache with the stylesheet.
*/
Stylesheet.prototype.getClassNameCache = function () {
return this._keyToClassName;
};
/**
* Gets the arguments associated with a given classname which was
* previously registered using cacheClassName.
*/
Stylesheet.prototype.argsFromClassName = function (className) {
var entry = this._classNameToArgs[className];
return entry && entry.args;
};
/**
* Gets the arguments associated with a given classname which was
* previously registered using cacheClassName.
*/
Stylesheet.prototype.insertedRulesFromClassName = function (className) {
var entry = this._classNameToArgs[className];
return entry && entry.rules;
};
/**
* Inserts a css rule into the stylesheet.
* @param preserve - Preserves the rule beyond a reset boundary.
*/
Stylesheet.prototype.insertRule = function (rule, preserve) {
var injectionMode = this._config.injectionMode;
var element = injectionMode !== InjectionMode.none ? this._getStyleElement() : undefined;
if (preserve) {
this._preservedRules.push(rule);
}
if (element) {
switch (this._config.injectionMode) {
case InjectionMode.insertNode:
var sheet = element.sheet;
try {
sheet.insertRule(rule, sheet.cssRules.length);
}
catch (e) {
// The browser will throw exceptions on unsupported rules (such as a moz prefix in webkit.)
// We need to swallow the exceptions for this scenario, otherwise we'd need to filter
// which could be slower and bulkier.
}
break;
case InjectionMode.appendChild:
element.appendChild(document.createTextNode(rule));
break;
}
}
else {
this._rules.push(rule);
}
if (this._config.onInsertRule) {
this._config.onInsertRule(rule);
}
};
/**
* Gets all rules registered with the stylesheet; only valid when
* using InsertionMode.none.
*/
Stylesheet.prototype.getRules = function (includePreservedRules) {
return ((includePreservedRules ? this._preservedRules.join('') : '') + this._rules.join('') + this._rulesToInsert.join(''));
};
/**
* Resets the internal state of the stylesheet. Only used in server
* rendered scenarios where we're using InsertionMode.none.
*/
Stylesheet.prototype.reset = function () {
this._rules = [];
this._rulesToInsert = [];
this._counter = 0;
this._classNameToArgs = {};
this._keyToClassName = {};
this._onResetCallbacks.forEach(function (callback) { return callback(); });
};
// Forces the regeneration of incoming styles without totally resetting the stylesheet.
Stylesheet.prototype.resetKeys = function () {
this._keyToClassName = {};
};
Stylesheet.prototype._getStyleElement = function () {
var _this = this;
if (!this._styleElement && typeof document !== 'undefined') {
this._styleElement = this._createStyleElement();
if (!REUSE_STYLE_NODE) {
// Reset the style element on the next frame.
window.requestAnimationFrame(function () {
_this._styleElement = undefined;
});
}
}
return this._styleElement;
};
Stylesheet.prototype._createStyleElement = function () {
var head = document.head;
var styleElement = document.createElement('style');
var nodeToInsertBefore = null;
styleElement.setAttribute('data-merge-styles', 'true');
var cspSettings = this._config.cspSettings;
if (cspSettings) {
if (cspSettings.nonce) {
styleElement.setAttribute('nonce', cspSettings.nonce);
}
}
if (this._lastStyleElement) {
// If the `nextElementSibling` is null, then the insertBefore will act as a regular append.
// https://developer.mozilla.org/en-US/docs/Web/API/Node/insertBefore#Syntax
nodeToInsertBefore = this._lastStyleElement.nextElementSibling;
}
else {
var placeholderStyleTag = this._findPlaceholderStyleTag();
if (placeholderStyleTag) {
nodeToInsertBefore = placeholderStyleTag.nextElementSibling;
}
else {
nodeToInsertBefore = head.childNodes[0];
}
}
head.insertBefore(styleElement, head.contains(nodeToInsertBefore) ? nodeToInsertBefore : null);
this._lastStyleElement = styleElement;
return styleElement;
};
Stylesheet.prototype._findPlaceholderStyleTag = function () {
var head = document.head;
if (head) {
return head.querySelector('style[data-merge-styles]');
}
return null;
};
return Stylesheet;
}());
export { Stylesheet };
//# sourceMappingURL=Stylesheet.js.map