Support selecting theme on the footer (#35741)

Fixes: https://github.com/go-gitea/gitea/pull/27576
This commit is contained in:
wxiaoguang
2025-10-28 18:25:00 +08:00
committed by GitHub
parent cddff73bbd
commit 6b5563c54a
33 changed files with 254 additions and 59 deletions

View File

@@ -65,15 +65,34 @@
flex-wrap: wrap;
align-items: center;
justify-content: center;
gap: 1em;
}
.page-footer .right-links > a {
border-left: 1px solid var(--color-secondary-dark-1);
padding-left: 8px;
margin-left: 5px;
padding-left: 1em;
}
.page-footer .ui.dropdown .menu.language-menu {
/* the theme item is also used for the menu's "default text" display */
.page-footer .ui.dropdown .theme-menu-item {
display: flex;
align-items: center;
gap: 0.5em;
}
/* Fomantic UI dropdown "remote items by API" can't change parent "item" element,
so we use "theme-menu-item" in the "item" and add tooltip to the inner one.
Then the inner one needs to get padding and parent "item" padding needs to be removed */
.page-footer .menu.theme-menu > .item {
padding: 0 !important;
}
.page-footer .menu.theme-menu > .item > .theme-menu-item {
padding: 11px 16px;
}
.page-footer .ui.dropdown .menu.language-menu,
.page-footer .ui.dropdown .menu.theme-menu {
max-height: min(500px, calc(100vh - 60px));
overflow-y: auto;
margin-bottom: 10px;

View File

@@ -2,5 +2,7 @@
@import "./theme-gitea-dark-protanopia-deuteranopia.css" (prefers-color-scheme: dark);
gitea-theme-meta-info {
--theme-display-name: "Auto (Red/Green Colorblind-friendly)";
--theme-display-name: "Auto";
--theme-colorblind-type: "red-green";
--theme-color-scheme: "auto";
}

View File

@@ -3,4 +3,5 @@
gitea-theme-meta-info {
--theme-display-name: "Auto";
--theme-color-scheme: "auto";
}

View File

@@ -1,7 +1,9 @@
@import "./theme-gitea-dark.css";
gitea-theme-meta-info {
--theme-display-name: "Dark (Red/Green Colorblind-friendly)";
--theme-display-name: "Dark";
--theme-colorblind-type: "red-green";
--theme-color-scheme: "dark";
}
/* red/green colorblind-friendly colors */

View File

@@ -3,6 +3,7 @@
gitea-theme-meta-info {
--theme-display-name: "Dark";
--theme-color-scheme: "dark";
}
:root {

View File

@@ -1,7 +1,9 @@
@import "./theme-gitea-light.css";
gitea-theme-meta-info {
--theme-display-name: "Light (Red/Green Colorblind-friendly)";
--theme-display-name: "Light";
--theme-colorblind-type: "red-green";
--theme-color-scheme: "light";
}
/* red/green colorblind-friendly colors */

View File

@@ -3,6 +3,7 @@
gitea-theme-meta-info {
--theme-display-name: "Light";
--theme-color-scheme: "light";
}
:root {

View File

@@ -1,14 +1,14 @@
import {GET} from '../modules/fetch.ts';
import {GET, POST} from '../modules/fetch.ts';
import {showGlobalErrorMessage} from '../bootstrap.ts';
import {fomanticQuery} from '../modules/fomantic/base.ts';
import {queryElems} from '../utils/dom.ts';
import {addDelegatedEventListener, queryElems} from '../utils/dom.ts';
import {registerGlobalInitFunc, registerGlobalSelectorFunc} from '../modules/observer.ts';
import {initAvatarUploaderWithCropper} from './comp/Cropper.ts';
import {initCompSearchRepoBox} from './comp/SearchRepoBox.ts';
const {appUrl} = window.config;
const {appUrl, appSubUrl} = window.config;
export function initHeadNavbarContentToggle() {
function initHeadNavbarContentToggle() {
const navbar = document.querySelector('#navbar');
const btn = document.querySelector('#navbar-expand-toggle');
if (!navbar || !btn) return;
@@ -20,7 +20,7 @@ export function initHeadNavbarContentToggle() {
});
}
export function initFootLanguageMenu() {
function initFooterLanguageMenu() {
document.querySelector('.ui.dropdown .menu.language-menu')?.addEventListener('click', async (e) => {
const item = (e.target as HTMLElement).closest('.item');
if (!item) return;
@@ -30,6 +30,27 @@ export function initFootLanguageMenu() {
});
}
function initFooterThemeSelector() {
const elDropdown = document.querySelector('#footer-theme-selector');
if (!elDropdown) return; // some pages don't have footer, for example: 500.tmpl
const $dropdown = fomanticQuery(elDropdown);
$dropdown.dropdown({
direction: 'upward',
apiSettings: {url: `${appSubUrl}/-/web-theme/list`, cache: false},
});
addDelegatedEventListener(elDropdown, 'click', '.menu > .item', async (el) => {
const themeName = el.getAttribute('data-value');
await POST(`${appSubUrl}/-/web-theme/apply?theme=${encodeURIComponent(themeName)}`);
window.location.reload();
});
}
export function initCommmPageComponents() {
initHeadNavbarContentToggle();
initFooterLanguageMenu();
initFooterThemeSelector();
}
export function initGlobalDropdown() {
// do not init "custom" dropdowns, "custom" dropdowns are managed by their own code.
registerGlobalSelectorFunc('.ui.dropdown:not(.custom)', (el) => {

View File

@@ -60,7 +60,7 @@ import {initColorPickers} from './features/colorpicker.ts';
import {initAdminSelfCheck} from './features/admin/selfcheck.ts';
import {initOAuth2SettingsDisableCheckbox} from './features/oauth2-settings.ts';
import {initGlobalFetchAction} from './features/common-fetch-action.ts';
import {initFootLanguageMenu, initGlobalComponent, initGlobalDropdown, initGlobalInput, initHeadNavbarContentToggle} from './features/common-page.ts';
import {initCommmPageComponents, initGlobalComponent, initGlobalDropdown, initGlobalInput} from './features/common-page.ts';
import {initGlobalButtonClickOnEnter, initGlobalButtons, initGlobalDeleteButton} from './features/common-button.ts';
import {initGlobalComboMarkdownEditor, initGlobalEnterQuickSubmit, initGlobalFormDirtyLeaveConfirm} from './features/common-form.ts';
import {callInitFunctions} from './modules/init.ts';
@@ -93,8 +93,7 @@ const initPerformanceTracer = callInitFunctions([
initInstall,
initHeadNavbarContentToggle,
initFootLanguageMenu,
initCommmPageComponents,
initHeatmap,
initImageDiff,

View File

@@ -0,0 +1,13 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<rect width="40" height="40" rx="20" fill="#0000"/>
<path d="M34.2843 34.2842C42.0948 26.4737 42.0948 13.8104 34.2843 5.9999L6 34.2842C13.8105 42.0947 26.4738 42.0947 34.2843 34.2842Z" fill="#0566D5"/>
<path d="M34.2828 34.2842C42.0932 26.4737 42.0932 13.8104 34.2828 5.99995L20.1406 20.1421L34.2828 34.2842Z" fill="#E7A100"/>
<circle cx="20" cy="20" r="18" fill="#0000" stroke="#aaa" stroke-width="4"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="40" height="40" rx="20" fill="#0000"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 678 B

View File

@@ -0,0 +1 @@
<svg viewBox="490 490 820 820"><path d="M866.7 582.1A321.3 321.3 0 0 0 738.6 623a317.3 317.3 0 0 0-109.1 108.5A418 418 0 0 0 609 772a335.3 335.3 0 0 0-19.6 71.5 205.2 205.2 0 0 0-2.8 45c0 25.8.2 29.3 2.8 45 4.1 25.4 9.9 46.4 19.6 71.5a314.2 314.2 0 0 0 111.6 137.3A306.8 306.8 0 0 0 893 1196a308.6 308.6 0 0 0 303.6-262.5c2.6-15.7 2.8-19.2 2.8-45s-.2-29.3-2.8-45A335.3 335.3 0 0 0 1177 772a314.2 314.2 0 0 0-111.6-137.3A308.3 308.3 0 0 0 918 582c-13-1.1-38.2-1.1-51.3.1zM747 663.5l-2.4 16.7c-4 26.4-4.9 41.1-4.3 65.3a323.7 323.7 0 0 0 37.2 145c18.2 36 41.3 66.6 72 95.5a346.4 346.4 0 0 0 208.5 93.1l18 1.6 4.5.5-8.5 8a259.3 259.3 0 0 1-141.5 65.8 281 281 0 0 1-123.9-11.4 267.2 267.2 0 0 1-181.7-269.9c2-27.6 5.7-47.6 13.3-70.7a281.2 281.2 0 0 1 46.4-85c8-10.1 28-30.2 37.9-38.1 13.8-11.1 24.5-18.3 24.5-16.4z"/></svg>

After

Width:  |  Height:  |  Size: 818 B