UNPKG

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