line_push/node_modules/vuetify/lib/services/theme/index.js
2022-07-17 13:16:16 +08:00

258 lines
6.2 KiB
JavaScript

/* eslint-disable no-multi-spaces */
// Extensions
import { Service } from '../service'; // Utilities
import * as ThemeUtils from './utils';
import { getNestedValue } from '../../util/helpers'; // Types
import Vue from 'vue';
export class Theme extends Service {
constructor(preset) {
super();
this.disabled = false;
this.isDark = null;
this.vueInstance = null;
this.vueMeta = null;
const {
dark,
disable,
options,
themes
} = preset[Theme.property];
this.dark = Boolean(dark);
this.defaults = this.themes = themes;
this.options = options;
if (disable) {
this.disabled = true;
return;
}
this.themes = {
dark: this.fillVariant(themes.dark, true),
light: this.fillVariant(themes.light, false)
};
} // When setting css, check for element
// and apply new values
set css(val) {
if (this.vueMeta) {
if (this.isVueMeta23) {
this.applyVueMeta23();
}
return;
}
this.checkOrCreateStyleElement() && (this.styleEl.innerHTML = val);
}
set dark(val) {
const oldDark = this.isDark;
this.isDark = val; // Only apply theme after dark
// has already been set before
oldDark != null && this.applyTheme();
}
get dark() {
return Boolean(this.isDark);
} // Apply current theme default
// only called on client side
applyTheme() {
if (this.disabled) return this.clearCss();
this.css = this.generatedStyles;
}
clearCss() {
this.css = '';
} // Initialize theme for SSR and SPA
// Attach to ssrContext head or
// apply new theme to document
init(root, ssrContext) {
if (this.disabled) return;
/* istanbul ignore else */
if (root.$meta) {
this.initVueMeta(root);
} else if (ssrContext) {
this.initSSR(ssrContext);
}
this.initTheme();
} // Allows for you to set target theme
setTheme(theme, value) {
this.themes[theme] = Object.assign(this.themes[theme], value);
this.applyTheme();
} // Reset theme defaults
resetThemes() {
this.themes.light = Object.assign({}, this.defaults.light);
this.themes.dark = Object.assign({}, this.defaults.dark);
this.applyTheme();
} // Check for existence of style element
checkOrCreateStyleElement() {
this.styleEl = document.getElementById('vuetify-theme-stylesheet');
/* istanbul ignore next */
if (this.styleEl) return true;
this.genStyleElement(); // If doesn't have it, create it
return Boolean(this.styleEl);
}
fillVariant(theme = {}, dark) {
const defaultTheme = this.themes[dark ? 'dark' : 'light'];
return Object.assign({}, defaultTheme, theme);
} // Generate the style element
// if applicable
genStyleElement() {
/* istanbul ignore if */
if (typeof document === 'undefined') return;
/* istanbul ignore next */
this.styleEl = document.createElement('style');
this.styleEl.type = 'text/css';
this.styleEl.id = 'vuetify-theme-stylesheet';
if (this.options.cspNonce) {
this.styleEl.setAttribute('nonce', this.options.cspNonce);
}
document.head.appendChild(this.styleEl);
}
initVueMeta(root) {
this.vueMeta = root.$meta();
if (this.isVueMeta23) {
// vue-meta needs to apply after mounted()
root.$nextTick(() => {
this.applyVueMeta23();
});
return;
}
const metaKeyName = typeof this.vueMeta.getOptions === 'function' ? this.vueMeta.getOptions().keyName : 'metaInfo';
const metaInfo = root.$options[metaKeyName] || {};
root.$options[metaKeyName] = () => {
metaInfo.style = metaInfo.style || [];
const vuetifyStylesheet = metaInfo.style.find(s => s.id === 'vuetify-theme-stylesheet');
if (!vuetifyStylesheet) {
metaInfo.style.push({
cssText: this.generatedStyles,
type: 'text/css',
id: 'vuetify-theme-stylesheet',
nonce: (this.options || {}).cspNonce
});
} else {
vuetifyStylesheet.cssText = this.generatedStyles;
}
return metaInfo;
};
}
applyVueMeta23() {
const {
set
} = this.vueMeta.addApp('vuetify');
set({
style: [{
cssText: this.generatedStyles,
type: 'text/css',
id: 'vuetify-theme-stylesheet',
nonce: this.options.cspNonce
}]
});
}
initSSR(ssrContext) {
// SSR
const nonce = this.options.cspNonce ? ` nonce="${this.options.cspNonce}"` : '';
ssrContext.head = ssrContext.head || '';
ssrContext.head += `<style type="text/css" id="vuetify-theme-stylesheet"${nonce}>${this.generatedStyles}</style>`;
}
initTheme() {
// Only watch for reactivity on client side
if (typeof document === 'undefined') return; // If we get here somehow, ensure
// existing instance is removed
if (this.vueInstance) this.vueInstance.$destroy(); // Use Vue instance to track reactivity
// TODO: Update to use RFC if merged
// https://github.com/vuejs/rfcs/blob/advanced-reactivity-api/active-rfcs/0000-advanced-reactivity-api.md
this.vueInstance = new Vue({
data: {
themes: this.themes
},
watch: {
themes: {
immediate: true,
deep: true,
handler: () => this.applyTheme()
}
}
});
}
get currentTheme() {
const target = this.dark ? 'dark' : 'light';
return this.themes[target];
}
get generatedStyles() {
const theme = this.parsedTheme;
/* istanbul ignore next */
const options = this.options || {};
let css;
if (options.themeCache != null) {
css = options.themeCache.get(theme);
/* istanbul ignore if */
if (css != null) return css;
}
css = ThemeUtils.genStyles(theme, options.customProperties);
if (options.minifyTheme != null) {
css = options.minifyTheme(css);
}
if (options.themeCache != null) {
options.themeCache.set(theme, css);
}
return css;
}
get parsedTheme() {
return ThemeUtils.parse(this.currentTheme || {}, undefined, getNestedValue(this.options, ['variations'], true));
} // Is using v2.3 of vue-meta
// https://github.com/nuxt/vue-meta/releases/tag/v2.3.0
get isVueMeta23() {
return typeof this.vueMeta.addApp === 'function';
}
}
Theme.property = 'theme';
//# sourceMappingURL=index.js.map