Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT] Split the long list of options into categories within a tab control #1021

Merged
merged 11 commits into from
Aug 7, 2024
184 changes: 184 additions & 0 deletions src/gui/Tabs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/*
* 'Shell commands' plugin for Obsidian.
* Copyright (C) 2021 - 2023 Jarkko Linnanvirta
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.0 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* Contact the author (Jarkko Linnanvirta): https://github.com/Taitava/
*/

import { setIcon } from "obsidian";

export interface Tab {
title: string;
icon: string;
content_generator: (container_element: HTMLElement) => Promise<void>;
}

export interface TabStructure {
header: HTMLElement;
active_tab_id: string;
buttons: {
[key: string]: HTMLElement;
};
contentContainers: {
[key: string]: HTMLElement;
};
contentGeneratorPromises: {
[key: string]: Promise<void>;
};
}

export interface Tabs {
[key: string]: Tab;
}

interface TabContentContainers {
[key: string]: HTMLElement;
}

interface TabButtons {
[key: string]: HTMLElement;
}

export function createTabs(
container_element: HTMLElement,
tabs: Tabs,
activateTabId: string,
): TabStructure {
const tab_header = container_element.createEl("div", { attr: { class: "sr-tab-header" } });
const tab_content_containers: TabContentContainers = {};
const tab_buttons: TabButtons = {};
const tab_structure: TabStructure = {
header: tab_header,
active_tab_id: Object.keys(tabs)[0] as string, // Indicate that the first tab is active. This does not affect what tab is active in practise, it just reports the active tab.
buttons: tab_buttons,
contentContainers: tab_content_containers,
contentGeneratorPromises: {},
};
let first_button: HTMLElement | undefined;
for (const tab_id in tabs) {
const tab = tabs[tab_id];

// Create button
const button = tab_header.createEl("button", {
attr: {
class: "sr-tab-header-button",
activateTab: "sr-tab-" + tab_id,
},
});
button.onclick = function (event: MouseEvent) {
const tab_button = this as HTMLElement; // Use 'this' instead of event.target because this way we'll always get a button element, not an element inside the button (i.e. an icon).

// Hide all tab contents and get the max dimensions
let max_width = 0;
let max_height = 0;
const tab_header = tab_button.parentElement;
if (null === tab_header) {
throw new Error("Tab header is missing. Did not get a parent from tab button.");
}
const container_element = tab_header.parentElement;
if (null === container_element) {
throw new Error(
"Container element is missing. Did not get a parent from tab header.",
);
}
const tab_contents = container_element.findAll("div.sr-tab-content"); // Do not get all tab contents that exist, because there might be multiple tab systems open at the same time.
const is_main_settings_modal = container_element.hasClass("vertical-tab-content");
for (const index in tab_contents) {
const tab_content = tab_contents[index];

// Get the maximum tab dimensions so that all tabs can have the same dimensions.
// But don't do it if this is the main settings modal
if (!is_main_settings_modal) {
tab_content.addClass("sr-tab-active"); // Need to make the tab visible temporarily in order to get the dimensions.
if (tab_content.offsetHeight > max_height) {
max_height = tab_content.offsetHeight;
}
if (tab_content.offsetWidth > max_width) {
max_width = tab_content.offsetWidth;
}
}

// Finally hide the tab
tab_content.removeClass("sr-tab-active");
}

// Remove active status from all buttons
const adjacent_tab_buttons = tab_header.findAll(".sr-tab-header-button"); // Do not get all tab buttons that exist, because there might be multiple tab systems open at the same time.
for (const index in adjacent_tab_buttons) {
const tab_button = adjacent_tab_buttons[index];
tab_button.removeClass("sr-tab-active");
}

// Activate the clicked tab
tab_button.addClass("sr-tab-active");
const activateTabAttribute: Attr | null =
tab_button.attributes.getNamedItem("activateTab");
if (null === activateTabAttribute) {
throw new Error("Tab button has no 'activateTab' HTML attribute! Murr!");
}
const activate_tab_id = activateTabAttribute.value;
const tab_content: HTMLElement | null = document.getElementById(activate_tab_id);
if (null === tab_content) {
throw new Error(
"No tab content was found with activate_tab_id '" +
activate_tab_id +
"'! Hmph!",
);
}
tab_content.addClass("sr-tab-active");

// Mark the clicked tab as active in TabStructure (just to report which tab is currently active)
tab_structure.active_tab_id = activate_tab_id.replace(/^sr-tab-/, ""); // Remove "sr-tab" prefix.

// Focus an element (if a focusable element is present)
tab_content.find(".sr-focus-element-on-tab-opening")?.focus(); // ? = If not found, do nothing.

// Apply the max dimensions to this tab
// But don't do it if this is the main settings modal
if (!is_main_settings_modal) {
tab_content.style.width = max_width + "px";
tab_content.style.height = max_height + "px";
}

// Do nothing else (I don't know if this is needed or not)
event.preventDefault();
};
if (tab.icon) setIcon(button, tab.icon);

button.insertAdjacentText("beforeend", " " + tab.title);
tab_buttons[tab_id] = button;

// Create content container
tab_content_containers[tab_id] = container_element.createEl("div", {
attr: { class: "sr-tab-content", id: "sr-tab-" + tab_id },
});

// Generate content
tab_structure.contentGeneratorPromises[tab_id] = tab.content_generator(
tab_content_containers[tab_id],
);

// Memorize the first tab's button
if (undefined === first_button) {
first_button = button;
}
}

// Open a tab.
tab_buttons[activateTabId].click();

// Return the TabStructure
return tab_structure;
}
22 changes: 20 additions & 2 deletions src/lang/locale/ar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,28 @@ export default {

// settings.ts
SETTINGS_HEADER: "Spaced Repetition Plugin - Settings",
GROUP_TAGS_FOLDERS: "Tags & Folders",
GROUP_FLASHCARD_REVIEW: "Flashcard Review",
GROUP_FLASHCARD_SEPARATORS: "Flashcard Separators",
GROUP_DATA_STORAGE: "Storage of Scheduling Data",
GROUP_FLASHCARDS_NOTES: "Flashcards & Notes",
GROUP_CONTRIBUTING: "Contributing",
CHECK_WIKI: '.<a href="${wiki_url}">wiki</a> لمزيد من المعلومات ، تحقق من',
GITHUB_DISCUSSIONS:
'Visit the <a href="${discussions_url}">discussions</a> section for Q&A help, feedback, and general discussion.',
GITHUB_ISSUES:
'Raise an issue <a href="${issues_url}">here</a> if you have a feature request or a bug-report.',
GITHUB_SOURCE_CODE:
'Project source code available on <a href="${github_project_url}">GitHub</a>',
CODE_CONTRIBUTION_INFO:
'Information on <a href="${code_contribution_url}">code contributions</a>',
TRANSLATION_CONTRIBUTION_INFO:
'Information on <a href="${translation_contribution_url}">translating the plugin</a> to your language',
PROJECT_CONTRIBUTIONS:
'Raise an issue <a href="${issues_url}">here</a> if you have a feature request or a bug-report',
FOLDERS_TO_IGNORE: "مجلدات لتجاهلها",
FOLDERS_TO_IGNORE_DESC:
"Templates Meta/Scripts : أدخل مسارات المجلد مفصولة بواسطة سطور جديدة,مثال",
FOLDERS_TO_IGNORE_DESC: `Templates Meta/Scripts.
Note that this setting is common to both Flashcards and Notes. : أدخل مسارات المجلد مفصولة بواسطة سطور جديدة,مثال`,
FLASHCARDS: "البطاقات",
FLASHCARD_EASY_LABEL: "نص الزر سهل",
FLASHCARD_GOOD_LABEL: "نص الزر جيد",
Expand Down
24 changes: 21 additions & 3 deletions src/lang/locale/cz.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,29 @@ export default {
YEARS_STR_IVL_MOBILE: "${interval}r",

// settings.ts
SETTINGS_HEADER: "Spaced Repetition Plugin - Nastavení",
SETTINGS_HEADER: "Spaced Repetition - Nastavení",
GROUP_TAGS_FOLDERS: "Tags & Folders",
GROUP_FLASHCARD_REVIEW: "Flashcard Review",
GROUP_FLASHCARD_SEPARATORS: "Flashcard Separators",
GROUP_DATA_STORAGE: "Storage of Scheduling Data",
GROUP_FLASHCARDS_NOTES: "Flashcards & Notes",
GROUP_CONTRIBUTING: "Contributing",
CHECK_WIKI: 'Pro více informací jděte na <a href="${wiki_url}">wiki</a>.',
GITHUB_DISCUSSIONS:
'Visit the <a href="${discussions_url}">discussions</a> section for Q&A help, feedback, and general discussion.',
GITHUB_ISSUES:
'Raise an issue <a href="${issues_url}">here</a> if you have a feature request or a bug-report.',
GITHUB_SOURCE_CODE:
'Project source code available on <a href="${github_project_url}">GitHub</a>',
CODE_CONTRIBUTION_INFO:
'Information on <a href="${code_contribution_url}">code contributions</a>',
TRANSLATION_CONTRIBUTION_INFO:
'Information on <a href="${translation_contribution_url}">translating the plugin</a> to your language',
PROJECT_CONTRIBUTIONS:
'Raise an issue <a href="${issues_url}">here</a> if you have a feature request or a bug-report',
FOLDERS_TO_IGNORE: "Ignorované složky",
FOLDERS_TO_IGNORE_DESC:
"Zadejte cesty ke složkám oddělené odřádkováním napříkad. Šablony Meta/Scripts",
FOLDERS_TO_IGNORE_DESC: `Zadejte cesty ke složkám oddělené odřádkováním napříkad. Šablony Meta/Scripts.
Note that this setting is common to both Flashcards and Notes.`,
FLASHCARDS: "Kartičky",
FLASHCARD_EASY_LABEL: "Easy Button Text",
FLASHCARD_GOOD_LABEL: "Good Button Text",
Expand Down
24 changes: 21 additions & 3 deletions src/lang/locale/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,29 @@ export default {
YEARS_STR_IVL_MOBILE: "${interval}j",

// settings.ts
SETTINGS_HEADER: "Spaced Repetition Plugin - Einstellungen",
SETTINGS_HEADER: "Spaced Repetition - Einstellungen",
GROUP_TAGS_FOLDERS: "Tags & Folders",
GROUP_FLASHCARD_REVIEW: "Flashcard Review",
GROUP_FLASHCARD_SEPARATORS: "Flashcard Separators",
GROUP_DATA_STORAGE: "Storage of Scheduling Data",
GROUP_FLASHCARDS_NOTES: "Flashcards & Notes",
GROUP_CONTRIBUTING: "Contributing",
CHECK_WIKI: 'Weitere Informationen gibt es im <a href="${wiki_url}">Wiki</a> (english).',
GITHUB_DISCUSSIONS:
'Visit the <a href="${discussions_url}">discussions</a> section for Q&A help, feedback, and general discussion.',
GITHUB_ISSUES:
'Raise an issue <a href="${issues_url}">here</a> if you have a feature request or a bug-report.',
GITHUB_SOURCE_CODE:
'Project source code available on <a href="${github_project_url}">GitHub</a>',
CODE_CONTRIBUTION_INFO:
'Information on <a href="${code_contribution_url}">code contributions</a>',
TRANSLATION_CONTRIBUTION_INFO:
'Information on <a href="${translation_contribution_url}">translating the plugin</a> to your language',
PROJECT_CONTRIBUTIONS:
'Raise an issue <a href="${issues_url}">here</a> if you have a feature request or a bug-report',
FOLDERS_TO_IGNORE: "Ausgeschlossene Ordner",
FOLDERS_TO_IGNORE_DESC:
"Mehrere Ordner mit Zeilenumbrüchen getrennt angeben. Bsp. OrdnerA[Zeilenumbruch]OrdnerB/Unterordner",
FOLDERS_TO_IGNORE_DESC: `Mehrere Ordner mit Zeilenumbrüchen getrennt angeben. Bsp. OrdnerA[Zeilenumbruch]OrdnerB/Unterordner.
Note that this setting is common to both Flashcards and Notes.`,
FLASHCARDS: "Lernkarten",
FLASHCARD_EASY_LABEL: "Einfach Knopf Text",
FLASHCARD_GOOD_LABEL: "Gut Knopf Text",
Expand Down
23 changes: 21 additions & 2 deletions src/lang/locale/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,29 @@ export default {
YEARS_STR_IVL_MOBILE: "${interval}y",

// settings.ts
SETTINGS_HEADER: "Spaced Repetition Plugin - Settings",
SETTINGS_HEADER: "Spaced Repetition - Settings",
GROUP_TAGS_FOLDERS: "Tags & Folders",
GROUP_FLASHCARD_REVIEW: "Flashcard Review",
GROUP_FLASHCARD_SEPARATORS: "Flashcard Separators",
GROUP_DATA_STORAGE: "Storage of Scheduling Data",
GROUP_FLASHCARDS_NOTES: "Flashcards & Notes",
GROUP_CONTRIBUTING: "Contributing",
CHECK_WIKI: 'For more information, check the <a href="${wiki_url}">wiki</a>.',
GITHUB_DISCUSSIONS:
'Visit the <a href="${discussions_url}">discussions</a> section for Q&A help, feedback, and general discussion.',
GITHUB_ISSUES:
'Raise an issue <a href="${issues_url}">here</a> if you have a feature request or a bug-report.',
GITHUB_SOURCE_CODE:
'Project source code available on <a href="${github_project_url}">GitHub</a>',
CODE_CONTRIBUTION_INFO:
'Information on <a href="${code_contribution_url}">code contributions</a>',
TRANSLATION_CONTRIBUTION_INFO:
'Information on <a href="${translation_contribution_url}">translating the plugin</a> to your language',
PROJECT_CONTRIBUTIONS:
'Raise an issue <a href="${issues_url}">here</a> if you have a feature request or a bug-report',
FOLDERS_TO_IGNORE: "Folders to ignore",
FOLDERS_TO_IGNORE_DESC: "Enter folder paths separated by newlines i.e. Templates Meta/Scripts",
FOLDERS_TO_IGNORE_DESC: `Enter folder paths separated by newlines e.g. Templates Meta/Scripts.
Note that this setting is common to both Flashcards and Notes.`,
FLASHCARDS: "Flashcards",
FLASHCARD_EASY_LABEL: "Easy Button Text",
FLASHCARD_GOOD_LABEL: "Good Button Text",
Expand Down
22 changes: 20 additions & 2 deletions src/lang/locale/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,28 @@ export default {

// settings.ts
SETTINGS_HEADER: "Extensión de Repetición Espaciada - Ajustes",
GROUP_TAGS_FOLDERS: "Tags & Folders",
GROUP_FLASHCARD_REVIEW: "Flashcard Review",
GROUP_FLASHCARD_SEPARATORS: "Flashcard Separators",
GROUP_DATA_STORAGE: "Storage of Scheduling Data",
GROUP_FLASHCARDS_NOTES: "Flashcards & Notes",
GROUP_CONTRIBUTING: "Contributing",
CHECK_WIKI: 'Para más información revisa la <a href="${wiki_url}">wiki</a>.',
GITHUB_DISCUSSIONS:
'Visit the <a href="${discussions_url}">discussions</a> section for Q&A help, feedback, and general discussion.',
GITHUB_ISSUES:
'Raise an issue <a href="${issues_url}">here</a> if you have a feature request or a bug-report.',
GITHUB_SOURCE_CODE:
'Project source code available on <a href="${github_project_url}">GitHub</a>',
CODE_CONTRIBUTION_INFO:
'Information on <a href="${code_contribution_url}">code contributions</a>',
TRANSLATION_CONTRIBUTION_INFO:
'Information on <a href="${translation_contribution_url}">translating the plugin</a> to your language',
PROJECT_CONTRIBUTIONS:
'Raise an issue <a href="${issues_url}">here</a> if you have a feature request or a bug-report',
FOLDERS_TO_IGNORE: "Directorios a ignorar",
FOLDERS_TO_IGNORE_DESC:
"Escriba las rutas de los directorios separadas por saltos de línea, por ejemplo, Plantillas Extra/Guiones",
FOLDERS_TO_IGNORE_DESC: `Escriba las rutas de los directorios separadas por saltos de línea, por ejemplo, Plantillas Extra/Guiones.
Note that this setting is common to both Flashcards and Notes.`,
FLASHCARDS: "Tarjetas de Memorización",
FLASHCARD_EASY_LABEL: "Texto del botón: Fácil",
FLASHCARD_GOOD_LABEL: "Texto del botón: Bien",
Expand Down
22 changes: 20 additions & 2 deletions src/lang/locale/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,28 @@ export default {

// settings.ts
SETTINGS_HEADER: "Plugin per ripetizione spaziata - Impostazioni",
GROUP_TAGS_FOLDERS: "Tags & Folders",
GROUP_FLASHCARD_REVIEW: "Flashcard Review",
GROUP_FLASHCARD_SEPARATORS: "Flashcard Separators",
GROUP_DATA_STORAGE: "Storage of Scheduling Data",
GROUP_FLASHCARDS_NOTES: "Flashcards & Notes",
GROUP_CONTRIBUTING: "Contributing",
CHECK_WIKI: 'Per maggiori informazioni, rivolgersi alla <a href="${wiki_url}">wiki</a>.',
GITHUB_DISCUSSIONS:
'Visit the <a href="${discussions_url}">discussions</a> section for Q&A help, feedback, and general discussion.',
GITHUB_ISSUES:
'Raise an issue <a href="${issues_url}">here</a> if you have a feature request or a bug-report.',
GITHUB_SOURCE_CODE:
'Project source code available on <a href="${github_project_url}">GitHub</a>',
CODE_CONTRIBUTION_INFO:
'Information on <a href="${code_contribution_url}">code contributions</a>',
TRANSLATION_CONTRIBUTION_INFO:
'Information on <a href="${translation_contribution_url}">translating the plugin</a> to your language',
PROJECT_CONTRIBUTIONS:
'Raise an issue <a href="${issues_url}">here</a> if you have a feature request or a bug-report',
FOLDERS_TO_IGNORE: "Cartelle da ignorare",
FOLDERS_TO_IGNORE_DESC:
"Inserisci i percorsi delle cartelle separati da a capo, per esempio, Templates Meta/Scripts",
FOLDERS_TO_IGNORE_DESC: `Inserisci i percorsi delle cartelle separati da a capo, per esempio, Templates Meta/Scripts.
Note that this setting is common to both Flashcards and Notes.`,
FLASHCARDS: "Schede",
FLASHCARD_EASY_LABEL: "Testo del bottone facile",
FLASHCARD_GOOD_LABEL: "Testo del bottone buono",
Expand Down
Loading
Loading