UNPKG

ng-devui

Version:

DevUI components based on Angular

898 lines (884 loc) 34.8 kB
import cssVars from 'css-vars-ponyfill'; import { ReplaySubject } from 'rxjs'; class Theme { constructor(theme) { this.id = theme.id; this.name = theme.name; this.cnName = theme.cnName || this.name; this.data = theme.data; this.extends = theme.extends || null; this.isDark = theme?.isDark; this.isPreview = theme.isPreview || false; this.isExtendable = theme.isExtendable || true; } } const THEME_KEY = { userLastPreferTheme: 'user-custom-theme', // localStorage 存储的上一次主题名字 userLastPreferThemeData: 'user-custom-theme-data', // localStorage 存储的上一次主题的变量,用于骨架屏阶段快速恢复主题 currentTheme: 'devuiCurrentTheme', // context(默认window) 当前主题名字 themeCollection: 'devuiThemes', // context(默认window) 存储所有主题集合 styleElementId: 'devuiThemeVariables', // DOM Style Element 的 id标识, 标记css变量声明的片段 transitionStyleElementId: 'devuiThemeColorTransition', // DOM Style Element 的 id标识,标记临时使用的css颜色动画 uiThemeAttributeName: 'ui-theme', // body 和 style元素标记用户数据 themeService: 'devuiThemeService' // 全局window下的theme service实例 }; const devuiLightTheme = new Theme({ id: 'devui-light-theme', name: 'Light Mode', cnName: '浅色主题', data: { 'devui-global-bg': '#f5f5f5', 'devui-glass-morphism-bg': 'rgba(245, 246, 248,0.9)', 'devui-global-bg-normal': '#ffffff', 'devui-base-bg': '#ffffff', 'devui-base-bg-dark': '#333854', 'devui-brand': '#5e7ce0', 'devui-brand-foil': '#859bff', 'devui-brand-hover': '#7693f5', 'devui-brand-active': '#526ecc', 'devui-brand-active-focus': '#344899', 'devui-contrast': '#c7000b', 'devui-text': '#252b3a', 'devui-text-weak': '#575d6c', 'devui-aide-text': '#8a8e99', 'devui-aide-text-hover': '#252b3a', 'devui-aide-text-stress': '#575d6c', 'devui-placeholder': '#8a8e99', 'devui-light-text': '#ffffff', 'devui-dark-text': '#252b3a', 'devui-link': '#526ecc', 'devui-link-active': '#344899', 'devui-link-light': '#96adfa', 'devui-link-light-active': '#beccfa', 'devui-line': '#adb0b8', 'devui-dividing-line': '#dfe1e6', 'devui-block': '#ffffff', 'devui-area': '#f8f8f8', 'devui-danger': '#f66f6a', 'devui-warning': '#fac20a', 'devui-waiting': '#9faad7', 'devui-success': '#50d4ab', 'devui-info': '#5e7ce0', 'devui-initial': '#e9edfa', 'devui-unavailable': '#f5f5f6', 'devui-shadow': 'rgba(0, 0, 0, 0.16)', 'devui-light-shadow': 'rgba(0, 0, 0, 0.08)', 'devui-connected-overlay-shadow': 'rgba(0, 0, 0, 0.16)', 'devui-feedback-overlay-shadow': 'rgba(0, 0, 0, 0.16)', 'devui-feedback-overlay-backdrop': 'rgba(0, 0, 0, 0.3)', 'devui-hover-shadow': 'rgba(0, 0, 0, 0.16)', 'devui-icon-text': '#252b3a', 'devui-icon-bg': '#ffffff', 'devui-icon-fill': '#252b3a', 'devui-icon-fill-weak': '#babbc0', 'devui-icon-fill-hover': '#5e7ce0', 'devui-icon-fill-active': '#5e7ce0', 'devui-icon-fill-active-hover': '#526ecc', 'devui-shape-icon-fill': '#d7d8da', 'devui-shape-icon-fill-active': '#babbc0', 'devui-shape-icon-fill-hover': '#babbc0', 'devui-shape-icon-fill-disabled': '#f5f5f5', 'devui-form-control-line': '#adb0b8', 'devui-form-control-bg': '#ffffff', 'devui-form-control-line-hover': '#575d6c', 'devui-form-control-line-active': '#5e7ce0', 'devui-form-control-interactive-outline': 'rgba(94,124,224,0.08)', 'devui-form-control-line-active-hover': '#344899', 'devui-list-item-active-bg': '#5e7ce0', 'devui-list-item-active-text': '#ffffff', 'devui-list-item-active-hover-bg': '#526ecc', 'devui-list-item-hover-bg': '#f2f5fc', 'devui-list-item-hover-text': '#526ecc', 'devui-list-item-selected-bg': '#e9edfa', 'devui-list-item-strip-bg': '#f2f5fc', 'devui-disabled-bg': '#f5f5f6', 'devui-disabled-line': '#dfe1e6', 'devui-disabled-text': '#adb0b8', 'devui-primary-disabled': '#beccfa', 'devui-icon-fill-active-disabled': '#beccfa', 'devui-label-bg': '#eef0f5', 'devui-connected-overlay-bg': '#ffffff', 'devui-connected-overlay-line': '#526ecc', 'devui-fullscreen-overlay-bg': '#ffffff', 'devui-feedback-overlay-bg': '#464d6e', 'devui-feedback-overlay-text': '#dfe1e6', 'devui-gray-form-control-bg': '#f5f5f5', 'devui-gray-form-control-hover-bg': '#ebebeb', 'devui-nav-expand-bg': '#fbfbfc', 'devui-embed-search-bg': '#f2f5fc', 'devui-embed-search-bg-hover': '#eef0f5', 'devui-float-block-shadow': 'rgba(94, 124, 224, 0.3)', 'devui-highlight-overlay': 'rgba(255, 255, 255, 0.8)', 'devui-range-item-hover-bg': '#e9edfa', 'devui-primary': '#5e7ce0', 'devui-primary-hover': '#7693f5', 'devui-primary-active': '#344899', 'devui-contrast-hover': '#d64a52', 'devui-contrast-active': '#b12220', 'devui-danger-line': '#f66f6a', 'devui-danger-bg': '#ffeeed', 'devui-warning-line': '#fa9841', 'devui-warning-bg': '#fff3e8', 'devui-info-line': '#5e7ce0', 'devui-info-bg': '#f2f5fc', 'devui-success-line': '#50d4ab', 'devui-success-bg': '#edfff9', 'devui-primary-line': '#5e7ce0', 'devui-primary-bg': '#f2f5fc', 'devui-default-line': '#5e7ce0', 'devui-default-bg': '#f3f6f8', 'devui-shadow-length-base': '0 1px 6px 0', 'devui-shadow-length-slide-left': '-2px 0 8px 0', 'devui-shadow-length-slide-right': '2px 0 8px 0', 'devui-shadow-length-connected-overlay': '0 4px 12px 0', 'devui-shadow-length-hover': '0 8px 24px 0', 'devui-shadow-length-feedback-overlay': '0 8px 16px 0', 'devui-shadow-length-fullscreen-overlay': '0 16px 48px 0', 'devui-border-radius': '2px', 'devui-border-radius-feedback': '4px', 'devui-border-radius-card': '6px', 'devui-border-radius-full': '100px', 'devui-font-size': '12px', 'devui-font-size-card-title': '14px', 'devui-font-size-page-title': '16px', 'devui-font-size-modal-title': '18px', 'devui-font-size-price': '20px', 'devui-font-size-data-overview': '24px', 'devui-font-size-icon': '16px', 'devui-font-size-sm': '12px', 'devui-font-size-md': '12px', 'devui-font-size-lg': '14px', 'devui-font-title-weight': 'bold', 'devui-font-content-weight': 'normal', 'devui-line-height-base': '1.5', 'devui-animation-ease-in-smooth': 'cubic-bezier(0.645, 0.045, 0.355, 1)', 'devui-animation-duration-slow': '300ms', 'devui-animation-duration-base': '200ms', 'devui-animation-duration-fast': '100ms', 'devui-animation-ease-in': 'cubic-bezier(0.5, 0, 0.84, 0.25)', 'devui-animation-ease-out': 'cubic-bezier(0.16, 0.75, 0.5, 1)', 'devui-animation-ease-in-out': 'cubic-bezier(0.5, 0.05, 0.5, 0.95)', 'devui-animation-ease-in-out-smooth': 'cubic-bezier(0.645, 0.045, 0.355, 1)', 'devui-animation-linear': 'cubic-bezier(0, 0, 1, 1)', 'devui-z-index-full-page-overlay': '1080', 'devui-z-index-pop-up': '1060', 'devui-z-index-dropdown': '1052', 'devui-z-index-modal': '1050', 'devui-z-index-drawer': '1040', 'devui-z-index-framework': '1000', 'devui-icon-active-color': '#ffffff', 'devui-table-column-shadow-color': 'rgba(37,43,58,0.08)' }, isDark: false, }); const devuiGreenTheme = new Theme({ id: 'avenueui-green-theme', name: 'Green - Light Mode', cnName: '绿色主题', data: { ...devuiLightTheme.data, 'devui-global-bg': '#f5f5f5', 'devui-glass-morphism-bg': 'rgba(245, 246, 248,0.9)', 'devui-brand': '#3DCCA6', 'devui-brand-foil': '#7fdac1', 'devui-brand-hover': '#6DDEBB', 'devui-brand-active': '#07c693', 'devui-brand-active-focus': '#369676', 'devui-link': '#07c693', 'devui-link-active': '#07c693', 'devui-link-light': '#96fac8', 'devui-link-light-active': '#befade', 'devui-info': '#079CCD', 'devui-initial': '#CCCCCC', 'devui-icon-fill-active': '#3DCCA6', 'devui-icon-fill-active-hover': '#07c693', 'devui-form-control-line-active': '#3DCCA6', 'devui-form-control-interactive-outline': 'rgba(61,204,166,0.08)', 'devui-form-control-line-active-hover': '#2EB28A', 'devui-list-item-active-bg': '#3DCCA6', 'devui-list-item-active-hover-bg': '#07c693', 'devui-list-item-hover-bg': '#f3fef9', 'devui-list-item-hover-text': '#07c693', 'devui-list-item-selected-bg': '#f3fef9', 'devui-list-item-strip-bg': '#f3fef9', 'devui-connected-overlay-line': '#07c693', 'devui-embed-search-bg': '#f3fef9', 'devui-float-block-shadow': 'rgba(94, 224, 181, 0.3)', 'devui-primary': '#3DCCA6', 'devui-primary-hover': '#6DDEBB', 'devui-primary-active': '#369676', 'devui-info-line': '#0486b1', 'devui-info-bg': '#e3f0f5', 'devui-success-line': '#50d492', 'devui-success-bg': '#edfff9', 'devui-primary-line': '#3DCCA6', 'devui-primary-bg': '#f3fef9', 'devui-default-line': '#3DCCA6', 'devui-default-bg': '#f3f8f7', 'devui-primary-disabled': '#c5f0e5', 'devui-icon-fill-active-disabled': '#c5f0e5', 'devui-range-item-hover-bg': '#d8f9ea', }, extends: 'devui-light-theme', isDark: false, }); const devuiDarkTheme = new Theme({ id: 'devui-dark-theme', name: 'Dark Mode', cnName: '深色主题', data: { ...devuiLightTheme.data, 'devui-global-bg': '#202124', 'devui-glass-morphism-bg': 'rgba(32,33,36,0.9)', 'devui-global-bg-normal': '#202124', 'devui-base-bg': '#2E2F31', 'devui-base-bg-dark': '#2e2f31', 'devui-brand': '#5e7ce0', 'devui-brand-foil': '#313a61', 'devui-brand-hover': '#425288', 'devui-brand-active': '#526ecc', 'devui-brand-active-focus': '#344899', 'devui-contrast': '#C7000B', 'devui-text': '#E8E8E8', 'devui-text-weak': '#A0A0A0', 'devui-aide-text': '#909090', 'devui-aide-text-hover': '#73788a', 'devui-aide-text-stress': '#A0A0A0', 'devui-placeholder': '#8A8A8A', 'devui-light-text': '#ffffff', 'devui-dark-text': '#252b3a', 'devui-link': '#526ECC', 'devui-link-active': '#344899', 'devui-link-light': '#96adfa', 'devui-link-light-active': '#beccfa', 'devui-line': '#505153', 'devui-dividing-line': '#3D3E40', 'devui-block': '#606061', 'devui-area': '#34363A', 'devui-danger': '#f66f6a', 'devui-warning': '#fac20a', 'devui-waiting': '#5e6580', 'devui-success': '#50d4ab', 'devui-info': '#5e7ce0', 'devui-initial': '#64676e', 'devui-unavailable': '#5b5b5c', 'devui-shadow': 'rgba(0, 0, 0, 0.72)', 'devui-light-shadow': 'rgba(0, 0, 0, 0.3)', 'devui-connected-overlay-shadow': 'rgba(0, 0, 0, 0.42)', 'devui-feedback-overlay-shadow': 'rgba(0, 0, 0, 0.54)', 'devui-feedback-overlay-backdrop': 'rgba(0, 0, 0, 0.72)', 'devui-hover-shadow': 'rgba(0, 0, 0, 0.54)', // 图标 'devui-icon-text': '#E8E8E8', 'devui-icon-bg': '#2E2F31', 'devui-icon-fill': '#909090', 'devui-icon-fill-hover': '#5e7ce0', 'devui-icon-fill-active': '#5e7ce0', 'devui-icon-fill-active-hover': '#526ecc', 'devui-shape-icon-fill': '#5b5d66', 'devui-shape-icon-fill-active': '#868a99', // 表单 'devui-form-control-line': '#505153', 'devui-form-control-bg': '#2E2F31', 'devui-form-control-line-hover': '#909090', 'devui-form-control-line-active': '#5e7ce0', 'devui-form-control-interactive-outline': 'rgba(94,124,224,0.08)', 'devui-form-control-line-active-hover': '#344899', 'devui-list-item-active-bg': '#5e7ce0', 'devui-list-item-active-text': '#ffffff', 'devui-list-item-active-hover-bg': '#526ecc', 'devui-list-item-hover-bg': '#3d3d42', 'devui-list-item-hover-text': '#526ecc', 'devui-list-item-selected-bg': '#454545', 'devui-list-item-strip-bg': '#383838', 'devui-table-expand-area-bg': '#393a3e', // 禁用 'devui-disabled-bg': '#3D3E44', 'devui-disabled-line': '#505153', 'devui-disabled-text': '#7D7D7D', 'devui-primary-disabled': '#2B3458', 'devui-icon-fill-active-disabled': '#2B3458', // 特殊背景色 'devui-label-bg': '#46443F', 'devui-connected-overlay-bg': '#2F2F2F', 'devui-connected-overlay-line': '#526ecc', 'devui-fullscreen-overlay-bg': '#2E2F31', 'devui-feedback-overlay-bg': '#4C4C4C', 'devui-feedback-overlay-text': '#DFE1E6', 'devui-embed-search-bg': '#383838', 'devui-embed-search-bg-hover': '#3D3E40', 'devui-float-block-shadow': 'rgba(94, 124, 224, 0.3)', 'devui-highlight-overlay': 'rgba(255, 255, 255, 0.1)', 'devui-range-item-hover-bg': '#454545', // 按钮 'devui-primary': '#5e7ce0', 'devui-primary-hover': '#425288', 'devui-primary-active': '#344899', 'devui-contrast-hover': '#D64A52', 'devui-contrast-active': '#B12220', // 状态 'devui-danger-line': '#985C5A', 'devui-danger-bg': '#4B3A39', 'devui-warning-line': '#8D6138', 'devui-warning-bg': '#554434', 'devui-info-line': '#546BB7', 'devui-info-bg': '#383D4F', 'devui-success-line': '#5D887D', 'devui-success-bg': '#304642', 'devui-primary-line': '#546BB7', 'devui-primary-bg': '#383D4F', 'devui-default-line': '#5e7ce0', 'devui-default-bg': '#383838', 'devui-icon-active-color': '#ffffff', 'devui-gray-form-control-bg': '#323338', 'devui-gray-form-control-hover-bg': '#393A3E', 'devui-table-column-shadow-color': 'rgba(0,0,0,0.08)' }, extends: 'devui-light-theme', isDark: true, }); const avenueuiGreenDarkTheme = new Theme({ id: 'avenueui-green-dark-theme', name: 'Green - Dark Mode', cnName: '绿色深色主题', data: { ...devuiDarkTheme.data, 'devui-brand': '#3DCCA6', 'devui-brand-foil': '#395e54', 'devui-brand-hover': '#4c9780', 'devui-brand-active': '#07c693', 'devui-brand-active-focus': '#297058', 'devui-link': '#07c693', 'devui-link-active': '#08a57b', 'devui-info': '#046788', 'devui-initial': '#64676e', 'devui-icon-fill-active': '#3DCCA6', 'devui-icon-fill-active-hover': '#07c693', 'devui-form-control-line-active': '#3DCCA6', 'devui-form-control-interactive-outline': 'rgba(61,204,166,0.08)', 'devui-form-control-line-active-hover': '#297058', 'devui-list-item-active-bg': '#3DCCA6', 'devui-list-item-active-hover-bg': '#07c693', 'devui-list-item-hover-text': '#07c693', 'devui-connected-overlay-line': '#07c693', 'devui-embed-search-bg': '#3f4241', 'devui-float-block-shadow': 'rgba(94, 224, 181, 0.3)', 'devui-primary': '#3DCCA6', 'devui-primary-hover': '#6DDEBB', 'devui-primary-active': '#369676', 'devui-info-line': '#035e7c', 'devui-info-bg': '#383c3d', 'devui-primary-line': '#3DCCA6', 'devui-primary-bg': '#3f4241', 'devui-default-line': '#3DCCA6', 'devui-default-bg': '#383838', 'devui-primary-disabled': '#28544B', 'devui-icon-fill-active-disabled': '#28544B', }, extends: 'devui-dark-theme', isDark: true, }); class PrefersColorSchemeMediaQuery { constructor() { this.prefersColorSchemeSubject = new ReplaySubject(1); this.prefersColorSchemeChange = this.prefersColorSchemeSubject.asObservable(); this.handleColorSchemeChange = (value) => { this.prefersColorSchemeSubject.next(value); }; } register() { matchMedia(PrefersColorSchemeMediaQuery.Query.light).addEventListener('change', this.changeToLight); matchMedia(PrefersColorSchemeMediaQuery.Query.dark).addEventListener('change', this.changeToDark); this.prefersColorSchemeSubject.next(this.getInitValue()); } unregister() { matchMedia(PrefersColorSchemeMediaQuery.Query.light).removeEventListener('change', this.changeToLight); matchMedia(PrefersColorSchemeMediaQuery.Query.dark).removeEventListener('change', this.changeToDark); this.prefersColorSchemeSubject.complete(); } getInitValue() { if (typeof window === 'undefined') { return 'light'; } return ((window.matchMedia(PrefersColorSchemeMediaQuery.Query.light).matches && 'light') || (window.matchMedia(PrefersColorSchemeMediaQuery.Query.dark).matches && 'dark') || 'no-preference'); } changeToLight(mql) { if (mql.matches) { this.handleColorSchemeChange('light'); } } changeToDark(mql) { if (mql.matches) { this.handleColorSchemeChange('dark'); } } } /* eslint-disable-next-line @typescript-eslint/no-namespace */ (function (PrefersColorSchemeMediaQuery) { let Query; (function (Query) { Query["light"] = "screen and (prefers-color-scheme: light)"; Query["dark"] = "screen and (prefers-color-scheme: dark)"; Query["noPreferences"] = "screen and (prefers-color-scheme: light)"; })(Query = PrefersColorSchemeMediaQuery.Query || (PrefersColorSchemeMediaQuery.Query = {})); })(PrefersColorSchemeMediaQuery || (PrefersColorSchemeMediaQuery = {})); class EventBus { constructor() { this.eventBusCore = []; } areFuncEqual(a, b) { return a.toString() === b.toString(); } isKeyValueObjInArr(arr, key, val) { const filteredArr = arr.filter((entry) => { return entry[key] === val; }); return filteredArr.length > 0; } removeFuncInFuncArr(arr, fn) { for (let z = 0; z < arr.length; z++) { if (this.areFuncEqual(arr[z], fn)) { arr.splice(z, 1); } } return arr; } getKeyValueObjInArr(arr, key, val) { const filteredArr = arr.filter((entry) => { return entry[key] === val; }); return filteredArr[0]; } addEvent(eventName, eventFunc) { if (!this.isKeyValueObjInArr(this.eventBusCore, 'eventName', eventName)) { this.eventBusCore.push({ eventName: eventName, eventFuncArr: [eventFunc] }); } else { this.eventBusCore = this.eventBusCore.map((event) => { if (event.eventName === eventName) { event.eventFuncArr.push(eventFunc); } return event; }); } } add(...rest) { const eventName = rest[0]; const callbacks = rest[1]; if (!eventName) { return; } if (typeof callbacks === 'function') { for (let i = 1; i < rest.length; i++) { this.addEvent(eventName, rest[i]); } } if (typeof callbacks === 'object' && callbacks.forEach) { callbacks.forEach((fn) => { this.addEvent(eventName, fn); }); } } remove(...rest) { const eventName = rest[0]; const callbacks = rest[1]; if (!eventName) { return; } for (let i = 0; i < this.eventBusCore.length; i++) { if (this.eventBusCore[i].eventName === eventName) { if (rest.length === 1) { return this.eventBusCore.splice(i, 1); } const removedEvent = this.eventBusCore.splice(i, 1)[0]; if (typeof callbacks === 'function') { for (let k = 1; k < rest.length; k++) { removedEvent.eventFuncArr = this.removeFuncInFuncArr(removedEvent.eventFuncArr, rest[k]); } } if (typeof callbacks === 'object' && callbacks.length) { for (let x = 0; x < callbacks.length; x++) { removedEvent.eventFuncArr = this.removeFuncInFuncArr(removedEvent.eventFuncArr, callbacks[x]); } } this.eventBusCore.push(removedEvent); } } } trigger(eventName, data) { const event = this.getKeyValueObjInArr(this.eventBusCore, 'eventName', eventName); if (event) { (event.eventFuncArr || []).forEach((fn) => { fn.apply(this, data); }); } } } class ContextService { getDataFromNameSpace(nameSpace) { if (typeof window === 'undefined') { return null; } return window[nameSpace]; } setDataFromNameSpace(nameSpace, value) { if (typeof window === 'undefined') { return; } window[nameSpace] = value; } } class StorageService { tryGetLocalStorage(key) { if (typeof window === 'undefined') { return null; } return window.localStorage.getItem(key); } trySetLocalStorage(key, value) { if (typeof window === 'undefined') { return; } try { window.localStorage.setItem(key, value); } catch (error) { console.error(error); } } } const THEME_CHANGE_TIME = 400; const THEME_CHANGE_DELAY = 100; /** * 负责CSS变量主题的装卸,主题元数据转换成主题数据 */ class ThemeService { set appendClasses(classes) { if (this._appendedClasses) { this.removeAppendedClass(this._appendedClasses); } if (classes) { this.addAppendClass(classes); } this._appendedClasses = classes; } get appendClasses() { return this._appendedClasses; } constructor(eventBus, storage, context) { this.eventBus = eventBus === undefined ? new EventBus() : eventBus; this.storage = storage === undefined ? new StorageService() : storage; this.context = context === undefined ? new ContextService() : context; } initializeTheme(specificThemeId, allowDynamicTheme) { const themeId = specificThemeId || this.storage.tryGetLocalStorage(THEME_KEY.userLastPreferTheme) || this.context.getDataFromNameSpace(THEME_KEY.currentTheme); let theme; if (themeId) { const themes = this.context.getDataFromNameSpace(THEME_KEY.themeCollection); if (themes && Object.keys(themes).length > 0) { theme = themes[themeId]; if (!theme) { const key = Object.keys(themes).find(t => themes[t].id === themeId); theme = themes[key]; } } } this.currentTheme = theme || { id: 'empty-theme', name: '', data: {}, isDark: false }; this.createColorTransition(); if (!theme && allowDynamicTheme) { document.body.setAttribute(THEME_KEY.uiThemeAttributeName, 'empty-theme'); return; } this.applyTheme(this.currentTheme); } formatCSSVariables(themeData) { return Object.keys(themeData).map(cssVar => ('--' + cssVar + ':' + themeData[cssVar])).join(';'); } applyTheme(theme) { if (typeof window === 'undefined' || typeof document === 'undefined') { return; } if (theme.isDark === this.currentTheme.isDark) { this.addColorTransition(); this.renderTheme(theme); // 通知外部主题变更 this.notify(theme, 'themeChanged'); setTimeout(() => { this.removeColorTransition(); }, THEME_CHANGE_TIME + THEME_CHANGE_DELAY); } else { this.themeHidden(); setTimeout(() => { this.removeColorTransition(); this.renderTheme(theme); // 通知外部主题变更 this.notify(theme, 'themeChanged'); this.themeShow(); setTimeout(() => { this.removeColorTransition(); }, THEME_CHANGE_TIME + THEME_CHANGE_DELAY); }, THEME_CHANGE_TIME); } } renderTheme(theme) { this.currentTheme = theme; if (!this.contentElement) { const styleElement = document.getElementById(THEME_KEY.styleElementId); if (styleElement) { this.contentElement = styleElement; } else { this.contentElement = document.createElement('style'); this.contentElement.id = THEME_KEY.styleElementId; document.head.appendChild(this.contentElement); } } this.contentElement.innerText = ':root { ' + this.formatCSSVariables(theme.data) + ' }'; this.contentElement.setAttribute(THEME_KEY.uiThemeAttributeName, this.currentTheme.id); document.body.setAttribute(THEME_KEY.uiThemeAttributeName, this.currentTheme.id); // 用于挂载额外变量和类名 this.applyExtraData(); this.saveCustomTheme(this.currentTheme); } saveCustomTheme(customTheme) { this.storage.trySetLocalStorage(THEME_KEY.userLastPreferTheme, customTheme.id); this.storage.trySetLocalStorage(THEME_KEY.userLastPreferThemeData, JSON.stringify(customTheme.data)); this.context.setDataFromNameSpace(THEME_KEY.currentTheme, customTheme.id); } notify(theme, eventType) { if (!this.eventBus) { return; } this.eventBus.trigger(eventType, theme); } setEventBus(eb) { this.eventBus = eb; } addAppendClass(classNames) { if (typeof document === 'undefined') { return; } document.body.classList.add(...classNames); } removeAppendedClass(classNames) { if (typeof document === 'undefined') { return; } document.body.classList.remove(...classNames); } setExtraData(data, apply = false) { this.extraData = data; if (apply) { this.applyExtraData(); } } applyExtraData() { const theme = this.currentTheme; if (this.extraData && this.extraData[theme.id] && this.extraData[theme.id].cssVariables) { this.contentElement.innerText = ':root { ' + this.formatCSSVariables(theme.data) + ' }' + ':root { ' + this.formatCSSVariables(this.extraData[theme.id].cssVariables) + ' }'; } if (this.extraData && this.extraData[theme.id] && this.extraData[theme.id].appendClasses) { this.appendClasses = this.extraData[theme.id].appendClasses; } else { this.appendClasses = undefined; } } unloadTheme() { if (typeof document === 'undefined') { return null; } if (this.contentElement && document.contains(this.contentElement)) { this.contentElement.parentElement.removeChild(this.contentElement); } if (this.appendClasses) { this.appendClasses = undefined; } } registerMediaQuery() { if (!this.mediaQuery) { this.mediaQuery = new PrefersColorSchemeMediaQuery(); } this.mediaQuery.register(); } unregisterMediaQuery() { if (!this.mediaQuery) { return; } this.mediaQuery.unregister(); this.mediaQuery = undefined; } createColorTransition() { if (typeof document === 'undefined') { return; } this.colorTransitionElement = document.createElement('style'); this.darkChangeHiddenElement = document.createElement('style'); this.darkChangeShowElement = document.createElement('style'); ; this.colorTransitionElement.id = THEME_KEY.transitionStyleElementId; this.darkChangeHiddenElement.id = THEME_KEY.transitionStyleElementId; this.darkChangeShowElement.id = THEME_KEY.transitionStyleElementId; this.colorTransitionElement.innerText = ` * { transition: background .3s ease-out, background-color .3s ease-out, border .3s ease-out, border-color .3s ease-out, box-shadow .3s ease-out, box-shadow-color .3s ease-out} `; this.darkChangeHiddenElement.innerText = ` * { transition: background .3s ease-out, background-color .3s ease-out, border .3s ease-out, border-color .3s ease-out, box-shadow .3s ease-out, box-shadow-color .3s ease-out } body { animation-duration: 400ms; animation-name: hidden; animation-iteration-count: 1; } @keyframes hidden { from { opacity: 1; } to { opacity: 0.1; } } `; this.darkChangeShowElement.innerText = ` body { animation-duration: 400ms; animation-name: show; animation-iteration-count: 1; } @keyframes show { from { opacity: 0.1; } to { opacity: 1; } } `; } themeHidden() { if (typeof document === 'undefined') { return; } document.head.appendChild(this.darkChangeHiddenElement); } themeShow() { if (typeof document === 'undefined') { return; } document.head.appendChild(this.darkChangeShowElement); } addColorTransition() { if (typeof document === 'undefined') { return; } document.head.appendChild(this.colorTransitionElement); } removeColorTransition() { if (this.colorTransitionElement.parentElement) { this.colorTransitionElement.parentElement.removeChild(this.colorTransitionElement); } if (this.darkChangeShowElement.parentElement) { this.darkChangeShowElement.parentElement.removeChild(this.darkChangeShowElement); } if (this.darkChangeHiddenElement.parentElement) { this.darkChangeHiddenElement.parentElement.removeChild(this.darkChangeHiddenElement); } } } /** * usage * main.ts ```ts import { ThemeServiceInit } from 'ng-devui/theme'; ThemeServiceInit(); ``` * */ function ThemeServiceInit(themes, defaultThemeName, extraData, ieSupport = false, // TODO:css-var-ponyflll 仍有一些问题待定位 allowDynamicTheme = false) { if (typeof window === 'undefined' || typeof document === 'undefined') { return null; } window[THEME_KEY.themeCollection] = themes || { 'devui-light-theme': devuiLightTheme, 'devui-dark-theme': devuiDarkTheme, }; window[THEME_KEY.currentTheme] = defaultThemeName || 'devui-light-theme'; const eventBus = window.globalEventBus || new EventBus(); // window.globalEventBus 为 框架的事件总线 const themeService = new ThemeService(eventBus); window[THEME_KEY.themeService] = themeService; themeService.setExtraData(extraData || { 'devui-dark-theme': { appendClasses: ['dark-mode'], }, }); const currentTheme = window?.localStorage.getItem(THEME_KEY.userLastPreferTheme) || defaultThemeName; themeService.initializeTheme(currentTheme, allowDynamicTheme); if (ieSupport) { ieSupportCssVar(); } return themeService; } function ThemeServiceFollowSystemOn(themeConfig) { if (typeof window === 'undefined' || typeof document === 'undefined') { return null; } const themeService = window[THEME_KEY.themeService]; themeService.registerMediaQuery(); return themeService.mediaQuery.prefersColorSchemeChange.subscribe((value) => { if (value === 'dark') { themeService.applyTheme(window[THEME_KEY.themeCollection][(themeConfig && themeConfig.darkThemeName) || 'devui-dark-theme']); } else { themeService.applyTheme(window[THEME_KEY.themeCollection][(themeConfig && themeConfig.lightThemeName) || 'devui-light-theme']); } }); } function ThemeServiceFollowSystemOff(sub) { if (typeof window === 'undefined' || typeof document === 'undefined') { return; } if (sub) { sub.unsubscribe(); } const themeService = window[THEME_KEY.themeService]; themeService.unregisterMediaQuery(); } function ieSupportCssVar() { if (typeof window === 'undefined' || typeof document === 'undefined') { return; } const isNativeSupport = (window.CSS && CSS.supports && CSS.supports('(--a: 0)')) || false; if (isNativeSupport) { return; } cssVars({ watch: true, silent: true }); const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { cssVars({ watch: false, silent: true }); cssVars({ watch: true, silent: true }); }); }); const config = { attributes: true, attributeFilter: [THEME_KEY.uiThemeAttributeName] }; observer.observe(document.querySelector(`#${THEME_KEY.styleElementId}`), config); } // TODO: management should handle add / remove theme from theme collection. // TODO: move global variables(window.xxxx) to single namespace /** * Generated bundle index. Do not edit. */ export { ContextService, EventBus, StorageService, Theme, ThemeService, ThemeServiceFollowSystemOff, ThemeServiceFollowSystemOn, ThemeServiceInit, avenueuiGreenDarkTheme, devuiDarkTheme, devuiGreenTheme, devuiLightTheme, ieSupportCssVar }; //# sourceMappingURL=ng-devui-theme.mjs.map