mirror of
https://github.com/transmission/transmission.git
synced 2026-02-15 07:26:49 +00:00
feat: new window "Appearance settings" for web app (#7318)
* feat: new window "Appearance settings" for web app * feat: new window "Appearance settings" for web app
This commit is contained in:
@@ -2,6 +2,7 @@ set(WEB_SOURCES
|
||||
src/about-dialog.js
|
||||
src/action-manager.js
|
||||
src/alert-dialog.js
|
||||
src/appearance-settings.js
|
||||
src/context-menu.js
|
||||
src/file-row.js
|
||||
src/formatter.js
|
||||
|
||||
@@ -30,11 +30,11 @@
|
||||
--black: #000;
|
||||
--blue-100: #51b3f7;
|
||||
--blue-200: #357aaa;
|
||||
--blue-300: #2c7fea;
|
||||
--blue-400: #1847d4;
|
||||
--dark-mode-black: #121212;
|
||||
--dark-mode-white: #c9d1d9;
|
||||
--default-accent-color-dark: #0c2d6b;
|
||||
--default-accent-color: #cdcdff;
|
||||
--default-accent-color-strong: #b0b0ff;
|
||||
--default-border-dark: #575757;
|
||||
--default-border-light: #aeaeae;
|
||||
--default-tinted: rgba(128, 128, 144, 0.125);
|
||||
@@ -92,7 +92,7 @@
|
||||
--color-bg-odd: var(--black);
|
||||
--color-bg-popup: var(--black);
|
||||
--color-bg-primary: var(--black);
|
||||
--color-bg-selected: var(--default-accent-color-dark);
|
||||
--color-bg-selected: var(--default-accent-color-strong);
|
||||
--color-bg-warn: #cf6679;
|
||||
--color-border-default: var(--default-border-dark);
|
||||
--color-border-stark: var(--dark-mode-white);
|
||||
@@ -101,7 +101,7 @@
|
||||
--color-fg-on-popup: var(--nice-grey);
|
||||
--color-fg-primary: var(--dark-mode-white);
|
||||
--color-fg-secondary: var(--nice-grey);
|
||||
--color-fg-selected: var(--dark-mode-white);
|
||||
--color-fg-selected: #404040;
|
||||
--color-fg-tertiary: var(--grey-500);
|
||||
--color-fg-warn: var(--dark-mode-black);
|
||||
--color-progressbar-fg-1: #edefff;
|
||||
@@ -118,7 +118,6 @@
|
||||
.contrast-more {
|
||||
--color-bg-even: var(--black);
|
||||
--color-bg-hover: var(--grey-40);
|
||||
--color-bg-selected: var(--blue-300);
|
||||
--color-bg-tabs: var(--black);
|
||||
--color-bg-warn: #cf6679;
|
||||
--color-border-default: var(--white);
|
||||
@@ -131,7 +130,7 @@
|
||||
--color-fg-port-open: var(--green-100);
|
||||
--color-fg-primary: var(--white);
|
||||
--color-fg-secondary: var(--white);
|
||||
--color-fg-selected: var(--white);
|
||||
--color-fg-selected: var(--black);
|
||||
--color-fg-tabs: var(--white);
|
||||
--color-fg-tertiary: var(--white);
|
||||
--color-fg-warn: var(--black);
|
||||
@@ -155,7 +154,7 @@
|
||||
--color-bg-odd: var(--white);
|
||||
--color-bg-popup: var(--white);
|
||||
--color-bg-primary: var(--white);
|
||||
--color-bg-selected: var(--blue-300);
|
||||
--color-bg-selected: var(--default-accent-color);
|
||||
--color-bg-warn: #e4606d5b;
|
||||
--color-border-default: var(--default-border-light);
|
||||
--color-border-stark: var(--grey-500);
|
||||
@@ -168,7 +167,7 @@
|
||||
--color-fg-port-open: var(--green-400);
|
||||
--color-fg-primary: #404040;
|
||||
--color-fg-secondary: var(--grey-500);
|
||||
--color-fg-selected: var(--nice-grey);
|
||||
--color-fg-selected: #404040;
|
||||
--color-fg-tertiary: var(--grey-500);
|
||||
--color-fg-warn: #cf212e;
|
||||
--color-progressbar-fg-1: #303030;
|
||||
@@ -189,7 +188,7 @@
|
||||
.contrast-more {
|
||||
--color-bg-even: var(--white);
|
||||
--color-bg-hover: var(--grey-40);
|
||||
--color-bg-selected: var(--blue-300);
|
||||
--color-bg-selected: var(--default-accent-color-strong);
|
||||
--color-bg-tabs: var(--white);
|
||||
--color-bg-warn: #cf6679;
|
||||
--color-border-default: var(--black);
|
||||
@@ -202,7 +201,7 @@
|
||||
--color-fg-port-open: var(--green-400);
|
||||
--color-fg-primary: var(--black);
|
||||
--color-fg-secondary: var(--black);
|
||||
--color-fg-selected: var(--white);
|
||||
--color-fg-selected: var(--black);
|
||||
--color-fg-tabs: var(--black);
|
||||
--color-fg-tertiary: var(--black);
|
||||
--color-fg-warn: var(--white);
|
||||
@@ -236,6 +235,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
@supports (background-color: Highlight) {
|
||||
:root {
|
||||
body:not(.highlight-legacy) {
|
||||
--color-bg-selected: Highlight;
|
||||
--color-fg-selected: HighlightText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@supports (background-color: AccentColor) {
|
||||
:root {
|
||||
body:not(.highlight-legacy):not(.highlight-system) {
|
||||
--color-bg-selected: AccentColor;
|
||||
--color-fg-selected: #f8f8f8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
background-color: var(--color-bg-primary);
|
||||
@@ -248,6 +265,15 @@ body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,10 @@ export class ActionManager extends EventTarget {
|
||||
'move-down': { enabled: false, text: 'Down' },
|
||||
'move-top': { enabled: false, text: 'Top' },
|
||||
'move-up': { enabled: false, text: 'Up' },
|
||||
'open-appearance-settings': {
|
||||
enabled: true,
|
||||
text: 'Appearance settings',
|
||||
},
|
||||
'open-torrent': {
|
||||
enabled: true,
|
||||
shortcut: 'O',
|
||||
|
||||
140
web/src/appearance-settings.js
Normal file
140
web/src/appearance-settings.js
Normal file
@@ -0,0 +1,140 @@
|
||||
/* @license This file Copyright © Mnemosyne LLC.
|
||||
It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
||||
or any future license endorsed by Mnemosyne LLC.
|
||||
License text can be found in the licenses/ folder. */
|
||||
|
||||
import { Prefs } from './prefs.js';
|
||||
import { createDialogContainer, makeUUID } from './utils.js';
|
||||
|
||||
export class Appearance extends EventTarget {
|
||||
constructor(prefs, action_manager) {
|
||||
super();
|
||||
|
||||
this.action_manager = action_manager;
|
||||
|
||||
this.prefs_listener = this._onPrefsChange.bind(this);
|
||||
this.prefs = prefs;
|
||||
this.prefs.addEventListener('change', this.prefs_listener);
|
||||
|
||||
this.elements = this._create();
|
||||
this.elements.dismiss.addEventListener('click', () => this.close());
|
||||
document.body.append(this.elements.root);
|
||||
this.elements.dismiss.focus();
|
||||
}
|
||||
|
||||
close() {
|
||||
this.elements.root.remove();
|
||||
this.dispatchEvent(new Event('close'));
|
||||
delete this.elements;
|
||||
}
|
||||
|
||||
_onPrefsChange(event_) {
|
||||
switch (event_.key) {
|
||||
case Prefs.SortDirection:
|
||||
case Prefs.SortMode:
|
||||
this.root.querySelector(`[data-pref="${event_.key}"]`).value =
|
||||
event_.value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_create() {
|
||||
const elements = createDialogContainer('dis-appearance');
|
||||
const { dismiss, heading, message } = elements;
|
||||
|
||||
heading.textContent = 'Appearance';
|
||||
dismiss.textContent = 'Close';
|
||||
|
||||
// contrast
|
||||
|
||||
let legend = document.createElement('h4');
|
||||
message.append(legend);
|
||||
legend.textContent = 'Theme';
|
||||
|
||||
let div = document.createElement('div');
|
||||
div.classList.add('table-row');
|
||||
message.append(div);
|
||||
|
||||
const add_check = (text, listener) => {
|
||||
const check = document.createElement('input');
|
||||
check.id = makeUUID();
|
||||
check.type = 'checkbox';
|
||||
|
||||
const label = document.createElement('label');
|
||||
label.htmlFor = check.id;
|
||||
label.textContent = text;
|
||||
|
||||
div.append(check, label);
|
||||
listener(check);
|
||||
};
|
||||
|
||||
const add_radio = (name, text, className, style, listener) => {
|
||||
const input = document.createElement('input');
|
||||
input.id = makeUUID();
|
||||
input.name = name;
|
||||
input.type = 'radio';
|
||||
input.value = style;
|
||||
|
||||
const label = document.createElement('label');
|
||||
label.htmlFor = input.id;
|
||||
label.textContent = text;
|
||||
|
||||
div.append(input, label, document.createElement('BR'));
|
||||
|
||||
listener(input, className);
|
||||
};
|
||||
|
||||
let listener = (e) => {
|
||||
e.checked = this.prefs.contrast_mode === Prefs.ContrastMore;
|
||||
e.addEventListener('change', (event_) => {
|
||||
const { checked } = event_.target;
|
||||
this.prefs.contrast_mode = checked
|
||||
? Prefs.ContrastMore
|
||||
: Prefs.ContrastLess;
|
||||
});
|
||||
};
|
||||
|
||||
add_check(this.action_manager.text('toggle-contrast'), listener);
|
||||
|
||||
// highlight color
|
||||
|
||||
legend = document.createElement('h4');
|
||||
message.append(legend);
|
||||
legend.textContent = 'Highlight color';
|
||||
|
||||
div = document.createElement('div');
|
||||
div.classList.add('table-row');
|
||||
message.append(div);
|
||||
|
||||
listener = (e, className) => {
|
||||
e.checked = !className || document.body.classList.contains(className);
|
||||
e.addEventListener('change', (event_) => {
|
||||
const { value } = event_.target;
|
||||
this.prefs.highlight_color = value;
|
||||
});
|
||||
};
|
||||
|
||||
add_radio(
|
||||
'highlight-color',
|
||||
'Accent color from system',
|
||||
null,
|
||||
'AccentColor',
|
||||
listener,
|
||||
);
|
||||
add_radio(
|
||||
'highlight-color',
|
||||
'Highlight color from system',
|
||||
'highlight-system',
|
||||
'Highlight',
|
||||
listener,
|
||||
);
|
||||
add_radio('highlight-color', 'Legacy', 'highlight-legacy', null, listener);
|
||||
|
||||
elements.confirm.remove();
|
||||
delete elements.confirm;
|
||||
|
||||
return elements;
|
||||
}
|
||||
}
|
||||
@@ -244,24 +244,6 @@ export class OverflowMenu extends EventTarget {
|
||||
|
||||
add_checkbox(this.action_manager.text('toggle-compact-rows'), listener);
|
||||
|
||||
// contrast
|
||||
|
||||
div = document.createElement('div');
|
||||
div.classList.add('table-row');
|
||||
options.append(div);
|
||||
|
||||
listener = (e) => {
|
||||
e.checked = this.prefs.contrast_mode === Prefs.ContrastMore;
|
||||
e.addEventListener('change', (event_) => {
|
||||
const { checked } = event_.target;
|
||||
this.prefs.contrast_mode = checked
|
||||
? Prefs.ContrastMore
|
||||
: Prefs.ContrastLess;
|
||||
});
|
||||
};
|
||||
|
||||
add_checkbox(this.action_manager.text('toggle-contrast'), listener);
|
||||
|
||||
// fullscreen
|
||||
|
||||
div = document.createElement('div');
|
||||
@@ -286,6 +268,19 @@ export class OverflowMenu extends EventTarget {
|
||||
|
||||
add_checkbox('Fullscreen', listener);
|
||||
|
||||
// appearance
|
||||
|
||||
const appearance_text = this.action_manager.text(
|
||||
'open-appearance-settings',
|
||||
);
|
||||
div = make_button(
|
||||
section,
|
||||
appearance_text,
|
||||
'open-appearance-settings',
|
||||
on_click,
|
||||
);
|
||||
options.append(div);
|
||||
|
||||
// SPEED LIMIT
|
||||
|
||||
section = make_section('speed', 'Speed Limit');
|
||||
|
||||
@@ -113,6 +113,7 @@ Prefs.FilterPaused = 'paused';
|
||||
Prefs.FilterPrivate = 'private';
|
||||
Prefs.FilterPublic = 'public';
|
||||
Prefs.FilterSeeding = 'seeding';
|
||||
Prefs.HighlightColor = 'highlight-color';
|
||||
Prefs.NotificationsEnabled = 'notifications-enabled';
|
||||
Prefs.RefreshRate = 'refresh-rate-sec';
|
||||
Prefs.SortAscending = 'ascending';
|
||||
@@ -136,6 +137,7 @@ Prefs._Defaults = {
|
||||
? Prefs.ContrastMore
|
||||
: Prefs.ContrastLess,
|
||||
[Prefs.FilterMode]: Prefs.FilterAll,
|
||||
[Prefs.HighlightColor]: 'AccentColor',
|
||||
[Prefs.NotificationsEnabled]: false,
|
||||
[Prefs.RefreshRate]: 5,
|
||||
[Prefs.SortDirection]: Prefs.SortAscending,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
License text can be found in the licenses/ folder. */
|
||||
|
||||
import { AboutDialog } from './about-dialog.js';
|
||||
import { Appearance } from './appearance-settings.js';
|
||||
import { ContextMenu } from './context-menu.js';
|
||||
import { Formatter } from './formatter.js';
|
||||
import { Inspector } from './inspector.js';
|
||||
@@ -141,6 +142,17 @@ export class Transmission extends EventTarget {
|
||||
case 'move-up':
|
||||
this._moveUp();
|
||||
break;
|
||||
case 'open-appearance-settings':
|
||||
if (
|
||||
this.popup[Transmission.default_popup_level] instanceof Appearance
|
||||
) {
|
||||
this.popup[Transmission.default_popup_level].close();
|
||||
} else {
|
||||
this.setCurrentPopup(
|
||||
new Appearance(this.prefs, this.action_manager),
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'open-torrent':
|
||||
this.setCurrentPopup(new OpenDialog(this, this.remote));
|
||||
break;
|
||||
@@ -405,7 +417,6 @@ export class Transmission extends EventTarget {
|
||||
// Add custom class to the body/html element to get the appropriate contrast color scheme
|
||||
document.body.classList.remove('contrast-more', 'contrast-less');
|
||||
document.body.classList.add(`contrast-${value}`);
|
||||
// this.refilterAllSoon();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -415,6 +426,16 @@ export class Transmission extends EventTarget {
|
||||
this.refilterAllSoon();
|
||||
break;
|
||||
|
||||
case Prefs.HighlightColor: {
|
||||
document.body.classList.remove('highlight-legacy', 'highlight-system');
|
||||
if (!value) {
|
||||
document.body.classList.add('highlight-legacy');
|
||||
} else if (value === 'Highlight') {
|
||||
document.body.classList.add('highlight-system');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Prefs.RefreshRate: {
|
||||
clearInterval(this.refreshTorrentsInterval);
|
||||
const callback = this.refreshTorrents.bind(this);
|
||||
|
||||
Reference in New Issue
Block a user