36 lines
4.3 KiB
JavaScript
36 lines
4.3 KiB
JavaScript
'use strict';
|
|
// ---- Themes: each person picks an accent colour and Auto, Light, Dark or AMOLED black (true black, easy on phone batteries). Kept in their synced settings, so it follows them between devices.
|
|
const THEME_ACCENTS = [['blue', 'Blue', '#3d6bf0', '#6a8cff'], ['teal', 'Teal', '#0f8f8a', '#3cc7c0'], ['green', 'Green', '#2f9e6e', '#4cc98d'], ['orange', 'Orange', '#d9741a', '#f09a45'], ['red', 'Red', '#d4392f', '#ff6b60'], ['pink', 'Pink', '#c9407d', '#f26aa4'], ['purple', 'Purple', '#7b4fe0', '#a688ff']];
|
|
const THEME_PERSON = ['#4f7cff', '#2f9e6e', '#e0742b', '#d9453a', '#8a5cf0', '#e5a00d', '#0f8f8a', '#c9407d'];
|
|
const THEME_MODES = [['auto', 'Auto'], ['light', 'Light'], ['dark', 'Dark'], ['amoled', 'AMOLED']];
|
|
const THEME_VARS = {
|
|
light: { '--bg': '#f5f6f8', '--panel': '#fff', '--panel2': '#eef0f4', '--text': '#14171c', '--muted': '#6a7280', '--line': '#e0e3e9' },
|
|
dark: { '--bg': '#0f1115', '--panel': '#181b21', '--panel2': '#20242c', '--text': '#eceef2', '--muted': '#8b93a3', '--line': '#272c35' },
|
|
amoled: { '--bg': '#000', '--panel': '#0b0b0d', '--panel2': '#16171b', '--text': '#eceef2', '--muted': '#8b93a3', '--line': '#1f2127' },
|
|
};
|
|
function themeApply() {
|
|
const root = document.documentElement, mode = store.get('theme_mode', 'auto'), acc = THEME_ACCENTS.find(a => a[0] === store.get('theme_accent', 'blue')) || THEME_ACCENTS[0];
|
|
const dark = mode === 'dark' || mode === 'amoled' || (mode === 'auto' && matchMedia('(prefers-color-scheme: dark)').matches);
|
|
for (const k of Object.keys(THEME_VARS.light)) { if (mode === 'auto') root.style.removeProperty(k); else root.style.setProperty(k, THEME_VARS[mode][k]); }
|
|
const hex = dark ? acc[3] : acc[2];
|
|
if (acc[0] === 'blue' && mode === 'auto') { root.style.removeProperty('--accent'); root.style.removeProperty('--accent-t'); }
|
|
else { const n = parseInt(hex.slice(1), 16), lum = (0.299 * (n >> 16) + 0.587 * ((n >> 8) & 255) + 0.114 * (n & 255)) / 255; root.style.setProperty('--accent', hex); root.style.setProperty('--accent-t', lum > .6 ? '#0b1020' : '#fff'); }
|
|
root.style.colorScheme = mode === 'auto' ? '' : dark ? 'dark' : 'light';
|
|
const m = document.querySelector('meta[name=theme-color]'); if (m) m.content = dark ? (mode === 'amoled' ? '#000000' : '#0f1115') : (mode === 'light' ? '#f5f6f8' : m.content);
|
|
}
|
|
function themeStart() { themeApply(); try { matchMedia('(prefers-color-scheme: dark)').addEventListener('change', themeApply); } catch {} }
|
|
function themeSettings(el) {
|
|
if (!el) return;
|
|
const draw = () => {
|
|
const mode = store.get('theme_mode', 'auto'), acc = store.get('theme_accent', 'blue');
|
|
el.innerHTML = `<h2>Look</h2><p class="sub">Saved for you and used on every device you sign in on.</p>
|
|
<div class="seg" role="radiogroup" aria-label="Brightness">${THEME_MODES.map(([k, l]) => `<a href="#" role="radio" aria-checked="${mode === k}" data-m="${k}" class="${mode === k ? 'on' : ''}">${l}</a>`).join('')}</div>
|
|
<div class="swatches" role="radiogroup" aria-label="Accent colour">${THEME_ACCENTS.map(([k, l, c]) => `<button type="button" role="radio" aria-checked="${acc === k}" aria-label="${l}" title="${l}" data-a="${k}" class="${acc === k ? 'on' : ''}" style="background:${c}"></button>`).join('')}</div>
|
|
<h3 class="sub" style="margin:14px 0 0">My colour on shared calendars</h3><div class="swatches" role="radiogroup" aria-label="My colour on shared calendars">${(typeof ME !== 'undefined' && ME ? THEME_PERSON : []).map(c => `<button type="button" role="radio" aria-checked="${ME.color === c}" aria-label="${c}" data-pc="${c}" class="${ME.color === c ? 'on' : ''}" style="background:${c}"></button>`).join('')}</div>`;
|
|
el.querySelectorAll('[data-pc]').forEach(b => b.onclick = async () => { try { const r = await jfetch('/api/me/color', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ color: b.dataset.pc }) }); ME.color = r.color; try { localStorage.setItem('me_cache', JSON.stringify(ME)); } catch {} draw(); } catch (e) { toast(e.message); } });
|
|
el.querySelectorAll('[data-m]').forEach(a => a.onclick = e => { e.preventDefault(); store.set('theme_mode', a.dataset.m); themeApply(); draw(); });
|
|
el.querySelectorAll('[data-a]').forEach(b => b.onclick = () => { store.set('theme_accent', b.dataset.a); themeApply(); draw(); });
|
|
};
|
|
draw();
|
|
}
|