UNPKG

@descope/sdk-mixins

Version:
275 lines (272 loc) 19.1 kB
import { __classPrivateFieldGet, __classPrivateFieldSet } from 'tslib'; import { createSingletonMixin, compose } from '@descope/sdk-helpers'; import { configMixin } from '../configMixin/configMixin.js'; import { createValidateAttributesMixin } from '../createValidateAttributesMixin/createValidateAttributesMixin.js'; import { descopeUiMixin } from '../descopeUiMixin/descopeUiMixin.js'; import { initElementMixin } from '../initElementMixin.js'; import { initLifecycleMixin } from '../initLifecycleMixin.js'; import { staticResourcesMixin } from '../staticResourcesMixin/staticResourcesMixin.js'; import { DEFAULT_STYLE_ID } from './constants.js'; import { deepMergeNonEmpty, loadFont, flattenToVars, loadDevTheme } from './helpers.js'; import { observeAttributesMixin } from '../observeAttributesMixin/observeAttributesMixin.js'; import { UI_COMPONENTS_URL_KEY } from '../descopeUiMixin/constants.js'; import { injectStyleMixin } from '../injectStyleMixin.js'; import { tenantIdMixin } from '../tenantIdMixin.js'; const themeValidation = (_, theme) => (theme || false) && theme !== 'light' && theme !== 'dark' && 'Supported theme values are "light", "dark", or leave empty for using the OS theme'; const themeMixin = createSingletonMixin((superclass) => { var _ThemeMixinClass_instances, _ThemeMixinClass_globalStyle, _ThemeMixinClass_tenantStyle, _ThemeMixinClass_customStyle, _ThemeMixinClass_getThemeOverrideString, _ThemeMixinClass__themeResource, _ThemeMixinClass__tenantThemeResource, _ThemeMixinClass_fetchTheme, _ThemeMixinClass_themeResource_get, _ThemeMixinClass_loadGlobalStyle, _ThemeMixinClass_fetchTenantTheme, _ThemeMixinClass_tenantThemeResource_get, _ThemeMixinClass_loadTenantStyle, _ThemeMixinClass_loadCustomStyle, _ThemeMixinClass_loadComponentsStyle, _ThemeMixinClass_getFontsConfig, _ThemeMixinClass_loadFonts, _ThemeMixinClass_applyTheme, _ThemeMixinClass_onThemeChange, _ThemeMixinClass_loadTheme, _ThemeMixinClass_toggleOsThemeChangeListener, _a; const BaseClass = compose(createValidateAttributesMixin({ theme: themeValidation }), tenantIdMixin, staticResourcesMixin, initLifecycleMixin, descopeUiMixin, configMixin, initElementMixin, observeAttributesMixin, injectStyleMixin)(superclass); return _a = class ThemeMixinClass extends BaseClass { constructor() { super(...arguments); _ThemeMixinClass_instances.add(this); _ThemeMixinClass_globalStyle.set(this, void 0); _ThemeMixinClass_tenantStyle.set(this, void 0); _ThemeMixinClass_customStyle.set(this, void 0); _ThemeMixinClass__themeResource.set(this, void 0); _ThemeMixinClass__tenantThemeResource.set(this, void 0); _ThemeMixinClass_onThemeChange.set(this, () => { __classPrivateFieldGet(this, _ThemeMixinClass_instances, "m", _ThemeMixinClass_loadTheme).call(this); __classPrivateFieldGet(this, _ThemeMixinClass_toggleOsThemeChangeListener, "f").call(this, this.getAttribute('theme') === 'os'); }); // add or remove os theme change listener _ThemeMixinClass_toggleOsThemeChangeListener.set(this, (listen) => { var _b, _c, _d; const method = listen ? 'addEventListener' : 'removeEventListener'; (_d = (_c = (_b = window .matchMedia) === null || _b === void 0 ? void 0 : _b.call(window, '(prefers-color-scheme: dark)')) === null || _c === void 0 ? void 0 : _c[method]) === null || _d === void 0 ? void 0 : _d.call(_c, 'change', () => __classPrivateFieldGet(this, _ThemeMixinClass_instances, "m", _ThemeMixinClass_loadTheme).call(this)); }); } get theme() { var _b, _c; const theme = this.getAttribute('theme'); if (theme === 'os') { const isOsDark = window.matchMedia && ((_c = (_b = window.matchMedia) === null || _b === void 0 ? void 0 : _b.call(window, '(prefers-color-scheme: dark)')) === null || _c === void 0 ? void 0 : _c.matches); return isOsDark ? 'dark' : 'light'; } return theme || 'light'; } get styleId() { return this.getAttribute('style-id') || DEFAULT_STYLE_ID; } get themeOverride() { const raw = this.getAttribute('theme-override'); if (!raw) return null; try { return JSON.parse(raw); } catch (e) { this.logger.error('Failed to parse theme-override attribute. error: ', e); return null; } } async init() { var _b; await ((_b = super.init) === null || _b === void 0 ? void 0 : _b.call(this)); __classPrivateFieldGet(this, _ThemeMixinClass_onThemeChange, "f").call(this); await Promise.all([ __classPrivateFieldGet(this, _ThemeMixinClass_instances, "m", _ThemeMixinClass_loadGlobalStyle).call(this), __classPrivateFieldGet(this, _ThemeMixinClass_instances, "m", _ThemeMixinClass_loadTenantStyle).call(this), __classPrivateFieldGet(this, _ThemeMixinClass_instances, "m", _ThemeMixinClass_loadComponentsStyle).call(this), ]); __classPrivateFieldGet(this, _ThemeMixinClass_instances, "m", _ThemeMixinClass_loadCustomStyle).call(this); this.observeAttributes(['theme'], __classPrivateFieldGet(this, _ThemeMixinClass_onThemeChange, "f")); this.observeAttributes(['theme-override'], () => __classPrivateFieldGet(this, _ThemeMixinClass_instances, "m", _ThemeMixinClass_loadCustomStyle).call(this)); this.observeAttributes(['tenant'], () => { __classPrivateFieldSet(this, _ThemeMixinClass__tenantThemeResource, null, "f"); __classPrivateFieldGet(this, _ThemeMixinClass_instances, "m", _ThemeMixinClass_loadTenantStyle).call(this); __classPrivateFieldGet(this, _ThemeMixinClass_instances, "m", _ThemeMixinClass_loadComponentsStyle).call(this); }); this.observeAttributes(['style-id'], () => { __classPrivateFieldSet(this, _ThemeMixinClass__themeResource, null, "f"); __classPrivateFieldGet(this, _ThemeMixinClass_instances, "m", _ThemeMixinClass_loadFonts).call(this); __classPrivateFieldGet(this, _ThemeMixinClass_instances, "m", _ThemeMixinClass_loadGlobalStyle).call(this); __classPrivateFieldGet(this, _ThemeMixinClass_instances, "m", _ThemeMixinClass_loadComponentsStyle).call(this); }); } }, _ThemeMixinClass_globalStyle = new WeakMap(), _ThemeMixinClass_tenantStyle = new WeakMap(), _ThemeMixinClass_customStyle = new WeakMap(), _ThemeMixinClass__themeResource = new WeakMap(), _ThemeMixinClass__tenantThemeResource = new WeakMap(), _ThemeMixinClass_onThemeChange = new WeakMap(), _ThemeMixinClass_toggleOsThemeChangeListener = new WeakMap(), _ThemeMixinClass_instances = new WeakSet(), _ThemeMixinClass_getThemeOverrideString = function _ThemeMixinClass_getThemeOverrideString() { const override = this.themeOverride; if (!override) return ''; return ['light', 'dark'] .map((theme) => { var _b, _c, _d, _e, _f, _g; const primary = (_d = (_c = (_b = override[theme]) === null || _b === void 0 ? void 0 : _b.globals) === null || _c === void 0 ? void 0 : _c.colors) === null || _d === void 0 ? void 0 : _d.primary; const secondary = (_g = (_f = (_e = override[theme]) === null || _e === void 0 ? void 0 : _e.globals) === null || _f === void 0 ? void 0 : _f.colors) === null || _g === void 0 ? void 0 : _g.secondary; if (!primary && !secondary) return ''; return `[data-theme="${theme}"]{${flattenToVars({ colors: { primary, secondary } }, (msg) => this.logger.error(msg))}}`; }) .join(''); }, _ThemeMixinClass_fetchTheme = async function _ThemeMixinClass_fetchTheme() { try { const { body: fetchedTheme } = await this.fetchStaticResource(`${this.styleId}.json`, 'json'); // In development mode, we sometimes want to override the UI components URL // The override components might have a different theme, so we need to merge it with the project theme in order to see the components correctly if (process.env.NODE_ENV === 'development') { if (localStorage === null || localStorage === void 0 ? void 0 : localStorage.getItem(UI_COMPONENTS_URL_KEY)) { try { this.logger.warn('You are in DEV mode, and UI components override URL was found\ntrying to merge project theme with the default theme of the UI components'); const devTheme = await loadDevTheme(); if (devTheme) { fetchedTheme.light.components = Object.assign(Object.assign({}, fetchedTheme.light.components), devTheme.light.components); fetchedTheme.dark.components = Object.assign(Object.assign({}, fetchedTheme.dark.components), devTheme.dark.components); this.logger.warn('Theme was merged successfully'); // eslint-disable-next-line no-console console.log('%cNOTICE! This is not the theme that will be used in production!\n\nMake sure to test it without the override UI components URL!', 'color: black; background-color:yellow; font-size: x-large'); } } catch (e) { this.logger.error('Failed to merge UI components theme\n', e); } } } return fetchedTheme; } catch (e) { this.logger.error('Cannot fetch theme file', 'make sure that your projectId & flowId are correct'); } return undefined; }, _ThemeMixinClass_themeResource_get = function _ThemeMixinClass_themeResource_get() { if (!__classPrivateFieldGet(this, _ThemeMixinClass__themeResource, "f")) { __classPrivateFieldSet(this, _ThemeMixinClass__themeResource, __classPrivateFieldGet(this, _ThemeMixinClass_instances, "m", _ThemeMixinClass_fetchTheme).call(this), "f"); __classPrivateFieldGet(this, _ThemeMixinClass__themeResource, "f").then((theme) => this.logger.debug('Fetched theme', theme)); } return __classPrivateFieldGet(this, _ThemeMixinClass__themeResource, "f"); }, _ThemeMixinClass_loadGlobalStyle = async function _ThemeMixinClass_loadGlobalStyle() { var _b, _c; const theme = await __classPrivateFieldGet(this, _ThemeMixinClass_instances, "a", _ThemeMixinClass_themeResource_get); if (!theme) { return; } const styles = (((_b = theme === null || theme === void 0 ? void 0 : theme.light) === null || _b === void 0 ? void 0 : _b.globals) || '') + (((_c = theme === null || theme === void 0 ? void 0 : theme.dark) === null || _c === void 0 ? void 0 : _c.globals) || ''); if (!__classPrivateFieldGet(this, _ThemeMixinClass_globalStyle, "f")) { // Use prepend so project global style always precedes tenant style regardless of fetch order __classPrivateFieldSet(this, _ThemeMixinClass_globalStyle, this.injectStyle(styles, { prepend: true }), "f"); } else { __classPrivateFieldGet(this, _ThemeMixinClass_globalStyle, "f").replaceSync(styles); } }, _ThemeMixinClass_fetchTenantTheme = async function _ThemeMixinClass_fetchTenantTheme() { const tenantId = this.tenantId; if (!tenantId) return undefined; try { const { body: fetchedTenantTheme } = await this.fetchStaticResource(`${tenantId}/theme.json`, 'json'); return fetchedTenantTheme; } catch (e) { this.logger.error('Cannot fetch tenant theme file', 'make sure that your tenantId, projectId & flowId are correct'); } return undefined; }, _ThemeMixinClass_tenantThemeResource_get = function _ThemeMixinClass_tenantThemeResource_get() { if (!__classPrivateFieldGet(this, _ThemeMixinClass__tenantThemeResource, "f")) { __classPrivateFieldSet(this, _ThemeMixinClass__tenantThemeResource, __classPrivateFieldGet(this, _ThemeMixinClass_instances, "m", _ThemeMixinClass_fetchTenantTheme).call(this), "f"); } return __classPrivateFieldGet(this, _ThemeMixinClass__tenantThemeResource, "f"); }, _ThemeMixinClass_loadTenantStyle = async function _ThemeMixinClass_loadTenantStyle() { var _b, _c, _d, _e; if (!this.tenantId) { (_b = __classPrivateFieldGet(this, _ThemeMixinClass_tenantStyle, "f")) === null || _b === void 0 ? void 0 : _b.replaceSync(''); return; } const tenantTheme = await __classPrivateFieldGet(this, _ThemeMixinClass_instances, "a", _ThemeMixinClass_tenantThemeResource_get); if (!tenantTheme) { (_c = __classPrivateFieldGet(this, _ThemeMixinClass_tenantStyle, "f")) === null || _c === void 0 ? void 0 : _c.replaceSync(''); return; } const styles = (((_d = tenantTheme.light) === null || _d === void 0 ? void 0 : _d.globals) || '') + (((_e = tenantTheme.dark) === null || _e === void 0 ? void 0 : _e.globals) || ''); if (!__classPrivateFieldGet(this, _ThemeMixinClass_tenantStyle, "f")) { __classPrivateFieldSet(this, _ThemeMixinClass_tenantStyle, this.injectStyle(styles), "f"); } else { __classPrivateFieldGet(this, _ThemeMixinClass_tenantStyle, "f").replaceSync(styles); } }, _ThemeMixinClass_loadCustomStyle = async function _ThemeMixinClass_loadCustomStyle() { var _b; if (!this.themeOverride) { (_b = __classPrivateFieldGet(this, _ThemeMixinClass_customStyle, "f")) === null || _b === void 0 ? void 0 : _b.replaceSync(''); return; } const overrideString = __classPrivateFieldGet(this, _ThemeMixinClass_instances, "m", _ThemeMixinClass_getThemeOverrideString).call(this); if (!__classPrivateFieldGet(this, _ThemeMixinClass_customStyle, "f")) { __classPrivateFieldSet(this, _ThemeMixinClass_customStyle, this.injectStyle(overrideString), "f"); } else { __classPrivateFieldGet(this, _ThemeMixinClass_customStyle, "f").replaceSync(overrideString); } }, _ThemeMixinClass_loadComponentsStyle = async function _ThemeMixinClass_loadComponentsStyle() { var _b, _c, _d, _e; const theme = await __classPrivateFieldGet(this, _ThemeMixinClass_instances, "a", _ThemeMixinClass_themeResource_get); if (!theme) return; const descopeUi = await this.descopeUi; if (!(descopeUi === null || descopeUi === void 0 ? void 0 : descopeUi.componentsThemeManager)) return; const tenantTheme = await __classPrivateFieldGet(this, _ThemeMixinClass_instances, "a", _ThemeMixinClass_tenantThemeResource_get); descopeUi.componentsThemeManager.themes = { light: deepMergeNonEmpty(((_b = theme === null || theme === void 0 ? void 0 : theme.light) === null || _b === void 0 ? void 0 : _b.components) || {}, ((_c = tenantTheme === null || tenantTheme === void 0 ? void 0 : tenantTheme.light) === null || _c === void 0 ? void 0 : _c.components) || {}), dark: deepMergeNonEmpty(((_d = theme === null || theme === void 0 ? void 0 : theme.dark) === null || _d === void 0 ? void 0 : _d.components) || {}, ((_e = tenantTheme === null || tenantTheme === void 0 ? void 0 : tenantTheme.dark) === null || _e === void 0 ? void 0 : _e.components) || {}), }; }, _ThemeMixinClass_getFontsConfig = async function _ThemeMixinClass_getFontsConfig() { var _b, _c; const { projectConfig } = (await this.config) || {}; const newConfig = (_b = projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.styles) === null || _b === void 0 ? void 0 : _b[this.styleId]; const oldConfig = projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.cssTemplate; const config = newConfig || oldConfig; const fonts = (_c = config === null || config === void 0 ? void 0 : config[this.theme]) === null || _c === void 0 ? void 0 : _c.fonts; return fonts; }, _ThemeMixinClass_loadFonts = async function _ThemeMixinClass_loadFonts() { const fonts = await __classPrivateFieldGet(this, _ThemeMixinClass_instances, "m", _ThemeMixinClass_getFontsConfig).call(this); if (fonts) { Object.values(fonts).forEach((font) => { if (font.url) { this.logger.debug(`Loading font from URL "${font.url}"`); loadFont(font.url); } }); } else { this.logger.debug('No fonts to load'); } }, _ThemeMixinClass_applyTheme = async function _ThemeMixinClass_applyTheme() { this.rootElement.setAttribute('data-theme', this.theme); const descopeUi = await this.descopeUi; if (descopeUi === null || descopeUi === void 0 ? void 0 : descopeUi.componentsThemeManager) { descopeUi.componentsThemeManager.currentThemeName = this.theme; } }, _ThemeMixinClass_loadTheme = function _ThemeMixinClass_loadTheme() { __classPrivateFieldGet(this, _ThemeMixinClass_instances, "m", _ThemeMixinClass_loadFonts).call(this); __classPrivateFieldGet(this, _ThemeMixinClass_instances, "m", _ThemeMixinClass_applyTheme).call(this); }, _a; }); export { themeMixin }; //# sourceMappingURL=themeMixin.js.map