ng-devui
Version:
DevUI components based on Angular
898 lines (884 loc) • 34.8 kB
JavaScript
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;
}
hidden {
from {
opacity: 1;
}
to {
opacity: 0.1;
}
}
`;
this.darkChangeShowElement.innerText = `
body {
animation-duration: 400ms;
animation-name: show;
animation-iteration-count: 1;
}
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