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 }