UNPKG

8.71 kBJavaScriptView Raw
1import { __assign } from "tslib";
2export var InjectionMode = {
3 /**
4 * Avoids style injection, use getRules() to read the styles.
5 */
6 none: 0,
7 /**
8 * Inserts rules using the insertRule api.
9 */
10 insertNode: 1,
11 /**
12 * Appends rules using appendChild.
13 */
14 appendChild: 2,
15};
16var STYLESHEET_SETTING = '__stylesheet__';
17/**
18 * MSIE 11 doesn't cascade styles based on DOM ordering, but rather on the order that each style node
19 * is created. As such, to maintain consistent priority, IE11 should reuse a single style node.
20 */
21var REUSE_STYLE_NODE = typeof navigator !== 'undefined' && /rv:11.0/.test(navigator.userAgent);
22var _global = {};
23// Grab window.
24try {
25 _global = window;
26}
27catch (_a) {
28 /* leave as blank object */
29}
30var _stylesheet;
31/**
32 * Represents the state of styles registered in the page. Abstracts
33 * the surface for adding styles to the stylesheet, exposes helpers
34 * for reading the styles registered in server rendered scenarios.
35 *
36 * @public
37 */
38var Stylesheet = /** @class */ (function () {
39 function Stylesheet(config) {
40 this._rules = [];
41 this._preservedRules = [];
42 this._rulesToInsert = [];
43 this._counter = 0;
44 this._keyToClassName = {};
45 this._onResetCallbacks = [];
46 this._classNameToArgs = {};
47 this._config = __assign({ injectionMode: InjectionMode.insertNode, defaultPrefix: 'css', namespace: undefined, cspSettings: undefined }, config);
48 this._keyToClassName = this._config.classNameCache || {};
49 }
50 /**
51 * Gets the singleton instance.
52 */
53 Stylesheet.getInstance = function () {
54 var _a;
55 _stylesheet = _global[STYLESHEET_SETTING];
56 if (!_stylesheet || (_stylesheet._lastStyleElement && _stylesheet._lastStyleElement.ownerDocument !== document)) {
57 var fabricConfig = ((_a = _global) === null || _a === void 0 ? void 0 : _a.FabricConfig) || {};
58 _stylesheet = _global[STYLESHEET_SETTING] = new Stylesheet(fabricConfig.mergeStyles);
59 }
60 return _stylesheet;
61 };
62 /**
63 * Configures the stylesheet.
64 */
65 Stylesheet.prototype.setConfig = function (config) {
66 this._config = __assign(__assign({}, this._config), config);
67 };
68 /**
69 * Configures a reset callback.
70 *
71 * @param callback - A callback which will be called when the Stylesheet is reset.
72 */
73 Stylesheet.prototype.onReset = function (callback) {
74 this._onResetCallbacks.push(callback);
75 };
76 /**
77 * Generates a unique classname.
78 *
79 * @param displayName - Optional value to use as a prefix.
80 */
81 Stylesheet.prototype.getClassName = function (displayName) {
82 var namespace = this._config.namespace;
83 var prefix = displayName || this._config.defaultPrefix;
84 return "" + (namespace ? namespace + '-' : '') + prefix + "-" + this._counter++;
85 };
86 /**
87 * Used internally to cache information about a class which was
88 * registered with the stylesheet.
89 */
90 Stylesheet.prototype.cacheClassName = function (className, key, args, rules) {
91 this._keyToClassName[key] = className;
92 this._classNameToArgs[className] = {
93 args: args,
94 rules: rules,
95 };
96 };
97 /**
98 * Gets the appropriate classname given a key which was previously
99 * registered using cacheClassName.
100 */
101 Stylesheet.prototype.classNameFromKey = function (key) {
102 return this._keyToClassName[key];
103 };
104 /**
105 * Gets all classnames cache with the stylesheet.
106 */
107 Stylesheet.prototype.getClassNameCache = function () {
108 return this._keyToClassName;
109 };
110 /**
111 * Gets the arguments associated with a given classname which was
112 * previously registered using cacheClassName.
113 */
114 Stylesheet.prototype.argsFromClassName = function (className) {
115 var entry = this._classNameToArgs[className];
116 return entry && entry.args;
117 };
118 /**
119 * Gets the arguments associated with a given classname which was
120 * previously registered using cacheClassName.
121 */
122 Stylesheet.prototype.insertedRulesFromClassName = function (className) {
123 var entry = this._classNameToArgs[className];
124 return entry && entry.rules;
125 };
126 /**
127 * Inserts a css rule into the stylesheet.
128 * @param preserve - Preserves the rule beyond a reset boundary.
129 */
130 Stylesheet.prototype.insertRule = function (rule, preserve) {
131 var injectionMode = this._config.injectionMode;
132 var element = injectionMode !== InjectionMode.none ? this._getStyleElement() : undefined;
133 if (preserve) {
134 this._preservedRules.push(rule);
135 }
136 if (element) {
137 switch (this._config.injectionMode) {
138 case InjectionMode.insertNode:
139 var sheet = element.sheet;
140 try {
141 sheet.insertRule(rule, sheet.cssRules.length);
142 }
143 catch (e) {
144 // The browser will throw exceptions on unsupported rules (such as a moz prefix in webkit.)
145 // We need to swallow the exceptions for this scenario, otherwise we'd need to filter
146 // which could be slower and bulkier.
147 }
148 break;
149 case InjectionMode.appendChild:
150 element.appendChild(document.createTextNode(rule));
151 break;
152 }
153 }
154 else {
155 this._rules.push(rule);
156 }
157 if (this._config.onInsertRule) {
158 this._config.onInsertRule(rule);
159 }
160 };
161 /**
162 * Gets all rules registered with the stylesheet; only valid when
163 * using InsertionMode.none.
164 */
165 Stylesheet.prototype.getRules = function (includePreservedRules) {
166 return ((includePreservedRules ? this._preservedRules.join('') : '') + this._rules.join('') + this._rulesToInsert.join(''));
167 };
168 /**
169 * Resets the internal state of the stylesheet. Only used in server
170 * rendered scenarios where we're using InsertionMode.none.
171 */
172 Stylesheet.prototype.reset = function () {
173 this._rules = [];
174 this._rulesToInsert = [];
175 this._counter = 0;
176 this._classNameToArgs = {};
177 this._keyToClassName = {};
178 this._onResetCallbacks.forEach(function (callback) { return callback(); });
179 };
180 // Forces the regeneration of incoming styles without totally resetting the stylesheet.
181 Stylesheet.prototype.resetKeys = function () {
182 this._keyToClassName = {};
183 };
184 Stylesheet.prototype._getStyleElement = function () {
185 var _this = this;
186 if (!this._styleElement && typeof document !== 'undefined') {
187 this._styleElement = this._createStyleElement();
188 if (!REUSE_STYLE_NODE) {
189 // Reset the style element on the next frame.
190 window.requestAnimationFrame(function () {
191 _this._styleElement = undefined;
192 });
193 }
194 }
195 return this._styleElement;
196 };
197 Stylesheet.prototype._createStyleElement = function () {
198 var head = document.head;
199 var styleElement = document.createElement('style');
200 styleElement.setAttribute('data-merge-styles', 'true');
201 var cspSettings = this._config.cspSettings;
202 if (cspSettings) {
203 if (cspSettings.nonce) {
204 styleElement.setAttribute('nonce', cspSettings.nonce);
205 }
206 }
207 if (this._lastStyleElement) {
208 // If the `nextElementSibling` is null, then the insertBefore will act as a regular append.
209 // https://developer.mozilla.org/en-US/docs/Web/API/Node/insertBefore#Syntax
210 head.insertBefore(styleElement, this._lastStyleElement.nextElementSibling);
211 }
212 else {
213 var placeholderStyleTag = this._findPlaceholderStyleTag();
214 if (placeholderStyleTag) {
215 head.insertBefore(styleElement, placeholderStyleTag.nextElementSibling);
216 }
217 else {
218 head.insertBefore(styleElement, head.childNodes[0]);
219 }
220 }
221 this._lastStyleElement = styleElement;
222 return styleElement;
223 };
224 Stylesheet.prototype._findPlaceholderStyleTag = function () {
225 var head = document.head;
226 if (head) {
227 return head.querySelector('style[data-merge-styles]');
228 }
229 return null;
230 };
231 return Stylesheet;
232}());
233export { Stylesheet };
234//# sourceMappingURL=Stylesheet.js.map
\No newline at end of file