Skip to content

Commit

Permalink
feat: added theme switcher with match-system option
Browse files Browse the repository at this point in the history
  • Loading branch information
kirill-dev-pro committed Nov 25, 2024
1 parent a7003d0 commit 0cb9d71
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 36 deletions.
11 changes: 1 addition & 10 deletions frontend/html/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,6 @@
{% block meta %}
{% include "common/meta.html" %}
{% endblock %}
<script>
const theme = localStorage.getItem('theme');
if (theme !== null) {
document.documentElement.setAttribute('theme', theme);
}
</script>

{% block feeds %}
<link rel="alternate" type="application/rss+xml"
Expand Down Expand Up @@ -62,10 +56,7 @@
CC BY-SA
</div>
<div class="footer-right">
<label class="theme-switcher" for="checkbox">
<input type="checkbox" id="checkbox" />
<span class="slider round"></span>
</label>
{% include "misc/theme_switcher.html" %}

{% if me %}
<form method="POST" action="{% url "logout" %}">
Expand Down
90 changes: 90 additions & 0 deletions frontend/html/misc/theme_switcher.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<starlight-theme-select>
<label style='width: 6.25em'>
<span class="sr-only">Выберите тему</span>
<select icon="laptop" label="Выберите тему" value="auto" width="6.25em">
<option value="dark">Темная тема</option>
<option value="light">Светлая тема</option>
<option value="auto">Системная тема</option>
</select>
</label>
</starlight-theme-select>


<script>
const storageKey = 'theme';

const parseTheme = (theme) =>
theme === 'auto' || theme === 'dark' || theme === 'light' ? theme : 'auto';

const loadTheme = () =>
parseTheme(typeof localStorage !== 'undefined' && localStorage.getItem(storageKey));

function storeTheme(theme) {
if (typeof localStorage !== 'undefined') {
localStorage.setItem(storageKey, theme === 'light' || theme === 'dark' ? theme : '');
}
}

const getPreferredColorScheme = () =>
matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';

matchMedia(`(prefers-color-scheme: light)`).addEventListener('change', () => {
if (loadTheme() === 'auto') onThemeChange('auto');
});

class StarlightThemeProvider {
storedTheme = loadTheme();

constructor() {
const theme = this.storedTheme ||
(window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark');
document.documentElement.dataset.theme = theme === 'light' ? 'light' : 'dark';
}

updatePickers(theme = this.storedTheme || 'auto') {
document.querySelectorAll('starlight-theme-select').forEach((picker) => {
console.log('picker', picker);
const select = picker.querySelector('select');
if (select) select.value = theme;

/** @type {HTMLTemplateElement | null} */
const tmpl = document.querySelector(`#theme-icons`);
const newIcon = tmpl && tmpl.content.querySelector('.' + theme);
if (newIcon) {
const oldIcon = picker.querySelector('starlight-theme-select i.label-icon');
if (oldIcon) {
oldIcon.replaceChildren(...newIcon.cloneNode(true).childNodes);
}
}
});
}
}


class StarlightThemeSelect extends HTMLElement {
themeProvider = new StarlightThemeProvider();

constructor() {
super();
this.onThemeChange(loadTheme());
this.querySelector('select')?.addEventListener('change', (e) => {
if (e.currentTarget instanceof HTMLSelectElement) {
this.onThemeChange(parseTheme(e.currentTarget.value));
}
});

this.themeProvider.updatePickers(loadTheme());

matchMedia(`(prefers-color-scheme: light)`).addEventListener('change', () => {
if (loadTheme() === 'auto') this.onThemeChange('auto');
});
}

onThemeChange(theme) {
this.themeProvider.updatePickers(theme);
document.documentElement.setAttribute("theme", theme === 'auto' ? getPreferredColorScheme() : theme);
storeTheme(theme);
}
}
customElements.define('starlight-theme-select', StarlightThemeSelect);
</script>
51 changes: 25 additions & 26 deletions frontend/static/js/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
createFileInput,
createMarkdownEditor,
handleFormSubmissionShortcuts,
imageUploadOptions
imageUploadOptions,
} from "./common/markdown-editor";
import { getCollapsedCommentThreadsSet } from "./common/comments";

Expand Down Expand Up @@ -38,39 +38,38 @@ const App = {
initializeEmojiForPoorPeople() {
const isApple = /iPad|iPhone|iPod|OS X/.test(navigator.userAgent) && !window.MSStream;
if (!isApple) {
document.body = twemoji.parse(
document.body,
{ base: "https://cdn.jsdelivr.net/gh/twitter/[email protected]/assets/" }
);
document.body = twemoji.parse(document.body, {
base: "https://cdn.jsdelivr.net/gh/twitter/[email protected]/assets/",
});
}
},
initializeThemeSwitcher() {
const themeSwitch = document.querySelector('.theme-switcher input[type="checkbox"]');
// const themeSwitch = document.querySelector('.theme-switcher input[type="checkbox"]');
const mediaQueryList = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)");

themeSwitch.addEventListener(
"change",
function (e) {
let theme = "light";
if (e.target.checked) {
theme = "dark";
}
document.documentElement.setAttribute("theme", theme);
localStorage.setItem("theme", theme);
},
false
);

const theme = localStorage.getItem("theme");
themeSwitch.checked = theme ? theme === "dark" : mediaQueryList.matches;
// themeSwitch.addEventListener(
// "change",
// function (e) {
// let theme = "light";
// if (e.target.checked) {
// theme = "dark";
// }
// document.documentElement.setAttribute("theme", theme);
// localStorage.setItem("theme", theme);
// },
// false
// );

// const theme = localStorage.getItem("theme");
// themeSwitch.checked = theme ? theme === "dark" : mediaQueryList.matches;

const setFaviconHref = (e) => {
const svgFavicon = document.querySelector('link[type="image/svg+xml"]');
const isDark = e.matches;

if (!theme) {
themeSwitch.checked = isDark;
}
// if (!theme) {
// themeSwitch.checked = isDark;
// }

svgFavicon.href = isDark ? "/static/images/favicon/favicon-dark.svg" : "/static/images/favicon/favicon.svg";
};
Expand All @@ -91,7 +90,7 @@ const App = {

const fullMarkdownEditors = [...document.querySelectorAll(".markdown-editor-full")].reduce(
(editors, element) => {
const fileInputEl = createFileInput({ allowedTypes: imageUploadOptions.allowedTypes })
const fileInputEl = createFileInput({ allowedTypes: imageUploadOptions.allowedTypes });
const editor = createMarkdownEditor(element, {
autosave: {
enabled: false,
Expand Down Expand Up @@ -145,7 +144,7 @@ const App = {
{
name: "upload-file",
action: () => {
fileInputEl.click()
fileInputEl.click();
},
className: "fa fa-paperclip",
text: "Upload image",
Expand Down

0 comments on commit 0cb9d71

Please sign in to comment.