diff --git a/css/felexdev-themes.css b/css/felexdev-themes.css
index d78437f..6d34e76 100644
--- a/css/felexdev-themes.css
+++ b/css/felexdev-themes.css
@@ -52,14 +52,6 @@
--text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
--font-family: 'Arial', sans-serif;
}
-.flxd-tm-color-scheme-old-terminal {
- --bg-color: #222;
- --text-color: #9d9;
- --accent-color: #40a9ff;
- --border-color: #444;
- --text-shadow: 0px 0px 10px rgba(255, 255, 0, 0.9);
- --font-family: Monospace;
-}
body {
margin: auto;
diff --git a/js/felexdev-theme-manager-minified.js b/js/felexdev-theme-manager-minified.js
index 59a7837..14ec866 100644
--- a/js/felexdev-theme-manager-minified.js
+++ b/js/felexdev-theme-manager-minified.js
@@ -1,34 +1,9 @@
/**
* @file felexdev-theme-manager.js
- * @description Миниицированная версия
+ * @description Минимальная версия
* @version 0.0.1
* @author felex67 (admin@felexdev.ru)
* @license Apache2.0
* @warning Системная версия('auto') должна регистрироваться первой!!!
*/
-class ThemeManager {
- /** Cunfigurations */
- static #c = {
- btnid:'btn-toggle', // Идентификатор кнопки переключения: `gpfx + btnid == 'flxd-tm-btn-toggle'`
- btnti:0, // Индекс табуляции кнопки переключения тем
- gpfx:'flxd-tm', // Глобальный префикс: `gpfx + '-' == 'flxd-tm-'`
- stid:'color-scheme', // Селектор CSS: `gpfx + stdid == 'flxd-tm-color-scheme'`
- svg:'',
- /* */
- };static #cl=document.documentElement.classList;static #b=document.createElement('div');static #i=0;static #t=[];static toggle=()=>{this.#s((this.#i+1)%this.#t.length);};static{let m=this;let c=m.#c;c.gpfx=c.gpfx+'-';c.stid=c.gpfx+c.stid;m.#b.id=c.btnid=c.gpfx+c.btnid;m.#b.tabIndex=m.#c.btnti;m.#b.setAttribute('aria-label', 'toggle color scheme');m.#b.addEventListener('click',m.toggle);m.#b.addEventListener('keydown',(e)=>{if(e.key==' '||e.key=='Enter')this.toggle();});};static #at=(n,i=null)=>{this.#t.push({n:n,i:i||this.#c.svg,s:`${this.#c.stid}-${n}`});};static #s(i){if(i==this.#i)return;let m=this;if(i&&m.#i)m.#cl.replace(m.#t[m.#i].s,m.#t[i].s);else if(m.#i)m.#cl.remove(m.#t[m.#i].s);else m.#cl.add(m.#t[i].s);m.#sv(m.#i=i);m.#b.innerHTML=m.#t[i].i;m.#b.setAttribute('aria-current',m.#t[i].n);};static #sv=(()=>{let m=this;let s=(i)=>{console.error(m.#c.gpfx+'#sv: Can`t save theme id, storages are unreachable.');};try{m.#i=parseInt(localStorage.getItem(m.#c.stid))||0;s=(i)=>{try{localStorage.setItem(m.#c.stid,i);}catch(e){console.error(e);}};}catch(e){console.error(m.#c.gpfx+'#sv-IIFE: localStorage is unreachable, trying cookie.');try{let c=document.cookie.match(`${m.#c.stid}\\s*=\\s*(\\d+)`);m.#i=c?parseInt(c[1])||0:0;s=(i)=>{try{document.cookie=`${m.#c.stid}=${i};max-age=${31536000};path=/`;}catch(e){console.error(e);}};}catch(e){console.error(m.#c.gpfx+`#s-IIFE: All storages are unreachable('localStorage' & 'document.cookie')`);}}return s;})();static{let m=this;
- /**
- * Add new theme
- * @typedef {function} add
- * @param {string} themeName - Название темы используемое в селекторе CSS. 'auto' -> 'flxd-tm-color-scheme-auto'
- * @param {string?} themeIcon - Текстовое векторное изображение PlainSVG, если равно нулю используется иконка по умолчанию: 2-я фаза луны.
- */ let add=m.#at;
- /** @warning Системная версия('auto') должна регистрироваться первой!!! */
- add('auto','');
- add('light','');
- add('dark','');
- /* Темы добавлять здесь */
- add('old-terminal','');
- /* */
- if(m.#i>=m.#t.length||m.#i<0){console.error(`${m.#c.gpfx}bottom-static: Invalid theme index loaded '${m.#i}'`);m.#i=0;m.#sv(m.#i)}m.#cl.add(m.#t[m.#i].s);m.#b.innerHTML=m.#t[m.#i].i;m.#b.setAttribute('aria-current',m.#t[m.#i].n);
- };static set=(name)=>{let i=-1;for(let j=0;j{window.removeEventListener('DOMContentLoaded',this.#_);document.body.appendChild(this.#b);};static{let m=this;m.#s(m.#i);if(!document.body)window.addEventListener('DOMContentLoaded',this.#_);}
-};
\ No newline at end of file
+class ThemeManager {static #c={bid:'btn-toggle',btid:0,gpfx:'flxd-tm',tpfx:'color-scheme',svg:'',mrqd:window.matchMedia('(prefers-color-scheme: dark)'),rqdh:null};static #cl=document.documentElement.classList;static #b=document.createElement('div');static #i=0;static #t=[];static toggle=()=>{this.#s((this.#i+1)%this.#t.length);};static #bclk=(e)=>{if(e.key==='Enter'||e.key===' ')this.#s((this.#i+1)%2)};static #stch=(e)=>{if(e.matches!=this.#i)this.#s(e.matches+0);};static{let c=this.#c;let b=this.#b;c.gpfx=c.gpfx+'-';c.tpfx=`${c.gpfx}${c.tpfx}-`;b.id=c.bid=c.gpfx+c.bid;b.tabIndex=c.btid;b.innerHTML=c.svg;b.setAttribute('aria-label', 'toggle color scheme');b.addEventListener('click',this.#bclk);b.addEventListener('keydown',this.#bclk);c.rqdh=c.mrqd.addEventListener('change',this.#stch);};static #at=(n)=>{this.#t.push({n:n,s:this.#c.tpfx+n});};static #s(i){if(i==this.#i)return;let m=this;m.#cl.replace(m.#t[m.#i].s,m.#t[i].s);m.#sv(m.#i=i);m.#b.setAttribute('aria-current',m.#t[i].n);};static #sv=(()=>{let s=()=>{console.error(this.#c.gpfx+'#sv: Невозможно сохранить тему, хранилище недоступно.');};try{this.#i=parseInt(localStorage.getItem(this.#c.tpfx))||0;s=()=>{try{localStorage.setItem(this.#c.tpfx,this.#i);}catch(e){console.error(e);}};}catch(e){console.error(this.#c.gpfx+'#sv-IIFE: localStorage не доступен.');}return s;})();static{let i=this.#i;let t=this.#t;this.#at('light');this.#at('dark');if(i>=t.length||i<0){console.error(`${this.#c.gpfx}bottom-static: В хранилище сохранён не валидный индекс '${i}'. Сброс на системную тему`);this.#i=this.#c.mrqd.matches+0;}this.#cl.add(t[i].s);this.#b.setAttribute('aria-current',t[i].n);};static set=(name)=>{let i=-1;for(let j=0;j{window.removeEventListener('DOMContentLoaded',this.#_);document.body.appendChild(this.#b);};static {if(!document.body)window.addEventListener('DOMContentLoaded',this.#_);};};
\ No newline at end of file
diff --git a/js/felexdev-theme-manager.js b/js/felexdev-theme-manager.js
index 3c27a6b..f645a9f 100644
--- a/js/felexdev-theme-manager.js
+++ b/js/felexdev-theme-manager.js
@@ -1,6 +1,6 @@
/**
* @file felexdev-theme-manager.js
- * @description Минифицированная версия
+ * @description Минимальная версия
* @version 0.0.1
* @author felex67 (admin@felexdev.ru)
* @license Apache2.0
@@ -11,7 +11,6 @@ class ThemeManager {
* @typedef {Object} Theme
*
* @property {string} name - Внутреннее название темы.
- * @property {string} icon - PlainSVG-иконка темы.
* @property {string} selector - CSS-селектор темы. Без точки.
*/
@@ -24,7 +23,6 @@ class ThemeManager {
* @property {string} globalPrefix - Глобальный префикс.
* @property {string} themePrefix - Префикс CSS-class-селектора темы.
* @property {string} svg - PlainSVG-иконка темы.
- * @type {{ btnId: string; btnTabIndex: number; globalPrefix: string; themePrefix: string; svg: string; }}
*/
static #config = {
btnId: 'btn-toggle', // Идентификатор кнопки переключения: `gpfx + btnid == 'flxd-tm-btn-toggle'`
@@ -32,6 +30,8 @@ class ThemeManager {
globalPrefix: 'flxd-tm', // Глобальный префикс: `gpfx + '-' == 'flxd-tm-'`
themePrefix: 'color-scheme', // Селектор CSS: `gpfx + stdid == 'flxd-tm-color-scheme'`
svg: '',
+ mediaRequestDark: window.matchMedia('(prefers-color-scheme: dark)'),
+ requestDarkHandle: null,
};
/**
@@ -57,7 +57,7 @@ class ThemeManager {
* @type {number}
*/
static #index = 0;
-
+
/**
* Массив тем
*
@@ -65,89 +65,113 @@ class ThemeManager {
* @type {[Theme]}
*/
static #themes = [];
+
+ /**
+ * Переключает тему
+ */
static toggle = () => {
this.#set((this.#index + 1) % this.#themes.length);
};
- static /* Инициализация конфигурации */ {
+
+ /**
+ * Обработчик события нажатия кнопки переключения темы
+ */
+ static #btnClick = (e) => {
+ if (e.key === 'Enter' || e.key === ' ')
+ this.#set((this.#index + 1) % 2)
+ };
+
+
+ /**
+ * Обработчик события изменения системной темы
+ *
+ * @param {Event} e
+ */
+ static #sysThemeChange = (e) => {
+ if (e.matches != this.#index)
+ this.#set(e.matches + 0);
+ };
+
+ static { /* Инициализация конфигурации */
const config = this.#config;
+ const button = this.#button;
+ /* Настраиваем префиксы */
config.globalPrefix = config.globalPrefix + '-';
config.themePrefix = `${config.globalPrefix}${config.themePrefix}-`;
- this.#button.id = config.btnId = config.globalPrefix + config.btnId;
- this.#button.tabIndex = config.btnTabIndex;
- this.#button.setAttribute('aria-label', 'toggle color scheme');
- this.#button.addEventListener('click', this.toggle);
- this.#button.addEventListener('keydown', (e) => { if (e.key == ' ' || e.key == 'Enter') this.toggle();});
+ /* Настраиваем кнопку */
+ button.id = config.btnId = config.globalPrefix + config.btnId;
+ button.tabIndex = config.btnTabIndex;
+ button.innerHTML = config.svg;
+ /* Настраиваем обработчики */
+ button.setAttribute('aria-label', 'toggle color scheme');
+ button.addEventListener('click', this.#btnClick);
+ button.addEventListener('keydown', this.#btnClick);
+ /* Устанавливаем обработчик смены системной темы */
+ config.requestDarkHandle = config.mediaRequestDark.addEventListener('change', this.#sysThemeChange);
};
/**
* Добавить новую тему
- * @param {string} themeName - Название темы используемое в селекторе CSS. 'auto' -> 'flxd-tm-color-scheme-auto'
- * @param {string?} themeIcon - Текстовое векторное изображение PlainSVG, если равно нулю используется иконка по умолчанию: 2-я фаза луны.
+ * @param {string} name - Название темы используемое в селекторе CSS. 'light' -> 'flxd-tm-color-scheme-light'
*/
- static #addTheme = (name, icon = null) => {
+ static #addTheme = (name) => {
const theme = {
name: name,
- icon: icon || this.#config.svg,
selector: this.#config.themePrefix + name,
};
this.#themes.push(theme);
};
+ /**
+ * Устанавливаем тему по индексу
+ * @param {number} index
+ */
static #set(index) {
if (index == this.#index)
- return;
- if (index && this.#index)
- this.#classList.replace(this.#themes[this.#index].selector, this.#themes[index].selector);
- else if (this.#index)
- this.#classList.remove(this.#themes[this.#index].selector);
- else this.#classList.add(this.#themes[index].selector);
+ return;
+ this.#classList.replace(this.#themes[this.#index].selector, this.#themes[index].selector);
this.#save(this.#index = index);
- this.#button.innerHTML = this.#themes[index].icon;
this.#button.setAttribute('aria-current', this.#themes[index].name);
};
+
+ /**
+ * Сохраняет индекс текущей темы
+ * @type {function() => void}
+ * @returns void
+ */
static #save = (() => {
- let save = (index) => { console.error(this.#config.globalPrefix + '#save: Невозможно сохранить тему, хранилища не доступны.'); };
+ let save = () => { console.error(this.#config.globalPrefix + '#save: Невозможно сохранить тему, хранилище недоступно.'); };
try {
this.#index = parseInt(localStorage.getItem(this.#config.themePrefix)) || 0;
- save = (index) => {
- try { localStorage.setItem(this.#config.themePrefix, index); }
+ save = () => {
+ try { localStorage.setItem(this.#config.themePrefix, this.#index); }
catch(e) { console.error(e); }
};
}
catch(e) {
- console.error(this.#config.globalPrefix + '#sv-IIFE: localStorage не доступен, пробую cookie.');
- try {
- let cookie = document.cookie.match(`${this.#config.themePrefix}\\s*=\\s*(\\d+)`);
- this.#index = cookie ? parseInt(c[1]) || 0 : 0;
- save = (i) => {
- try {
- document.cookie = `${this.#config.themePrefix}=${i};max-age=${31536000};path=/`;
- }
- catch(e) { console.error(e); }
- };
- }
- catch(e) { console.error(this.#config.globalPrefix + `#s-IIFE: Хранилища не доступны. Настройки темы не сохраняются!`); }
+ console.error(this.#config.globalPrefix + '#sv-IIFE: localStorage не доступен.');
}
return save;
})();
- static /* Инициализация тем */ {
+ static { /* Инициализация тем */
let index = this.#index;
let themes = this.#themes;
- let addTheme = this.#addTheme;
- /** @warning Системная версия('auto') должна регистрироваться первой!!! */
- addTheme('auto','');
- addTheme('light','');
- addTheme('dark','');
- /* Новые темы добавлять здесь */
- addTheme('old-terminal','');
- /* */
+ /* Добавляем темы(светлая/темная) */
+ this.#addTheme('light');
+ this.#addTheme('dark');
+ /* Проверяем индекс */
if (index >= themes.length || index < 0) {
console.error(`${this.#config.globalPrefix}bottom-static: В хранилище сохранён не валидный индекс '${index}'. Сброс на системную тему`);
- this.#index = index = 0;
+ this.#index = this.#config.mediaRequestDark.matches + 0;
}
- if(index) this.#classList.add(themes[index].selector);
- this.#button.innerHTML = themes[index].icon;
+ /* Применяем тему */
+ this.#classList.add(themes[index].selector);
this.#button.setAttribute('aria-current', themes[index].name);
};
+ /**
+ *
+ * @param {string} name Название темы
+ * @throws Error если `name` нет в списке
+ */
static set = (name) => {
let i = -1;
for(let j = 0; j < this.#themes.length; j++) {
@@ -161,12 +185,15 @@ class ThemeManager {
}
this.#set(i);
};
+ /**
+ * Инициализация интерфейса. Добавляет кнопку к документу
+ */
static #_ = () => {
window.removeEventListener('DOMContentLoaded',this.#_);
document.body.appendChild(this.#button);
};
+ /** Назначаем обработчик события создания DOM */
static {
- let m = this;
this.#set(this.#index);
if (!document.body)window.addEventListener('DOMContentLoaded', this.#_);
}