mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-22 09:00:10 +00:00
setup: flex-ify, light/dark, keep page position on reload
got rid of a bunch of m[l/r/x/y]-x tailwind classes and used more flex-[row/col] gap-2's. UI should be more consistent in general, and with the admin UI. The page you were on is actually read from the URL on reload, however does not keep settings (implemented just for ease of UI editing, really). `missing-colors.js` preprocessor script now applies dark prefixes for <section>s, but like with cards, does not apply a default ~neutral to those without, so that <section class=""> looks different to <section class="~neutral">. Light/dark selector added to setup too, and the actual mode given to the browser through CSS `color-scheme` is correct, meaning things like textareas, checkboxes and controls are now colored according to the theme.
This commit is contained in:
parent
e5f79c60ae
commit
2057823b7a
14
css/base.css
14
css/base.css
@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
--bg-light: #fff;
|
--bg-light: #fff;
|
||||||
--bg-dark: #101010;
|
--bg-dark: #101010;
|
||||||
|
|
||||||
|
color-scheme: light;
|
||||||
}
|
}
|
||||||
|
|
||||||
.light {
|
.light {
|
||||||
@ -26,6 +28,7 @@
|
|||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
--settings-section-button-filter: 80%;
|
--settings-section-button-filter: 80%;
|
||||||
|
color-scheme: dark !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark body {
|
.dark body {
|
||||||
@ -456,3 +459,14 @@ input[type="checkbox" i], [class^="ri-"], [class*=" ri-"], .ri-refresh-line:befo
|
|||||||
margin-bottom: -0.5rem;
|
margin-bottom: -0.5rem;
|
||||||
padding-bottom: 0.5rem;
|
padding-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
section.section:not(.\~neutral) {
|
||||||
|
background-color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer components {
|
||||||
|
.switch input {
|
||||||
|
@apply mr-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
922
html/setup.html
922
html/setup.html
File diff suppressed because it is too large
Load Diff
@ -102,6 +102,7 @@
|
|||||||
"jellyseerr": {
|
"jellyseerr": {
|
||||||
"title": "Jellyseerr",
|
"title": "Jellyseerr",
|
||||||
"description": "Jellyseerr is an alternative to Ombi, and integrates with jfa-go slightly better. Again, after setup is finished, go to Settings to create a profile and add a template for new Jellyseerr accounts.",
|
"description": "Jellyseerr is an alternative to Ombi, and integrates with jfa-go slightly better. Again, after setup is finished, go to Settings to create a profile and add a template for new Jellyseerr accounts.",
|
||||||
|
"importExisting": "Import existing users",
|
||||||
"importExistingDescription": "If enabled, your existing users will have contact details and preferences from jfa-go synchronized."
|
"importExistingDescription": "If enabled, your existing users will have contact details and preferences from jfa-go synchronized."
|
||||||
},
|
},
|
||||||
"messages": {
|
"messages": {
|
||||||
@ -164,6 +165,7 @@
|
|||||||
"helpMessages": {
|
"helpMessages": {
|
||||||
"title": "Help Messages",
|
"title": "Help Messages",
|
||||||
"description": "These messages will display in the account creation page and in some emails.",
|
"description": "These messages will display in the account creation page and in some emails.",
|
||||||
|
"markdownMessageNotice": "Contents of some emails, pages and messages can be customized further with markdown in Settings.",
|
||||||
"contactMessage": "Contact Message",
|
"contactMessage": "Contact Message",
|
||||||
"contactMessageNotice": "Displays at the bottom of all pages except admin.",
|
"contactMessageNotice": "Displays at the bottom of all pages except admin.",
|
||||||
"helpMessage": "Help Message",
|
"helpMessage": "Help Message",
|
||||||
|
@ -33,7 +33,7 @@ function fixHTML(infile, outfile) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let doc = new parser.load(f);
|
let doc = new parser.load(f);
|
||||||
for (let item of ["badge", "chip", "shield", "input", "table", "button", "portal", "select", "aside", "card", "field", "textarea"]) {
|
for (let item of ["badge", "chip", "shield", "input", "table", "button", "portal", "select", "aside", "card", "field", "textarea", "section"]) {
|
||||||
let items = doc("."+item);
|
let items = doc("."+item);
|
||||||
items.each((i, elem) => {
|
items.each((i, elem) => {
|
||||||
let hasColor = false;
|
let hasColor = false;
|
||||||
@ -50,8 +50,8 @@ function fixHTML(infile, outfile) {
|
|||||||
}
|
}
|
||||||
if (!hasColor) {
|
if (!hasColor) {
|
||||||
if (!hasDark(doc(elem))) {
|
if (!hasDark(doc(elem))) {
|
||||||
// card without ~neutral look different than with.
|
// card (and sections in sectioned cards) without ~neutral look different than with.
|
||||||
if (item != "card") doc(elem).addClass("~neutral");
|
if (item != "card" && item != "section") doc(elem).addClass("~neutral");
|
||||||
doc(elem).addClass("dark:~d_neutral");
|
doc(elem).addClass("dark:~d_neutral");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
export class ThemeManager {
|
export class ThemeManager {
|
||||||
|
|
||||||
private _themeButton: HTMLElement = null;
|
private _themeButton: HTMLElement = null;
|
||||||
|
private _metaTag: HTMLMetaElement;
|
||||||
|
|
||||||
private _beforeTransition = () => {
|
private _beforeTransition = () => {
|
||||||
const doc = document.documentElement;
|
const doc = document.documentElement;
|
||||||
@ -45,6 +46,7 @@ export class ThemeManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(button?: HTMLElement) {
|
constructor(button?: HTMLElement) {
|
||||||
|
this._metaTag = document.querySelector("meta[name=color-scheme]") as HTMLMetaElement;
|
||||||
const theme = localStorage.getItem("theme");
|
const theme = localStorage.getItem("theme");
|
||||||
if (theme == "dark") {
|
if (theme == "dark") {
|
||||||
this._enable(true);
|
this._enable(true);
|
||||||
@ -54,18 +56,22 @@ export class ThemeManager {
|
|||||||
this._enable(true);
|
this._enable(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arguments.length == 1)
|
if (button)
|
||||||
this.bindButton(button);
|
this.bindButton(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _toggle = () => {
|
private _toggle = () => {
|
||||||
|
let metaValue = "light dark";
|
||||||
this._beforeTransition();
|
this._beforeTransition();
|
||||||
if (!document.documentElement.classList.contains('dark')) {
|
if (!document.documentElement.classList.contains('dark')) {
|
||||||
document.documentElement.classList.add('dark');
|
document.documentElement.classList.add('dark');
|
||||||
|
metaValue = "dark light";
|
||||||
} else {
|
} else {
|
||||||
document.documentElement.classList.remove('dark');
|
document.documentElement.classList.remove('dark');
|
||||||
}
|
}
|
||||||
localStorage.setItem('theme', document.documentElement.classList.contains('dark') ? "dark" : "light");
|
localStorage.setItem('theme', document.documentElement.classList.contains('dark') ? "dark" : "light");
|
||||||
|
|
||||||
|
// this._metaTag.setAttribute("content", metaValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
private _enable = (dark: boolean) => {
|
private _enable = (dark: boolean) => {
|
||||||
@ -80,6 +86,8 @@ export class ThemeManager {
|
|||||||
document.documentElement.classList.remove(opposite);
|
document.documentElement.classList.remove(opposite);
|
||||||
}
|
}
|
||||||
document.documentElement.classList.add(mode);
|
document.documentElement.classList.add(mode);
|
||||||
|
|
||||||
|
// this._metaTag.setAttribute("content", `${mode} ${opposite}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
enable = (dark: boolean) => {
|
enable = (dark: boolean) => {
|
||||||
|
108
ts/setup.ts
108
ts/setup.ts
@ -1,5 +1,6 @@
|
|||||||
import { _get, _post, toggleLoader, notificationBox } from "./modules/common.js";
|
import { _get, _post, toggleLoader, notificationBox } from "./modules/common.js";
|
||||||
import { lang, LangFile, loadLangSelector } from "./modules/lang.js";
|
import { lang, LangFile, loadLangSelector } from "./modules/lang.js";
|
||||||
|
import { ThemeManager } from "./modules/theme.js";
|
||||||
|
|
||||||
interface sWindow extends Window {
|
interface sWindow extends Window {
|
||||||
messages: {};
|
messages: {};
|
||||||
@ -8,6 +9,7 @@ interface sWindow extends Window {
|
|||||||
declare var window: sWindow;
|
declare var window: sWindow;
|
||||||
window.URLBase = "";
|
window.URLBase = "";
|
||||||
|
|
||||||
|
const theme = new ThemeManager(document.getElementById("button-theme"));
|
||||||
|
|
||||||
window.notifications = new notificationBox(document.getElementById('notification-box') as HTMLDivElement, 5);
|
window.notifications = new notificationBox(document.getElementById('notification-box') as HTMLDivElement, 5);
|
||||||
|
|
||||||
@ -68,7 +70,16 @@ class Checkbox {
|
|||||||
constructor(el: HTMLElement, depends?: string, dependsTrue?: boolean, section?: string, setting?: string) {
|
constructor(el: HTMLElement, depends?: string, dependsTrue?: boolean, section?: string, setting?: string) {
|
||||||
this._el = el as HTMLInputElement;
|
this._el = el as HTMLInputElement;
|
||||||
this._hideEl = this._el as HTMLElement;
|
this._hideEl = this._el as HTMLElement;
|
||||||
if (this._hideEl.parentElement.tagName == "LABEL") { this._hideEl = this._hideEl.parentElement; }
|
if (this._hideEl.parentElement.tagName == "LABEL") {
|
||||||
|
this._hideEl = this._hideEl.parentElement;
|
||||||
|
} else if (this._hideEl.parentElement.classList.contains("switch")) {
|
||||||
|
if (this._hideEl.parentElement.parentElement.tagName == "LABEL") {
|
||||||
|
this._hideEl = this._hideEl.parentElement.parentElement;
|
||||||
|
} else {
|
||||||
|
this._hideEl = this._hideEl.parentElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (section && setting) {
|
if (section && setting) {
|
||||||
this._section = section;
|
this._section = section;
|
||||||
this._setting = setting;
|
this._setting = setting;
|
||||||
@ -86,11 +97,11 @@ class Checkbox {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._el.hasAttribute("checked")) {
|
/* if (this._el.hasAttribute("checked")) {
|
||||||
this._el.checked = true;
|
this._el.checked = true;
|
||||||
} else {
|
} else {
|
||||||
this._el.checked = false;
|
this._el.checked = false;
|
||||||
}
|
} */
|
||||||
this.broadcast();
|
this.broadcast();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,7 +228,7 @@ class LangSelect extends Select {
|
|||||||
}
|
}
|
||||||
this.value = "en-us";
|
this.value = "en-us";
|
||||||
}
|
}
|
||||||
});
|
}, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -448,23 +459,32 @@ settings["email"]["method"].onchange = emailMethodChange;
|
|||||||
settings["messages"]["enabled"].onchange = emailMethodChange;
|
settings["messages"]["enabled"].onchange = emailMethodChange;
|
||||||
emailMethodChange();
|
emailMethodChange();
|
||||||
|
|
||||||
|
const getParentCard = (el: HTMLElement): HTMLDivElement => {
|
||||||
|
let pEl = el.parentElement;
|
||||||
|
while (pEl.tagName != "html") {
|
||||||
|
if (pEl.classList.contains("card")) return pEl as HTMLDivElement;
|
||||||
|
pEl = pEl.parentElement;
|
||||||
|
}
|
||||||
|
return pEl as HTMLDivElement;
|
||||||
|
};
|
||||||
|
|
||||||
const jellyfinLoginAccessChange = () => {
|
const jellyfinLoginAccessChange = () => {
|
||||||
const adminOnly = settings["ui"]["admin_only"].value == "true";
|
const adminOnly = settings["ui"]["admin_only"].value == "true";
|
||||||
const allowAll = settings["ui"]["allow_all"].value == "true";
|
const allowAll = settings["ui"]["allow_all"].value == "true";
|
||||||
const adminOnlyEl = document.getElementById("ui-admin_only") as HTMLInputElement;
|
const adminOnlyEl = document.getElementById("ui-admin_only") as HTMLInputElement;
|
||||||
const allowAllEls = [document.getElementById("ui-allow_all"), document.getElementById("description-ui-allow_all")];
|
const allowAllEl = document.getElementById("ui-allow_all") as HTMLInputElement;
|
||||||
const nextButton = adminOnlyEl.parentElement.parentElement.parentElement.querySelector("span.next") as HTMLSpanElement;
|
const nextButton = getParentCard(adminOnlyEl).querySelector("span.next") as HTMLSpanElement;
|
||||||
if (adminOnly && !allowAll) {
|
if (adminOnly && !allowAll) {
|
||||||
(allowAllEls[0] as HTMLInputElement).disabled = true;
|
allowAllEl.disabled = true;
|
||||||
adminOnlyEl.disabled = false;
|
adminOnlyEl.disabled = false;
|
||||||
nextButton.removeAttribute("disabled");
|
nextButton.removeAttribute("disabled");
|
||||||
} else if (!adminOnly && allowAll) {
|
} else if (!adminOnly && allowAll) {
|
||||||
adminOnlyEl.disabled = true;
|
adminOnlyEl.disabled = true;
|
||||||
(allowAllEls[0] as HTMLInputElement).disabled = false;
|
allowAllEl.disabled = false;
|
||||||
nextButton.removeAttribute("disabled");
|
nextButton.removeAttribute("disabled");
|
||||||
} else {
|
} else {
|
||||||
adminOnlyEl.disabled = false;
|
adminOnlyEl.disabled = false;
|
||||||
(allowAllEls[0] as HTMLInputElement).disabled = false;
|
allowAllEl.disabled = false;
|
||||||
nextButton.setAttribute("disabled", "true")
|
nextButton.setAttribute("disabled", "true")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -495,18 +515,17 @@ for (let section in settings) {
|
|||||||
|
|
||||||
const pageNames: string[][] = [];
|
const pageNames: string[][] = [];
|
||||||
|
|
||||||
window.history.replaceState("welcome", "Setup - jfa-go");
|
(() => {
|
||||||
|
const pushState = window.history.pushState;
|
||||||
|
window.history.pushState = function (data: any, __: string, _: string | URL) {
|
||||||
|
pushState.apply(window.history, arguments);
|
||||||
|
let ev = { state: data as string } as PopStateEvent;
|
||||||
|
window.onpopstate(ev);
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
const changePage = (title: string, pageTitle: string) => {
|
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
|
||||||
const lang = urlParams.get("lang");
|
|
||||||
let page = "/#" + title;
|
|
||||||
if (lang) { page += "?lang=" + lang; }
|
|
||||||
window.history.pushState(title || "welcome", pageTitle, page);
|
|
||||||
};
|
|
||||||
const cards = Array.from(document.getElementById("page-container").getElementsByClassName("card")) as Array<HTMLDivElement>;
|
|
||||||
(window as any).cards = cards;
|
|
||||||
window.onpopstate = (event: PopStateEvent) => {
|
window.onpopstate = (event: PopStateEvent) => {
|
||||||
|
console.log("CALLLLLLLL", event);
|
||||||
if (event.state === "welcome") {
|
if (event.state === "welcome") {
|
||||||
cards[0].classList.remove("unfocused");
|
cards[0].classList.remove("unfocused");
|
||||||
for (let i = 1; i < cards.length; i++) { cards[i].classList.add("unfocused"); }
|
for (let i = 1; i < cards.length; i++) { cards[i].classList.add("unfocused"); }
|
||||||
@ -521,6 +540,35 @@ window.onpopstate = (event: PopStateEvent) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const cards = Array.from(document.getElementsByClassName("page-container")[0].querySelectorAll(".card.sectioned")) as Array<HTMLDivElement>;
|
||||||
|
(window as any).cards = cards;
|
||||||
|
|
||||||
|
const changePageToIndex = (title: string, pageTitle: string, i: number) => {
|
||||||
|
cards[i].classList.remove("unfocused");
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const lang = urlParams.get("lang");
|
||||||
|
let page = "/#" + title;
|
||||||
|
if (lang) { page += "?lang=" + lang; }
|
||||||
|
console.log("pushing", title, pageTitle, page);
|
||||||
|
window.history.pushState(title || "welcome", pageTitle, page);
|
||||||
|
};
|
||||||
|
|
||||||
|
const changePage = (title: string, pageTitle: string) => {
|
||||||
|
let found = false;
|
||||||
|
for (let i = 0; i < cards.length; i++) {
|
||||||
|
if (!found && pageNames[i][0] == title && !(cards[i].classList.contains("hidden"))) {
|
||||||
|
found = true;
|
||||||
|
changePageToIndex(title, pageTitle, i);
|
||||||
|
} else {
|
||||||
|
cards[i].classList.add("unfocused");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
changePageToIndex(title, pageTitle, 0);
|
||||||
|
}
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
};
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
for (let i = 0; i < cards.length; i++) {
|
for (let i = 0; i < cards.length; i++) {
|
||||||
const card = cards[i];
|
const card = cards[i];
|
||||||
@ -534,36 +582,34 @@ window.onpopstate = (event: PopStateEvent) => {
|
|||||||
let pageTitle = titleEl.textContent + " - jfa-go";
|
let pageTitle = titleEl.textContent + " - jfa-go";
|
||||||
pageNames.push([title, pageTitle]);
|
pageNames.push([title, pageTitle]);
|
||||||
if (back) { back.addEventListener("click", () => {
|
if (back) { back.addEventListener("click", () => {
|
||||||
let found = false;
|
|
||||||
for (let ind = cards.length - 1; ind >= 0; ind--) {
|
for (let ind = cards.length - 1; ind >= 0; ind--) {
|
||||||
cards[ind].classList.add("unfocused");
|
if (ind < i && !(cards[ind].classList.contains("hidden"))) {
|
||||||
if (ind < i && !(cards[ind].classList.contains("hidden")) && !found) {
|
|
||||||
cards[ind].classList.remove("unfocused");
|
|
||||||
changePage(pageNames[ind][0], pageNames[ind][1]);
|
changePage(pageNames[ind][0], pageNames[ind][1]);
|
||||||
found = true;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
window.scrollTo(0, 0);
|
|
||||||
}); }
|
}); }
|
||||||
if (next) {
|
if (next) {
|
||||||
const func = () => {
|
const func = () => {
|
||||||
if (next.hasAttribute("disabled")) return;
|
if (next.hasAttribute("disabled")) return;
|
||||||
let found = false;
|
|
||||||
for (let ind = 0; ind < cards.length; ind++) {
|
for (let ind = 0; ind < cards.length; ind++) {
|
||||||
cards[ind].classList.add("unfocused");
|
if (ind > i && !(cards[ind].classList.contains("hidden"))) {
|
||||||
if (ind > i && !(cards[ind].classList.contains("hidden")) && !found) {
|
|
||||||
cards[ind].classList.remove("unfocused");
|
|
||||||
changePage(pageNames[ind][0], pageNames[ind][1]);
|
changePage(pageNames[ind][0], pageNames[ind][1]);
|
||||||
found = true;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
window.scrollTo(0, 0);
|
|
||||||
};
|
};
|
||||||
next.addEventListener("click", func)
|
next.addEventListener("click", func)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
(() => {
|
||||||
|
let initialLocation = window.location.hash.replace("#", "") || "welcome";
|
||||||
|
changePage(initialLocation, "Setup - jfa-go");
|
||||||
|
})();
|
||||||
|
// window.history.replaceState("welcome", "Setup - jfa-go",);
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
const button = document.getElementById("jellyfin-test-connection") as HTMLSpanElement;
|
const button = document.getElementById("jellyfin-test-connection") as HTMLSpanElement;
|
||||||
const ogText = button.textContent;
|
const ogText = button.textContent;
|
||||||
|
Loading…
Reference in New Issue
Block a user