import style from './_theme-settings/_theme-settings.scss' import markup from './_theme-settings/_theme-settings.html' import themeItemMarkup from './_theme-settings/_theme-settings-theme-item.html' import bgItemMarkup from './_theme-settings/_theme-settings-bg-item.html' const DEFAULT_THEME = 1 const CSS_FILENAME_PATTERN = '%name%.css' const CONTROLS = [ 'rtl', 'style', 'layoutPosition', 'layoutNavbarFixed', 'layoutFooterFixed', 'layoutReversed', 'navbarBg', 'sidenavBg', 'footerBg', 'themes' ] const STYLES = [ 'light', 'material', 'dark' ] const DEFAULT_NAVBAR_BG = 'navbar-theme' const DEFAULT_SIDENAV_BG = 'sidenav-theme' const DEFAULT_FOOTER_BG = 'footer-theme' class ThemeSettings { constructor({ cssPath, themesPath, cssFilenamePattern, controls, styles, sidenavBgs, defaultSidenavBg, navbarBgs, defaultNavbarBg, footerBgs, defaultFooterBg, availableThemes, defaultTheme, pathResolver, onSettingsChange, lang }) { if (this._ssr) return if (!window.layoutHelpers) throw new Error('window.layoutHelpers required.') this.settings = {} this.settings.cssPath = cssPath this.settings.themesPath = themesPath this.settings.cssFilenamePattern = cssFilenamePattern || CSS_FILENAME_PATTERN this.settings.navbarBgs = navbarBgs || ThemeSettings.NAVBAR_BGS this.settings.defaultNavbarBg = defaultNavbarBg || DEFAULT_NAVBAR_BG this.settings.sidenavBgs = sidenavBgs || ThemeSettings.SIDENAV_BGS this.settings.defaultSidenavBg = defaultSidenavBg || DEFAULT_SIDENAV_BG this.settings.footerBgs = footerBgs || ThemeSettings.FOOTER_BGS this.settings.defaultFooterBg = defaultFooterBg || DEFAULT_FOOTER_BG this.settings.availableThemes = availableThemes || ThemeSettings.AVAILABLE_THEMES this.settings.defaultTheme = this._getDefaultTheme(typeof defaultTheme !== 'undefined' ? defaultTheme : DEFAULT_THEME) this.settings.controls = controls || CONTROLS this.settings.styles = styles || STYLES this.settings.lang = lang || 'en' this.pathResolver = pathResolver || (p => p) if (this.settings.styles.length < 2) { const i = this.settings.controls.indexOf('style') if (i !== -1) { this.settings.controls = this.settings.controls.slice(0, i).concat(this.settings.controls.slice(i + 1)) } } this.settings.onSettingsChange = typeof onSettingsChange === 'function' ? onSettingsChange : () => {} this._loadSettings() this._listeners = [] this._controls = {} this._initDirection() this._initStyle() this._initTheme() this.setLayoutPosition(this.settings.layoutPosition, false) this.setLayoutNavbarFixed(this.settings.layoutNavbarFixed, false) this.setLayoutFooterFixed(this.settings.layoutFooterFixed, false) this.setLayoutReversed(this.settings.layoutReversed, false) this._setup() this._waitForNavs() } setRtl(rtl) { if (!this._hasControls('rtl')) return this._setSetting('Rtl', String(rtl)) window.location.reload() } setStyle(style) { if (!this._hasControls('style')) return this._setSetting('Style', ['material', 'dark'].indexOf(style) === -1 ? 'light' : style) window.location.reload() } isLightStyle() { return document.documentElement.classList.contains('light-style') } isMaterialStyle() { return document.documentElement.classList.contains('material-style') } isDarkStyle() { return document.documentElement.classList.contains('dark-style') } setTheme(themeName, updateStorage = true, cb = null) { if (!this._hasControls('themes')) return const theme = this._getThemeByName(themeName) if (!theme) return this.settings.theme = theme if (updateStorage) this._setSetting('Theme', themeName) const themeUrl = this.pathResolver( this.settings.themesPath + this.settings.cssFilenamePattern.replace( '%name%', themeName + ( this.settings.style !== 'light' ? `-${this.settings.style}` : '' ) ) ) this._loadStylesheets( { [themeUrl]: document.querySelector('.theme-settings-theme-css') }, cb || (() => {}) ) if (updateStorage) this.settings.onSettingsChange.call(this, this.settings) } setLayoutPosition(pos, updateStorage = true) { if (!this._hasControls('layoutPosition')) return if (pos !== 'static' && pos !== 'static-offcanvas' && pos !== 'fixed' && pos !== 'fixed-offcanvas') return this.settings.layoutPosition = pos if (updateStorage) this._setSetting('LayoutPosition', pos) window.layoutHelpers.setPosition( pos === 'fixed' || pos === 'fixed-offcanvas', pos === 'static-offcanvas' || pos === 'fixed-offcanvas' ) if (updateStorage) this.settings.onSettingsChange.call(this, this.settings) } setLayoutNavbarFixed(fixed, updateStorage = true) { if (!this._hasControls('layoutNavbarFixed')) return this.settings.layoutNavbarFixed = fixed if (updateStorage) this._setSetting('FixedNavbar', fixed) window.layoutHelpers.setNavbarFixed(fixed) if (updateStorage) this.settings.onSettingsChange.call(this, this.settings) } setLayoutFooterFixed(fixed, updateStorage = true) { if (!this._hasControls('layoutFooterFixed')) return this.settings.layoutFooterFixed = fixed if (updateStorage) this._setSetting('FixedFooter', fixed) window.layoutHelpers.setFooterFixed(fixed) if (updateStorage) this.settings.onSettingsChange.call(this, this.settings) } setLayoutReversed(reversed, updateStorage = true) { if (!this._hasControls('layoutReversed')) return this.settings.layoutReversed = reversed if (updateStorage) this._setSetting('LayoutReversed', reversed) window.layoutHelpers.setReversed(reversed) if (updateStorage) this.settings.onSettingsChange.call(this, this.settings) } setNavbarBg(bg, updateStorage = true, _container = document) { if (!this._hasControls('navbarBg')) return if (this.settings.navbarBgs.indexOf(bg) === -1) return this.settings.navbarBg = bg if (updateStorage) this._setSetting('NavbarBg', bg) const navbar = _container.querySelector('.layout-navbar.navbar, .layout-navbar .navbar') if (!navbar) return navbar.className = navbar.className.replace(/^bg\-[^ ]+| bg\-[^ ]+/ig, '') navbar.classList.remove('navbar-light') navbar.classList.remove('navbar-dark') const classes = bg.split(' ') navbar.classList.add(`bg-${classes[0]}`) for (let i = 1, l = classes.length; i < l; i++) navbar.classList.add(classes[i]) if (updateStorage) this.settings.onSettingsChange.call(this, this.settings) } setSidenavBg(bg, updateStorage = true, _container = document) { if (!this._hasControls('sidenavBg')) return if (this.settings.sidenavBgs.indexOf(bg) === -1) return this.settings.sidenavBg = bg if (updateStorage) this._setSetting('SidenavBg', bg) const sidenav = _container.querySelector('.layout-sidenav.sidenav, .layout-sidenav .sidenav, .layout-sidenav-horizontal.sidenav, .layout-sidenav-horizontal .sidenav') if (!sidenav) return sidenav.className = sidenav.className.replace(/^bg\-[^ ]+| bg\-[^ ]+/ig, '') sidenav.classList.remove('sidenav-light') sidenav.classList.remove('sidenav-dark') let classes = bg.split(' ') if (sidenav.classList.contains('sidenav-horizontal')) { classes = classes.join(' ').replace(' sidenav-dark', '').replace(' sidenav-light', '').split(' ') classes[0] = classes[0].replace(/-darke?r?$/, '') } sidenav.classList.add(`bg-${classes[0]}`) for (let i = 1, l = classes.length; i < l; i++) sidenav.classList.add(classes[i]) if (updateStorage) this.settings.onSettingsChange.call(this, this.settings) } setFooterBg(bg, updateStorage = true, _container = document) { if (!this._hasControls('footerBg')) return if (this.settings.footerBgs.indexOf(bg) === -1) return this.settings.footerBg = bg if (updateStorage) this._setSetting('FooterBg', bg) const footer = _container.querySelector('.layout-footer.footer, .layout-footer .footer') if (!footer) return footer.className = footer.className.replace(/^bg\-[^ ]+| bg\-[^ ]+/ig, '') footer.classList.remove('footer-light') footer.classList.remove('footer-dark') const classes = bg.split(' ') footer.classList.add(`bg-${classes[0]}`) for (let i = 1, l = classes.length; i < l; i++) footer.classList.add(classes[i]) if (updateStorage) this.settings.onSettingsChange.call(this, this.settings) } setLang(lang, force = false) { if (lang === this.settings.lang && !force) return if (!ThemeSettings.LANGUAGES[lang]) throw new Error(`Language "${lang}" not found!`) const t = ThemeSettings.LANGUAGES[lang] ;[ 'panel_header', 'rtl_switcher', 'style_switcher_light', 'style_switcher_material', 'style_switcher_dark', 'layout_header', 'layout_static', 'layout_offcanvas', 'layout_fixed', 'layout_fixed_offcanvas', 'layout_navbar_swicher', 'layout_footer_swicher', 'layout_reversed_swicher', 'navbar_bg_header', 'sidenav_bg_header', 'footer_bg_header', 'theme_header' ].forEach(key => { const el = this.container.querySelector(`.theme-settings-t-${key}`) el && (el.textContent = t[key]) }) const tt = t.themes || {} const themes = this.container.querySelectorAll('.theme-settings-theme-item') || [] for (let i = 0, l = themes.length; i < l; i++) { const themeName = themes[i].querySelector('input[type="radio"]').value themes[i].querySelector('.theme-settings-theme-name').textContent = tt[themeName] || this._getThemeByName(themeName).title } this.settings.lang = lang } update() { if (this._ssr) return const hasNavbar = !!document.querySelector('.layout-navbar') const hasSidenav = !!document.querySelector('.layout-sidenav') const hasHorizontalSidenav = !!document.querySelector('.layout-sidenav-horizontal.sidenav, .layout-sidenav-horizontal .sidenav') const isLayout1 = !!document.querySelector('.layout-wrapper.layout-1') const hasFooter = !!document.querySelector('.layout-footer') if (this._controls.layoutReversed) { if (!hasSidenav) { this._controls.layoutReversed.setAttribute('disabled', 'disabled') this._controls.layoutReversedW.classList.add('disabled') } else { this._controls.layoutReversed.removeAttribute('disabled') this._controls.layoutReversedW.classList.remove('disabled') } } if (this._controls.layoutNavbarFixed) { if (!hasNavbar) { this._controls.layoutNavbarFixed.setAttribute('disabled', 'disabled') this._controls.layoutNavbarFixedW.classList.add('disabled') } else { this._controls.layoutNavbarFixed.removeAttribute('disabled') this._controls.layoutNavbarFixedW.classList.remove('disabled') } } if (this._controls.layoutFooterFixed) { if (!hasFooter) { this._controls.layoutFooterFixed.setAttribute('disabled', 'disabled') this._controls.layoutFooterFixedW.classList.add('disabled') } else { this._controls.layoutFooterFixed.removeAttribute('disabled') this._controls.layoutFooterFixedW.classList.remove('disabled') } } if (this._controls.layoutPosition) { if (!hasSidenav) { this._controls.layoutPosition.querySelector('[value="static-offcanvas"]').setAttribute('disabled', 'disabled') this._controls.layoutPosition.querySelector('[value="fixed-offcanvas"]').setAttribute('disabled', 'disabled') } else { this._controls.layoutPosition.querySelector('[value="static-offcanvas"]').removeAttribute('disabled') this._controls.layoutPosition.querySelector('[value="fixed-offcanvas"]').removeAttribute('disabled') } if ((!hasNavbar && !hasSidenav) || (!hasSidenav && !isLayout1)) { this._controls.layoutPosition.setAttribute('disabled', 'disabled') } else { this._controls.layoutPosition.removeAttribute('disabled') } } if (this._controls.navbarBgWInner) { if (!hasNavbar) { this._controls.navbarBgWInner.setAttribute('disabled', 'disabled') } else { this._controls.navbarBgWInner.removeAttribute('disabled') } } if (this._controls.sidenavBgWInner) { const items = Array.prototype.slice.call(document.querySelectorAll('.theme-settings-sidenavBg-inner .theme-settings-bg-item')) if (!hasSidenav && !hasHorizontalSidenav) { items.forEach(item => { item.classList.add('disabled') item.querySelector('input').setAttribute('disabled', 'disabled') }) } else { items.forEach(item => { item.classList.remove('disabled') item.querySelector('input').removeAttribute('disabled') }) if (hasHorizontalSidenav) items.forEach(item => { if (!/-darke?r?/.test(item.className) || /bg-dark/.test(item.className)) return item.classList.add('disabled') item.querySelector('input').setAttribute('disabled', 'disabled') }) } } if (this._controls.footerBgWInner) { if (!hasFooter) { this._controls.footerBgWInner.setAttribute('disabled', 'disabled') } else { this._controls.footerBgWInner.removeAttribute('disabled') } } } updateNavbarBg(_container = document) { this.setNavbarBg(this.settings.navbarBg, false, _container) } updateSidenavBg(_container = document) { this.setSidenavBg(this.settings.sidenavBg, false, _container) } updateFooterBg(_container = document) { this.setFooterBg(this.settings.footerBg, false, _container) } clearLocalStorage() { if (this._ssr) return this._setSetting('Theme', '') this._setSetting('Rtl', '') this._setSetting('Style', '') this._setSetting('LayoutReversed', '') this._setSetting('FixedNavbar', '') this._setSetting('FixedFooter', '') this._setSetting('LayoutPosition', '') this._setSetting('NavbarBg', '') this._setSetting('SidenavBg', '') this._setSetting('FooterBg', '') } destroy() { if (this._ssr) return this._cleanup() this.settings = null this.container.parentNode.removeChild(this.container) this.container = null } _loadSettings() { const cl = document.documentElement.classList const rtl = this._getSetting('Rtl') const style = this._getSetting('Style') const reversed = this._getSetting('LayoutReversed') const fixedNavbar = this._getSetting('FixedNavbar') const fixedFooter = this._getSetting('FixedFooter') const navbarBg = this._getSetting('NavbarBg') const sidenavBg = this._getSetting('SidenavBg') const footerBg = this._getSetting('FooterBg') const lPosition = this._getSetting('LayoutPosition') let position if (lPosition !== '' && ['static', 'static-offcanvas', 'fixed', 'fixed-offcanvas'].indexOf(lPosition) !== -1) { position = lPosition } else if (cl.contains('layout-offcanvas')) { position = 'static-offcanvas' } else if (cl.contains('layout-fixed')) { position = 'fixed' } else if (cl.contains('layout-fixed-offcanvas')) { position = 'fixed-offcanvas' } else { position = 'static' } // Set settings this.settings.rtl = rtl !== '' ? rtl === 'true' : document.documentElement.getAttribute('dir') === 'rtl' this.settings.style = this.settings.styles.indexOf(style) !== -1 ? style : ( cl.contains('material-style') ? 'material' : ( cl.contains('dark-style') ? 'dark' : 'light' ) ) if (this.settings.styles.indexOf(this.settings.style) === -1) { this.settings.style = this.settings.styles[0] } this.settings.layoutPosition = position this.settings.layoutReversed = reversed !== '' ? reversed === 'true' : cl.contains('layout-reversed') this.settings.layoutNavbarFixed = fixedNavbar !== '' ? fixedNavbar === 'true' : cl.contains('layout-navbar-fixed') this.settings.layoutFooterFixed = fixedFooter !== '' ? fixedFooter === 'true' : cl.contains('layout-footer-fixed') this.settings.navbarBg = this.settings.navbarBgs.indexOf(navbarBg) !== -1 ? navbarBg : this.settings.defaultNavbarBg this.settings.sidenavBg = this.settings.sidenavBgs.indexOf(sidenavBg) !== -1 ? sidenavBg : this.settings.defaultSidenavBg this.settings.footerBg = this.settings.footerBgs.indexOf(footerBg) !== -1 ? footerBg : this.settings.defaultFooterBg this.settings.theme = this._getThemeByName(this._getSetting('Theme'), true) // Filter options depending on available controls if (!this._hasControls('rtl')) this.settings.rtl = document.documentElement.getAttribute('dir') === 'rtl' if (!this._hasControls('style')) { this.settings.style = cl.contains('material-style') ? 'material' : ( cl.contains('dark-style') ? 'dark' : 'light' ) } if (!this._hasControls('layoutPosition')) this.settings.layoutPosition = null if (!this._hasControls('layoutReversed')) this.settings.layoutReversed = null if (!this._hasControls('layoutNavbarFixed')) this.settings.layoutNavbarFixed = null if (!this._hasControls('layoutFooterFixed')) this.settings.layoutFooterFixed = null if (!this._hasControls('navbarBg')) this.settings.navbarBg = null if (!this._hasControls('sidenavBg')) this.settings.sidenavBg = null if (!this._hasControls('footerBg')) this.settings.footerBg = null if (!this._hasControls('themes')) this.settings.theme = null } _setup(_container = document) { this._cleanup() this.container = this._getElementFromString(markup) // Open btn // const openBtn = this.container.querySelector('.theme-settings-open-btn') const openBtnCb = () => { this.container.classList.add('theme-settings-open') this.update() if (this._updateInterval) clearInterval(this._updateInterval) this._updateInterval = setInterval(() => { this.update() }, 1000) } openBtn.addEventListener('click', openBtnCb) this._listeners.push([ openBtn, 'click', openBtnCb ]) // Close btn // const closeBtn = this.container.querySelector('.theme-settings-close-btn') const closeBtnCb = () => { this.container.classList.remove('theme-settings-open') if (this._updateInterval) { clearInterval(this._updateInterval) this._updateInterval = null } } closeBtn.addEventListener('click', closeBtnCb) this._listeners.push([ closeBtn, 'click', closeBtnCb ]) // RTL // const rtlW = this.container.querySelector('.theme-settings-rtl') if (!this._hasControls('rtl')) { rtlW.parentNode.removeChild(rtlW) } else { const rtl = rtlW.querySelector('input') if (this.settings.rtl) rtl.setAttribute('checked', 'checked') const rtlCb = e => { this._loadingState(true) this.setRtl(e.target.checked) } rtl.addEventListener('change', rtlCb) this._listeners.push([ rtl, 'change', rtlCb ]) } // Style // const styleW = this.container.querySelector('.theme-settings-style') if (!this._hasControls('style')) { styleW.parentNode.removeChild(styleW) } else { const style = styleW.querySelector('select') this.settings.styles.forEach(styleItem => { const styleEl = this._getElementFromString( `` ) style.appendChild(styleEl) }) const styleCb = e => { this._loadingState(true) this.setStyle(e.target.value) } style.addEventListener('change', styleCb) this._listeners.push([ style, 'change', styleCb ]) } // Layout wrapper // const layoutW = this.container.querySelector('.theme-settings-layout') if (!this._hasControls('layoutPosition layoutNavbarFixed layoutFooterFixed layoutReversed', true)) { layoutW.parentNode.removeChild(layoutW) } else { // Position // const layoutPositionW = this.container.querySelector('.theme-settings-layoutPosition') if (!this._hasControls('layoutPosition')) { layoutPositionW.parentNode.removeChild(layoutPositionW) } else { this._controls.layoutPosition = layoutPositionW.querySelector('select') this._controls.layoutPosition.value = this.settings.layoutPosition const layoutPositionCb = e => this.setLayoutPosition(e.target.value) this._controls.layoutPosition.addEventListener('change', layoutPositionCb) this._listeners.push([ this._controls.layoutPosition, 'change', layoutPositionCb ]) } // Navbar // this._controls.layoutNavbarFixedW = this.container.querySelector('.theme-settings-layoutNavbarFixed') if (!this._hasControls('layoutNavbarFixed')) { this._controls.layoutNavbarFixedW.parentNode.removeChild(this._controls.layoutNavbarFixedW) } else { this._controls.layoutNavbarFixed = this._controls.layoutNavbarFixedW.querySelector('input') if (this.settings.layoutNavbarFixed) this._controls.layoutNavbarFixed.setAttribute('checked', 'checked') const layoutNavbarFixedCb = e => this.setLayoutNavbarFixed(e.target.checked) this._controls.layoutNavbarFixed.addEventListener('change', layoutNavbarFixedCb) this._listeners.push([ this._controls.layoutNavbarFixed, 'change', layoutNavbarFixedCb ]) } // Footer // this._controls.layoutFooterFixedW = this.container.querySelector('.theme-settings-layoutFooterFixed') if (!this._hasControls('layoutFooterFixed')) { this._controls.layoutFooterFixedW.parentNode.removeChild(this._controls.layoutFooterFixedW) } else { this._controls.layoutFooterFixed = this._controls.layoutFooterFixedW.querySelector('input') if (this.settings.layoutFooterFixed) this._controls.layoutFooterFixed.setAttribute('checked', 'checked') const layoutFooterFixedCb = e => this.setLayoutFooterFixed(e.target.checked) this._controls.layoutFooterFixed.addEventListener('change', layoutFooterFixedCb) this._listeners.push([ this._controls.layoutFooterFixed, 'change', layoutFooterFixedCb ]) } // Reversed // this._controls.layoutReversedW = this.container.querySelector('.theme-settings-layoutReversed') if (!this._hasControls('layoutReversed')) { this._controls.layoutReversedW.parentNode.removeChild(this._controls.layoutReversedW) } else { this._controls.layoutReversed = this._controls.layoutReversedW.querySelector('input') if (this.settings.layoutReversed) this._controls.layoutReversed.setAttribute('checked', 'checked') const layoutReversedCb = e => this.setLayoutReversed(e.target.checked) this._controls.layoutReversed.addEventListener('change', layoutReversedCb) this._listeners.push([ this._controls.layoutReversed, 'change', layoutReversedCb ]) } } // Navbar Bg // const navbarBgW = this.container.querySelector('.theme-settings-navbarBg') if (!this._hasControls('navbarBg')) { navbarBgW.parentNode.removeChild(navbarBgW) } else { this._controls.navbarBgWInner = navbarBgW.querySelector('.theme-settings-navbarBg-inner') this.settings.navbarBgs.forEach(bg => { const bgItem = this._getElementFromString(bgItemMarkup) const control = bgItem.querySelector('input') bgItem.classList.add(`bg-${bg.split(' ')[0]}`) control.name = 'theme-settings-navbarBg-input' control.value = bg if (this.settings.navbarBg === bg) { control.setAttribute('checked', 'checked') bgItem.classList.add('active') } const cb = e => { const items = this._controls.navbarBgWInner.querySelectorAll('.theme-settings-bg-item') for (let i = 0, l = items.length; i < l; i++) items[i].classList.remove('active') e.target.parentNode.classList.add('active') this.setNavbarBg(e.target.value) } control.addEventListener('change', cb) this._listeners.push([ control, 'change', cb ]) this._controls.navbarBgWInner.appendChild(bgItem) }) } // Sidenav Bg // const sidenavBgW = this.container.querySelector('.theme-settings-sidenavBg') if (!this._hasControls('sidenavBg')) { sidenavBgW.parentNode.removeChild(sidenavBgW) } else { this._controls.sidenavBgWInner = sidenavBgW.querySelector('.theme-settings-sidenavBg-inner') this.settings.sidenavBgs.forEach(bg => { const bgItem = this._getElementFromString(bgItemMarkup) const control = bgItem.querySelector('input') bgItem.classList.add(`bg-${bg.split(' ')[0]}`) control.name = 'theme-settings-sidenavBg-input' control.value = bg if (this.settings.sidenavBg === bg) { control.setAttribute('checked', 'checked') bgItem.classList.add('active') } const cb = e => { const items = this._controls.sidenavBgWInner.querySelectorAll('.theme-settings-bg-item') for (let i = 0, l = items.length; i < l; i++) items[i].classList.remove('active') e.target.parentNode.classList.add('active') this.setSidenavBg(e.target.value) } control.addEventListener('change', cb) this._listeners.push([ control, 'change', cb ]) this._controls.sidenavBgWInner.appendChild(bgItem) }) } // Footer Bg // const footerBgW = this.container.querySelector('.theme-settings-footerBg') if (!this._hasControls('footerBg')) { footerBgW.parentNode.removeChild(footerBgW) } else { this._controls.footerBgWInner = footerBgW.querySelector('.theme-settings-footerBg-inner') this.settings.footerBgs.forEach(bg => { const bgItem = this._getElementFromString(bgItemMarkup) const control = bgItem.querySelector('input') bgItem.classList.add(`bg-${bg.split(' ')[0]}`) control.name = 'theme-settings-footerBg-input' control.value = bg if (this.settings.footerBg === bg) { control.setAttribute('checked', 'checked') bgItem.classList.add('active') } const cb = e => { const items = this._controls.footerBgWInner.querySelectorAll('.theme-settings-bg-item') for (let i = 0, l = items.length; i < l; i++) items[i].classList.remove('active') e.target.parentNode.classList.add('active') this.setFooterBg(e.target.value) } control.addEventListener('change', cb) this._listeners.push([ control, 'change', cb ]) this._controls.footerBgWInner.appendChild(bgItem) }) } // Themes // const themesW = this.container.querySelector('.theme-settings-themes') if (!this._hasControls('themes')) { themesW.parentNode.removeChild(themesW) } else { const themesWInner = this.container.querySelector('.theme-settings-themes-inner') // SETUP THEMES this.settings.availableThemes.forEach(theme => { const themeItem = this._getElementFromString(themeItemMarkup) const control = themeItem.querySelector('input') const colors = this.isDarkStyle() ? theme.colorsDark : theme.colors control.value = theme.name if (this.settings.theme.name === theme.name) { control.setAttribute('checked', 'checked') } themeItem.querySelector('.theme-settings-theme-colors').innerHTML = ` ` const cb = e => { if (this._loading) return this._loading = true this._loadingState(true, true) this.setTheme(e.target.value, true, () => { this._loading = false this._loadingState(false, true) }) } control.addEventListener('change', cb) this._listeners.push([ control, 'change', cb ]) themesWInner.appendChild(themeItem) }) } // Set language this.setLang(this.settings.lang, true) // Append container if (_container === document) { if (_container.body) { _container.body.appendChild(this.container) } else { window.addEventListener('DOMContentLoaded', () => _container.body.appendChild(this.container)) } } else { _container.appendChild(this.container) } } _initDirection() { if (this._hasControls('rtl')) document.documentElement.setAttribute('dir', this.settings.rtl ? 'rtl' : 'ltr') } _initStyle() { if (!this._hasControls('style')) return const style = this.settings.style this._insertStylesheet('theme-settings-bootstrap-css', this.pathResolver( this.settings.cssPath + this.settings.cssFilenamePattern.replace('%name%', 'bootstrap' + (style !== 'light' ? `-${style}` : '')) )) this._insertStylesheet('theme-settings-appwork-css', this.pathResolver( this.settings.cssPath + this.settings.cssFilenamePattern.replace('%name%', 'appwork' + (style !== 'light' ? `-${style}` : '')) )) this._insertStylesheet('theme-settings-colors-css', this.pathResolver( this.settings.cssPath + this.settings.cssFilenamePattern.replace('%name%', 'colors' + (style !== 'light' ? `-${style}` : '')) )) const classesToRemove = style === 'light' ? ['material-style', 'dark-style'] : ( style === 'material' ? ['light-style', 'dark-style'] : ['light-style', 'material-style'] ) classesToRemove.forEach(cls => { document.documentElement.classList.remove(cls) }) document.documentElement.classList.add(`${style}-style`) if (style === 'material' && window.attachMaterialRipple) { if (document.body) { window.attachMaterialRipple() } else { window.addEventListener('DOMContentLoaded', () => window.attachMaterialRipple()) } } } _initTheme() { if (this._hasControls('themes')) { this._insertStylesheet('theme-settings-theme-css', this.pathResolver( this.settings.themesPath + this.settings.cssFilenamePattern.replace('%name%', this.settings.theme.name + (this.settings.style !== 'light' ? `-${this.settings.style}` : '')) )) } } _insertStylesheet(className, href) { const curLink = document.querySelector(`.${className}`) if (typeof document.documentMode === 'number' && document.documentMode < 11) { if (!curLink) return if (href === curLink.getAttribute('href')) return const link = document.createElement('link') link.setAttribute('rel', 'stylesheet') link.setAttribute('type', 'text/css') link.className = className link.setAttribute('href', href) curLink.parentNode.insertBefore(link, curLink.nextSibling) } else { document.write(``) } curLink.parentNode.removeChild(curLink) } _loadStylesheets(stylesheets, cb) { const paths = Object.keys(stylesheets) const count = paths.length let loaded = 0 function loadStylesheet(path, curLink, _cb) { const link = document.createElement('link') link.setAttribute('href', path) link.setAttribute('rel', 'stylesheet') link.setAttribute('type', 'text/css') link.className = curLink.className const sheet = 'sheet' in link ? 'sheet' : 'styleSheet' const cssRules = 'sheet' in link ? 'cssRules' : 'rules' let timeoutId, intervalId timeoutId = setTimeout(function() { clearInterval(intervalId) clearTimeout(timeoutId) curLink.parentNode.removeChild(link) _cb(false, path) }, 15000 ) intervalId = setInterval(function() { try { if (link[sheet] && link[sheet][cssRules].length) { clearInterval(intervalId) clearTimeout(timeoutId) curLink.parentNode.removeChild(curLink) _cb(true) } } catch(e) { console.error(e) } finally {} }, 10) curLink.parentNode.insertBefore(link, curLink.nextSibling) } for (let i = 0; i < paths.length; i++) { loadStylesheet(paths[i], stylesheets[paths[i]], function(success, errPath) { if (!success) { if (console && typeof console.error === 'function') { console.error('Error occured while loading stylesheets!') } alert('Error occured while loading stylesheets!') console.log(errPath) } if (++loaded >= count) { cb() } }) } } _loadingState(enable, themes) { this.container.classList[enable ? 'add' : 'remove'](`theme-settings-loading${themes ? '-theme' : ''}`) } _waitForNavs() { this._addObserver( '.layout-navbar.navbar, .layout-navbar .navbar', node => { return node && node.classList && node.classList.contains('layout-navbar') && ( node.classList.contains('navbar') || node.querySelector('.navbar') ) }, () => this.setNavbarBg(this.settings.navbarBg, false) ) this._addObserver( '.layout-sidenav.sidenav, .layout-sidenav .sidenav, .layout-sidenav-horizontal.sidenav, .layout-sidenav-horizontal .sidenav', node => { return node && node.classList && ( (node.classList.contains('layout-sidenav') || node.classList.contains('layout-sidenav-horizontal')) && ( node.classList.contains('sidenav') || node.querySelector('.sidenav') ) ) }, () => this.setSidenavBg(this.settings.sidenavBg, false) ) this._addObserver( '.layout-footer.footer, .layout-footer .footer', node => { return node && node.classList && node.classList.contains('layout-footer') && ( node.classList.contains('footer') || node.querySelector('.footer') ) }, () => this.setFooterBg(this.settings.footerBg, false) ) if (!document.body && ((this._observers && this._observers.length) || (this._intervals && this._intervals.length))) { const loadCb = () => { this._clearObservers() this.setNavbarBg(this.settings.navbarBg, false) this.setSidenavBg(this.settings.sidenavBg, false) this.setFooterBg(this.settings.footerBg, false) window.removeEventListener('load', loadCb) } window.addEventListener('load', loadCb) } } _addObserver(selector, condition, cb) { if (!this._observers) this._observers = [] if (!this._intervals) this._intervals = [] let _observer let _interval if (document.querySelector(selector)) { cb.call(this) } else if (!document.body) { if (typeof MutationObserver !== 'undefined') { _observer = new MutationObserver(mutations => { mutations.forEach(mutation => { if (!mutation.addedNodes) return for (let i = 0; i < mutation.addedNodes.length; i++) { let node = mutation.addedNodes[i] if (condition.call(this, node)) { _observer.disconnect() this._observers.splice(this._observers.indexOf(_observer), 1) _observer = null cb.call(this) break } } }) }) this._observers.push(_observer) _observer.observe(document.documentElement, { childList: true, subtree: true, attributes: false, characterData: false }) } else { _interval = setInterval(() => { if (document.querySelector(selector)) { clearInterval(_interval) this._intervals.splice(this._intervals.indexOf(_interval), 1) _interval = null cb.call(this) } }, 10) this._intervals.push(_interval) } } } _clearObservers() { if (this._observers && this._observers.length) { for (let i = 0, l = this._observers.length; i < l; i++) { this._observers[i].disconnect() } } if (this._intervals && this._intervals.length) { for (let j = 0, k = this._intervals.length; j < k; j++) { clearInterval(this._intervals[j]) } } this._observers = null this._intervals = null } _getElementFromString(str) { const wrapper = document.createElement('div') wrapper.innerHTML = str return wrapper.firstChild } _getSetting(key) { let result = null try { result = localStorage.getItem(`themeSettings${key}`) } catch (e) {} return String(result || '') } _setSetting(key, val) { try { localStorage.setItem(`themeSettings${key}`, String(val)) } catch (e) {} } _removeListeners() { for (let i = 0, l = this._listeners.length; i < l; i++) { this._listeners[i][0].removeEventListener(this._listeners[i][1], this._listeners[i][2]) } } _cleanup() { this._removeListeners() this._listeners = [] this._controls = {} this._clearObservers() if (this._updateInterval) { clearInterval(this._updateInterval) this._updateInterval = null } } get _ssr() { return typeof window === 'undefined' } _hasControls(controls, oneOf = false) { return controls.split(' ').reduce((result, control) => { if (this.settings.controls.indexOf(control) !== -1) { if (oneOf || result !== false) result = true } else { if (!oneOf || result !== true) result = false } return result }, null) } _getDefaultTheme(themeId) { let theme if (typeof themeId === 'string') { theme = this._getThemeByName(themeId, false) } else { theme = this.settings.availableThemes[themeId] } if (!theme) { throw new Error(`Theme ID "${themeId}" not found!`) } return theme } _getThemeByName(themeName, returnDefault = false) { const themes = this.settings.availableThemes for (var i = 0, l = themes.length; i < l; i++) { if (themes[i].name === themeName) return themes[i] } return returnDefault ? this.settings.defaultTheme : null } } ThemeSettings.AVAILABLE_THEMES = [ { name: 'theme-air', title: 'Air', colors: { primary: '#3c97fe', navbar: '#f8f8f8', sidenav: '#f8f8f8' }, colorsDark: { primary: '#3c97fe', navbar: '#25282e', sidenav: '#25282e' } }, { name: 'theme-corporate', title: 'Corporate', colors: { primary: '#26B4FF', navbar: '#fff', sidenav: '#2e323a' }, colorsDark: { primary: '#26B4FF', navbar: '#1c1f24', sidenav: '#2f3238' } }, { name: 'theme-cotton', title: 'Сotton', colors: { primary: '#e84c64', navbar: '#ffffff', sidenav: '#ffffff' }, colorsDark: { primary: '#e84c64', navbar: '#25282e', sidenav: '#d2d3d4' } }, { name: 'theme-gradient', title: 'Gradient', colors: { primary: '#775cdc', navbar: '#ffffff', sidenav: 'linear-gradient(to top, #4e54c8, #8c55e4)' }, colorsDark: { primary: '#775cdc', navbar: '#1c1f24', sidenav: 'linear-gradient(to top, #4e54c8, #8c55e4)' } }, { name: 'theme-paper', title: 'Paper', colors: { primary: '#17b3a3', navbar: '#ffffff', sidenav: '#ffffff' }, colorsDark: { primary: '#17b3a3', navbar: '#32353b', sidenav: '#ffffff' } }, { name: 'theme-shadow', title: 'Shadow', colors: { primary: '#7b83ff', navbar: '#f8f8f8', sidenav: '#ececf9' }, colorsDark: { primary: '#7b83ff', navbar: '#25282e', sidenav: '#424256' } }, { name: 'theme-soft', title: 'Soft', colors: { primary: '#1cbb84', navbar: '#39517b', sidenav: '#ffffff' }, colorsDark: { primary: '#1cbb84', navbar: '#39517b', sidenav: '#E9E9EA' } }, { name: 'theme-sunrise', title: 'Sunrise', colors: { primary: '#fc5a5c', navbar: '#222222', sidenav: '#ffffff' }, colorsDark: { primary: '#fc5a5c', navbar: '#fff', sidenav: '#1c1f24' } }, { name: 'theme-twitlight', title: 'Twitlight', colors: { primary: '#4c84ff', navbar: '#343c44', sidenav: '#3f4853' }, colorsDark: { primary: '#4c84ff', navbar: '#2b3239', sidenav: '#303942' } }, { name: 'theme-vibrant', title: 'Vibrant', colors: { primary: '#fc5a5c', navbar: '#f8f8f8', sidenav: '#222222' }, colorsDark: { primary: '#fc5a5c', navbar: '#25282e', sidenav: '#1c1f24' } }, ] ThemeSettings.NAVBAR_BGS = [ 'navbar-theme', 'primary', 'primary-dark navbar-dark', 'primary-darker navbar-dark', 'secondary', 'secondary-dark navbar-dark', 'secondary-darker navbar-dark', 'success', 'success-dark navbar-dark', 'success-darker navbar-dark', 'info', 'info-dark navbar-dark', 'info-darker navbar-dark', 'warning', 'warning-dark navbar-light', 'warning-darker navbar-light', 'danger', 'danger-dark navbar-dark', 'danger-darker navbar-dark', 'dark', 'white', 'light', 'lighter' ] ThemeSettings.SIDENAV_BGS = [ 'sidenav-theme', 'primary', 'primary-dark sidenav-dark', 'primary-darker sidenav-dark', 'secondary', 'secondary-dark sidenav-dark', 'secondary-darker sidenav-dark', 'success', 'success-dark sidenav-dark', 'success-darker sidenav-dark', 'info', 'info-dark sidenav-dark', 'info-darker sidenav-dark', 'warning', 'warning-dark sidenav-light', 'warning-darker sidenav-light', 'danger', 'danger-dark sidenav-dark', 'danger-darker sidenav-dark', 'dark', 'white', 'light', 'lighter' ] ThemeSettings.FOOTER_BGS = [ 'footer-theme', 'primary', 'primary-dark footer-dark', 'primary-darker footer-dark', 'secondary', 'secondary-dark footer-dark', 'secondary-darker footer-dark', 'success', 'success-dark footer-dark', 'success-darker footer-dark', 'info', 'info-dark footer-dark', 'info-darker footer-dark', 'warning', 'warning-dark footer-light', 'warning-darker footer-light', 'danger', 'danger-dark footer-dark', 'danger-darker footer-dark', 'dark', 'white', 'light', 'lighter' ] ThemeSettings.LANGUAGES = { en: { panel_header: 'SETTINGS', rtl_switcher: 'RTL direction', style_switcher_light: 'Light style', style_switcher_material: 'Material style', style_switcher_dark: 'Dark style', layout_header: 'LAYOUT', layout_static: 'Static', layout_offcanvas: 'Offcanvas', layout_fixed: 'Fixed', layout_fixed_offcanvas: 'Fixed offcanvas', layout_navbar_swicher: 'Fixed navbar', layout_footer_swicher: 'Fixed footer', layout_reversed_swicher: 'Reversed', navbar_bg_header: 'NAVBAR BACKGROUND', sidenav_bg_header: 'SIDENAV BACKGROUND', footer_bg_header: 'FOOTER BACKGROUND', theme_header: 'THEME' } } export { ThemeSettings }