UNPKG

14.1 kBJavaScriptView Raw
1"use strict";
2
3var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
5Object.defineProperty(exports, "__esModule", {
6 value: true
7});
8exports.getRegistry = getRegistry;
9exports.clearRegistry = clearRegistry;
10exports.setRegistry = setRegistry;
11exports.generateComponentTheme = generateComponentTheme;
12exports.generateTheme = generateTheme;
13exports.getRegisteredThemes = getRegisteredThemes;
14exports.registerComponentTheme = registerComponentTheme;
15exports.registerTheme = registerTheme;
16exports.mountComponentStyles = mountComponentStyles;
17exports.flushComponentStyles = flushComponentStyles;
18exports.ThemeRegistry = exports.default = void 0;
19
20var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
21
22var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
23
24var _console = require("@instructure/console");
25
26var _mergeDeep = require("@instructure/ui-utils/lib/mergeDeep.js");
27
28var _isEmpty = require("@instructure/ui-utils/lib/isEmpty.js");
29
30var _StyleSheet = require("@instructure/ui-stylesheet/lib/StyleSheet.js");
31
32var _uid = require("@instructure/uid");
33
34var _getCssText = require("./getCssText.js");
35
36var _transformCss = require("./transformCss.js");
37
38var DEFAULT_THEME_KEY = '@@themeableDefaultTheme';
39var GLOBAL_THEME_REGISTRY = 'GLOBAL_THEME_REGISTRY'; // initialize the registry:
40
41if (global[GLOBAL_THEME_REGISTRY]) {
42 /*#__PURE__*/
43 ( /*#__PURE__*/0, _console.error)(false, "[themeable] A theme registry has already been initialized. Ensure that you are importing only one copy of '@instructure/ui-themeable'."); // initialize the registry using whatever has been previously defined:
44
45 setRegistry(validateRegistry(global[GLOBAL_THEME_REGISTRY]));
46} else {
47 // initialize the registry to the default/empty state:
48 clearRegistry();
49}
50
51function makeRegistry() {
52 return {
53 styleSheet: _StyleSheet.StyleSheet,
54 defaultThemeKey: null,
55 components: (0, _defineProperty2.default)({}, DEFAULT_THEME_KEY, {}),
56 themes: {},
57 registered: [] // the theme keys in the order they are registered
58
59 };
60}
61
62function validateRegistry(registry) {
63 var defaultRegistry = makeRegistry();
64
65 if (typeof registry === 'undefined') {
66 return defaultRegistry;
67 }
68
69 var valid = true;
70 Object.keys(defaultRegistry).forEach(function (key) {
71 if (typeof registry[key] === 'undefined') {
72 valid = false;
73 }
74 });
75
76 /*#__PURE__*/
77 ( /*#__PURE__*/0, _console.error)(valid, '[themeable] Invalid global theme registry!');
78 return registry;
79}
80/**
81 * Get the global theme registry
82 * @return {object} The theme registry
83 */
84
85
86function getRegistry() {
87 return global[GLOBAL_THEME_REGISTRY];
88}
89/**
90 * Set the global theme registry
91 */
92
93
94function setRegistry(registry) {
95 global[GLOBAL_THEME_REGISTRY] = registry;
96}
97/**
98 * Clear/reset the global theme registry
99 */
100
101
102function clearRegistry() {
103 setRegistry(makeRegistry());
104}
105/**
106 * Get the default theme key
107 * @return {String} the default theme key
108 */
109
110
111function getDefaultThemeKey() {
112 var _getRegistry = getRegistry(),
113 defaultThemeKey = _getRegistry.defaultThemeKey,
114 registered = _getRegistry.registered;
115
116 return defaultThemeKey || registered[registered.length - 1] || DEFAULT_THEME_KEY;
117}
118/**
119 * Get the default theme key
120 * @param {String} the default theme key
121 * @param {Object} overrides for the theme variables
122 */
123
124
125function setDefaultTheme(themeKey, overrides) {
126 var registry = getRegistry();
127 var theme = registry.themes[themeKey];
128
129 if (!theme) {
130 if (themeKey !== DEFAULT_THEME_KEY) {
131 /*#__PURE__*/
132 ( /*#__PURE__*/0, _console.error)(theme, "[themeable] Could not find theme: '".concat(themeKey, "' in the registry."));
133 }
134
135 theme = {};
136 }
137
138 registry.defaultThemeKey = themeKey;
139 registry.overrides = overrides;
140 return theme;
141}
142/**
143 * Wraps a theme and provides a method to set as default and toggle between a11y and base
144 *
145 * @param {String} themeKey
146 * @param {Object} options Provide the base theme and an optional accessible version
147 */
148
149
150function makeTheme(_ref) {
151 var key = _ref.key,
152 variables = _ref.variables,
153 a11y = _ref.a11y,
154 immutable = _ref.immutable,
155 description = _ref.description;
156 var themeKey = key || (0, _uid.uid)();
157 return {
158 key: themeKey,
159 immutable: immutable,
160 variables: (0, _objectSpread2.default)({}, variables),
161 description: description,
162 use: function use() {
163 var _ref2 = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {},
164 accessible = _ref2.accessible,
165 overrides = _ref2.overrides;
166
167 if (accessible) {
168 /*#__PURE__*/
169 ( /*#__PURE__*/0, _console.warn)(a11y && a11y.key, "[themeable] No accessible theme provided for ".concat(themeKey, "."));
170
171 if (a11y && a11y.key) {
172 setDefaultTheme(a11y.key);
173 }
174 } else {
175 setDefaultTheme(themeKey, overrides);
176 }
177 }
178 };
179}
180
181function registerTheme(theme) {
182 var registry = getRegistry();
183 var registeredTheme;
184
185 if (theme.key && registry.themes[theme.key]) {
186 registeredTheme = registry.themes[theme.key];
187 } else {
188 registeredTheme = makeTheme(theme);
189 registry.themes[registeredTheme.key] = registeredTheme;
190 registry.registered.push(registeredTheme.key);
191 }
192
193 return registeredTheme;
194}
195
196function getRegisteredTheme(themeKey) {
197 var defaultTheme = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
198 if (!themeKey) return defaultTheme;
199 var theme = getRegistry().themes[themeKey];
200
201 if (theme) {
202 return theme;
203 } else {
204 if (themeKey !== DEFAULT_THEME_KEY) {
205 /*#__PURE__*/
206 ( /*#__PURE__*/0, _console.error)(theme, "[themeable] Could not find theme: '".concat(themeKey, "' in the registry."));
207 }
208
209 return defaultTheme;
210 }
211}
212
213function getVariablesWithOverrides(themeKey, overrides) {
214 var theme = getRegisteredTheme(themeKey);
215 var variables = theme.variables || {};
216 var overridesIsEmpty = (0, _isEmpty.isEmpty)(overrides);
217
218 if (!overridesIsEmpty && theme.immutable) {
219 /*#__PURE__*/
220 ( /*#__PURE__*/0, _console.warn)(false, "[themeable] Theme, '".concat(theme.key, "', is immutable. Cannot apply overrides: ").concat(JSON.stringify(overrides)));
221 return variables;
222 }
223
224 var variablesIsEmpty = (0, _isEmpty.isEmpty)(variables);
225 if (!variablesIsEmpty && !overridesIsEmpty) return (0, _mergeDeep.mergeDeep)(variables, overrides);
226 if (variablesIsEmpty) return overrides || {};
227 return variables;
228}
229/**
230 * Merge theme variables for 'themeKey' with the defaults (and overrides)
231 * @private
232 * @param {String} themeKey
233 * @param {Object} variable Theme overrides
234 * @return {Object} A merged variables object
235 */
236
237
238function mergeWithDefaultThemeVariables(themeKey, overrides) {
239 var variables;
240
241 if (themeKey) {
242 variables = getVariablesWithOverrides(themeKey, overrides);
243 } else {
244 // fall back to defaults, but still apply overrides
245 var defaultOverrides = getRegistry().overrides;
246 var defaultOverridesIsEmpty = (0, _isEmpty.isEmpty)(defaultOverrides);
247
248 if (!defaultOverridesIsEmpty && !(0, _isEmpty.isEmpty)(overrides)) {
249 variables = (0, _mergeDeep.mergeDeep)(defaultOverrides, overrides);
250 } else if (defaultOverridesIsEmpty) {
251 variables = overrides;
252 } else {
253 variables = defaultOverrides;
254 }
255 }
256
257 return getVariablesWithOverrides(getDefaultThemeKey(), variables);
258}
259/**
260 * Wraps a component theme function to merge its return values with the return
261 * values of the default function
262 * @private
263 * @param {Function} componentThemeFunction
264 * @param {String} themeKey
265 * @return {Object} A wrapped theme object
266 */
267
268
269function makeComponentTheme(componentThemeFunction, themeKey) {
270 return function (variables) {
271 var theme = {};
272
273 if (typeof componentThemeFunction === 'function') {
274 theme = componentThemeFunction(variables);
275 } // so that the components for the themeKey can
276 // just specify overrides we merge them with defaults here
277
278
279 var defaultComponentTheme = {};
280
281 if (typeof componentThemeFunction[themeKey] === 'function') {
282 defaultComponentTheme = componentThemeFunction[themeKey](variables);
283 }
284
285 if (!(0, _isEmpty.isEmpty)(defaultComponentTheme) && !(0, _isEmpty.isEmpty)(theme)) {
286 theme = (0, _objectSpread2.default)({}, theme, {}, defaultComponentTheme);
287 } else if ((0, _isEmpty.isEmpty)(theme)) {
288 theme = defaultComponentTheme;
289 }
290
291 return theme;
292 };
293}
294/**
295 * Register a component theme function
296 *
297 * @param {String} key The theme key for the component (e.g., [Link.theme])
298 * @param {Function} componentThemeFunction The function to use for preparing this component's theme
299 */
300
301
302function registerComponentTheme(componentKey, componentThemeFunction) {
303 var _getRegistry2 = getRegistry(),
304 components = _getRegistry2.components;
305
306 if (typeof componentThemeFunction !== 'function') {
307 return;
308 }
309
310 components[DEFAULT_THEME_KEY][componentKey] = componentThemeFunction;
311 Object.keys(componentThemeFunction).forEach(function (themeKey) {
312 // eslint-disable-next-line no-prototype-builtins
313 if (!components.hasOwnProperty(themeKey)) {
314 components[themeKey] = {};
315 }
316
317 components[themeKey][componentKey] = makeComponentTheme(componentThemeFunction, themeKey);
318 });
319}
320
321function getRegisteredComponents(themeKey) {
322 var _getRegistry3 = getRegistry(),
323 components = _getRegistry3.components;
324
325 var t = themeKey || getDefaultThemeKey(); // fall back to the default component theme functions
326
327 return (0, _objectSpread2.default)({}, components[DEFAULT_THEME_KEY], {}, components[t]);
328}
329
330function getRegisteredComponent(themeKey, componentKey) {
331 var _getRegistry4 = getRegistry(),
332 components = _getRegistry4.components;
333
334 return components[themeKey] && components[themeKey][componentKey] || components[DEFAULT_THEME_KEY][componentKey];
335}
336/**
337 * Generate themes for all registered [@themeable](#themeable) components,
338 * to be used by [`<ApplyTheme />`](#ApplyTheme).
339 *
340 * @param {String} themeKey The theme to use (for global theme variables across components)
341 * @param {Object} overrides theme variable overrides (usually for user defined values)
342 * @return {Object} A theme config to use with `<ApplyTheme />`
343 */
344
345
346function generateTheme(themeKey, overrides) {
347 var registry = getRegistry();
348
349 /*#__PURE__*/
350 ( /*#__PURE__*/0, _console.error)(registry.registered.length > 0, '[themeable] No themes have been registered. ' + 'Import a theme from @instructure/ui-themes or register a custom theme with registerTheme ' + '(see @instructure/ui-themeable).');
351 var components = getRegisteredComponents(themeKey);
352 var theme = {};
353 var variables = mergeWithDefaultThemeVariables(themeKey, overrides);
354
355 if ((0, _isEmpty.isEmpty)(variables)) {
356 return;
357 }
358
359 Object.getOwnPropertySymbols(components).forEach(function (componentKey) {
360 theme[componentKey] = components[componentKey](variables);
361 });
362 return theme;
363}
364/**
365 * Generate theme variables for a @themeable component.
366 * If no themeKey is provided, the default theme will be generated.
367 *
368 * @param {Symbol} key The theme key for the component (e.g., [Link.theme])
369 * @param {String} themeKey The theme to use to generate the variables (falls back to the default theme)
370 * @param {Object} overrides overrides for component level theme variables (usually user defined)
371 * @return {Object} A theme config for the component
372 */
373
374
375function generateComponentTheme(componentKey, themeKey, overrides) {
376 var t = themeKey || getDefaultThemeKey();
377 var theme = getRegisteredTheme(t);
378 var componentTheme = {};
379 var cachedComponentTheme = theme[componentKey];
380
381 if (cachedComponentTheme) {
382 // use the cached component theme if it exists
383 componentTheme = cachedComponentTheme;
384 } else {
385 var variables = (0, _objectSpread2.default)({
386 borders: {},
387 breakpoints: {},
388 colors: {},
389 forms: {},
390 media: {},
391 shadows: {},
392 spacing: {},
393 stacking: {},
394 transitions: {},
395 typography: {}
396 }, mergeWithDefaultThemeVariables(themeKey));
397 var componentThemeFunction = getRegisteredComponent(t, componentKey);
398
399 if (typeof componentThemeFunction === 'function') {
400 try {
401 componentTheme = componentThemeFunction(variables);
402 } catch (e) {
403 /*#__PURE__*/
404 ( /*#__PURE__*/0, _console.error)(false, "[themeable] ".concat(e));
405 }
406 }
407 }
408
409 if ((0, _isEmpty.isEmpty)(overrides)) {
410 return theme[componentKey] = componentTheme;
411 } else if (theme.immutable) {
412 /*#__PURE__*/
413 ( /*#__PURE__*/0, _console.warn)(false, "[themeable] Theme '".concat(t, "' is immutable. Cannot apply overrides for '").concat(componentKey.toString(), "': ").concat(JSON.stringify(overrides)));
414 return componentTheme;
415 } else if ((0, _isEmpty.isEmpty)(componentTheme)) {
416 return overrides;
417 } else {
418 return (0, _objectSpread2.default)({}, componentTheme, {}, overrides);
419 }
420}
421
422function getRegisteredThemes() {
423 return getRegistry().themes;
424}
425
426function mountComponentStyles(template, defaultTheme, componentId) {
427 var _getRegistry5 = getRegistry(),
428 styleSheet = _getRegistry5.styleSheet;
429
430 if (styleSheet && !styleSheet.mounted(componentId)) {
431 var cssText = (0, _getCssText.getCssText)(template, defaultTheme, componentId);
432 styleSheet.mount(componentId, (0, _transformCss.toRules)(cssText));
433 }
434}
435
436function flushComponentStyles() {
437 var _getRegistry6 = getRegistry(),
438 styleSheet = _getRegistry6.styleSheet;
439
440 styleSheet && styleSheet.flush();
441}
442
443var ThemeRegistry = {
444 getRegistry: getRegistry,
445 clearRegistry: clearRegistry,
446 setRegistry: setRegistry,
447 generateComponentTheme: generateComponentTheme,
448 generateTheme: generateTheme,
449 getRegisteredThemes: getRegisteredThemes,
450 registerComponentTheme: registerComponentTheme,
451 registerTheme: registerTheme,
452 mountComponentStyles: mountComponentStyles,
453 flushComponentStyles: flushComponentStyles
454};
455exports.ThemeRegistry = ThemeRegistry;
456var _default = ThemeRegistry;
457exports.default = _default;
\No newline at end of file