mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-08-28 17:21:05 +00:00
feat: search bar in settings & update changelog
This commit is contained in:
@@ -61,6 +61,7 @@
|
|||||||
let selectedBackgroundCategory = $state("All");
|
let selectedBackgroundCategory = $state("All");
|
||||||
let backgroundCategories = $state<string[]>([]);
|
let backgroundCategories = $state<string[]>([]);
|
||||||
let storeSearchTerm = $state("");
|
let storeSearchTerm = $state("");
|
||||||
|
let settingsSearch = $state("");
|
||||||
|
|
||||||
let showDisclaimerModal = $state(false);
|
let showDisclaimerModal = $state(false);
|
||||||
let disclaimerCallbacks = $state<{ onConfirm: () => void; onCancel: () => void } | null>(null);
|
let disclaimerCallbacks = $state<{ onConfirm: () => void; onCancel: () => void } | null>(null);
|
||||||
@@ -142,6 +143,7 @@
|
|||||||
);
|
);
|
||||||
|
|
||||||
const sectionTitle = $derived.by(() => {
|
const sectionTitle = $derived.by(() => {
|
||||||
|
if (activePage === "settings" && settingsSearch.trim()) return "Search results";
|
||||||
if (activePage === "themes") return "Themes";
|
if (activePage === "themes") return "Themes";
|
||||||
if (activePage === "backgrounds") return "Backgrounds";
|
if (activePage === "backgrounds") return "Backgrounds";
|
||||||
return [...userNav, ...appNav].find((item) => item.id === activeSection)?.label ?? "Settings";
|
return [...userNav, ...appNav].find((item) => item.id === activeSection)?.label ?? "Settings";
|
||||||
@@ -251,6 +253,7 @@
|
|||||||
|
|
||||||
const selectNavItem = (id: string) => {
|
const selectNavItem = (id: string) => {
|
||||||
if (activePage === "settings") {
|
if (activePage === "settings") {
|
||||||
|
settingsSearch = "";
|
||||||
activeSection = id;
|
activeSection = id;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -281,6 +284,7 @@
|
|||||||
const applyDestination = (destination: SettingsDestination) => {
|
const applyDestination = (destination: SettingsDestination) => {
|
||||||
activePage = destination.page;
|
activePage = destination.page;
|
||||||
if (destination.page === "settings" && destination.section) {
|
if (destination.page === "settings" && destination.section) {
|
||||||
|
settingsSearch = "";
|
||||||
activeSection = destination.section;
|
activeSection = destination.section;
|
||||||
} else if (destination.page === "themes" && destination.view) {
|
} else if (destination.page === "themes" && destination.view) {
|
||||||
activeThemeView = destination.view === "store" ? "theme-store" : "theme-settings";
|
activeThemeView = destination.view === "store" ? "theme-store" : "theme-settings";
|
||||||
@@ -373,6 +377,32 @@
|
|||||||
: 'w-[260px] px-4 py-5'}"
|
: 'w-[260px] px-4 py-5'}"
|
||||||
aria-label="Settings categories"
|
aria-label="Settings categories"
|
||||||
>
|
>
|
||||||
|
{#if activePage === "settings"}
|
||||||
|
<label class="relative mb-4 block shrink-0">
|
||||||
|
<span class="sr-only">Search settings</span>
|
||||||
|
<svg
|
||||||
|
class="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-zinc-400"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="m21 21-4.35-4.35m1.35-5.65a7 7 0 1 1-14 0 7 7 0 0 1 14 0Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<input
|
||||||
|
type="search"
|
||||||
|
placeholder="Search settings"
|
||||||
|
bind:value={settingsSearch}
|
||||||
|
class="h-10 w-full rounded-lg bg-zinc-200/70 pl-9 pr-3 text-sm text-zinc-900 placeholder:text-zinc-400 transition-colors duration-200 focus:bg-white focus:outline-none focus:ring-2 focus:ring-zinc-400 focus:ring-offset-2 dark:bg-zinc-800/80 dark:text-white dark:placeholder:text-zinc-500 dark:focus:bg-zinc-800 dark:focus:ring-offset-zinc-900"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<SidebarNav
|
<SidebarNav
|
||||||
groups={navGroups}
|
groups={navGroups}
|
||||||
selectedId={selectedNavId}
|
selectedId={selectedNavId}
|
||||||
@@ -455,7 +485,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex-1 min-h-0 px-4 pb-8 overflow-y-auto no-scrollbar">
|
<div class="flex-1 min-h-0 px-4 pb-8 overflow-y-auto no-scrollbar">
|
||||||
{#if activePage === "settings"}
|
{#if activePage === "settings"}
|
||||||
{#if activeSection === "shortcuts"}
|
{#if activeSection === "shortcuts" && !settingsSearch.trim()}
|
||||||
<Shortcuts />
|
<Shortcuts />
|
||||||
{:else}
|
{:else}
|
||||||
<Settings
|
<Settings
|
||||||
@@ -463,8 +493,12 @@
|
|||||||
showFontPicker={openFontPicker}
|
showFontPicker={openFontPicker}
|
||||||
{showDisclaimer}
|
{showDisclaimer}
|
||||||
showCloudPanel={openCloudPanel}
|
showCloudPanel={openCloudPanel}
|
||||||
{activeSection}
|
activeSection={settingsSearch.trim() ? "all" : activeSection}
|
||||||
|
searchQuery={settingsSearch}
|
||||||
/>
|
/>
|
||||||
|
{#if settingsSearch.trim()}
|
||||||
|
<Shortcuts searchQuery={settingsSearch} />
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{:else if activePage === "themes"}
|
{:else if activePage === "themes"}
|
||||||
<Theme section="themes" />
|
<Theme section="themes" />
|
||||||
|
|||||||
@@ -136,12 +136,13 @@
|
|||||||
void loadPluginSettings();
|
void loadPluginSettings();
|
||||||
});
|
});
|
||||||
|
|
||||||
const { showColourPicker, showFontPicker, showDisclaimer, showCloudPanel, activeSection = "general" } = $props<{
|
const { showColourPicker, showFontPicker, showDisclaimer, showCloudPanel, activeSection = "general", searchQuery = "" } = $props<{
|
||||||
showColourPicker: () => void;
|
showColourPicker: () => void;
|
||||||
showFontPicker: () => void;
|
showFontPicker: () => void;
|
||||||
showDisclaimer: (onConfirm: () => void, onCancel: () => void, title?: string, message?: string) => void;
|
showDisclaimer: (onConfirm: () => void, onCancel: () => void, title?: string, message?: string) => void;
|
||||||
showCloudPanel: () => void;
|
showCloudPanel: () => void;
|
||||||
activeSection?: string;
|
activeSection?: string;
|
||||||
|
searchQuery?: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
/** Map each plugin into the settings sidebar category that fits its purpose. */
|
/** Map each plugin into the settings sidebar category that fits its purpose. */
|
||||||
@@ -167,6 +168,13 @@
|
|||||||
activeSection === "all" ||
|
activeSection === "all" ||
|
||||||
(pluginSectionById[pluginId] ?? "features") === activeSection;
|
(pluginSectionById[pluginId] ?? "features") === activeSection;
|
||||||
|
|
||||||
|
const matchesSearch = (...parts: Array<string | undefined>) => {
|
||||||
|
const q = searchQuery.trim().toLowerCase();
|
||||||
|
return !q || parts.some((part) => part?.toLowerCase().includes(q));
|
||||||
|
};
|
||||||
|
|
||||||
|
const pluginHit = (plugin: Plugin) =>
|
||||||
|
matchesSearch(plugin.name, plugin.description, ...Object.values(plugin.settings).flatMap((s) => [s.title, s.description]));
|
||||||
|
|
||||||
async function exportCloudSettingsJsonToFile() {
|
async function exportCloudSettingsJsonToFile() {
|
||||||
const payload = await getSnapshotForUpload();
|
const payload = await getSnapshotForUpload();
|
||||||
@@ -183,6 +191,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet Setting({ title, description, Component, props }: SettingsList) }
|
{#snippet Setting({ title, description, Component, props }: SettingsList) }
|
||||||
|
{#if matchesSearch(title, description)}
|
||||||
<div class="flex justify-between items-center px-5 py-5">
|
<div class="flex justify-between items-center px-5 py-5">
|
||||||
<div class="pr-5">
|
<div class="pr-5">
|
||||||
<h2 class="text-xl font-bold">{title}</h2>
|
<h2 class="text-xl font-bold">{title}</h2>
|
||||||
@@ -192,6 +201,7 @@
|
|||||||
<Component {...props} />
|
<Component {...props} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
<div class="flex flex-col divide-y divide-zinc-100 dark:divide-zinc-700">
|
<div class="flex flex-col divide-y divide-zinc-100 dark:divide-zinc-700">
|
||||||
@@ -204,6 +214,7 @@
|
|||||||
props: {}
|
props: {}
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
{#if matchesSearch("BetterSEQTA Cloud", "Account & sync")}
|
||||||
<div class="border-none">
|
<div class="border-none">
|
||||||
<div class="p-1 my-1 from-white to-zinc-100 bg-gradient-to-br rounded-xl border shadow-sm border-zinc-200/50 dark:border-zinc-700/40 dark:to-zinc-900/50 dark:from-zinc-900/40">
|
<div class="p-1 my-1 from-white to-zinc-100 bg-gradient-to-br rounded-xl border shadow-sm border-zinc-200/50 dark:border-zinc-700/40 dark:to-zinc-900/50 dark:from-zinc-900/40">
|
||||||
<div class="flex justify-between items-center px-5 py-4">
|
<div class="flex justify-between items-center px-5 py-4">
|
||||||
@@ -223,6 +234,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
{/if}
|
||||||
|
|
||||||
{#if showsSection("general")}
|
{#if showsSection("general")}
|
||||||
{#each [
|
{#each [
|
||||||
@@ -354,12 +366,13 @@
|
|||||||
{@render Setting(option)}
|
{@render Setting(option)}
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
{#if !isEngage}
|
{#if !isEngage && matchesSearch("Sidebar Style", "Item Size", "Corner Radius", "Active Indicator", "Sidebar Width", "Transparency Effects", "Blur Strength")}
|
||||||
<div class="border-none">
|
<div class="border-none">
|
||||||
<SidebarAppearance />
|
<SidebarAppearance />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#if matchesSearch("Adaptive Theme Colour", "Soft Gradient", "Smooth colour transition")}
|
||||||
<div class="border-none">
|
<div class="border-none">
|
||||||
<div class="p-1 my-1 from-white to-zinc-100 bg-gradient-to-br rounded-xl border shadow-sm border-zinc-200/50 dark:border-zinc-700/40 dark:to-zinc-900/50 dark:from-zinc-900/40">
|
<div class="p-1 my-1 from-white to-zinc-100 bg-gradient-to-br rounded-xl border shadow-sm border-zinc-200/50 dark:border-zinc-700/40 dark:to-zinc-900/50 dark:from-zinc-900/40">
|
||||||
<div class="flex justify-between items-center px-5 py-4">
|
<div class="flex justify-between items-center px-5 py-4">
|
||||||
@@ -403,8 +416,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
{/if}
|
||||||
|
|
||||||
{#if showsSection("general")}
|
{#if showsSection("general")}
|
||||||
|
{#if matchesSearch("Home Page Assessments", "Include Past Assessments", "Maximum Subjects", "Maximum Assessments per Subject")}
|
||||||
<div class="border-none">
|
<div class="border-none">
|
||||||
<div class="p-1 my-1 from-white to-zinc-100 bg-gradient-to-br rounded-xl border shadow-sm border-zinc-200/50 dark:border-zinc-700/40 dark:to-zinc-900/50 dark:from-zinc-900/40">
|
<div class="p-1 my-1 from-white to-zinc-100 bg-gradient-to-br rounded-xl border shadow-sm border-zinc-200/50 dark:border-zinc-700/40 dark:to-zinc-900/50 dark:from-zinc-900/40">
|
||||||
<div class="flex justify-between items-center px-5 py-4">
|
<div class="flex justify-between items-center px-5 py-4">
|
||||||
@@ -464,9 +479,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
{/if}
|
||||||
|
|
||||||
{#each pluginSettings as plugin (plugin.pluginId)}
|
{#each pluginSettings as plugin (plugin.pluginId)}
|
||||||
{#if pluginBelongsInSection(plugin.pluginId)}
|
{#if pluginBelongsInSection(plugin.pluginId) && pluginHit(plugin)}
|
||||||
<div class="border-none">
|
<div class="border-none">
|
||||||
<div class="p-1 my-1 from-white to-zinc-100 bg-gradient-to-br rounded-xl border shadow-sm border-zinc-200/50 dark:border-zinc-700/40 dark:to-zinc-900/50 dark:from-zinc-900/40 {!(plugin as any).disableToggle && Object.keys(plugin.settings).length === 0 ? 'hidden' : ''}">
|
<div class="p-1 my-1 from-white to-zinc-100 bg-gradient-to-br rounded-xl border shadow-sm border-zinc-200/50 dark:border-zinc-700/40 dark:to-zinc-900/50 dark:from-zinc-900/40 {!(plugin as any).disableToggle && Object.keys(plugin.settings).length === 0 ? 'hidden' : ''}">
|
||||||
<!-- Always show enable toggle if disableToggle is true -->
|
<!-- Always show enable toggle if disableToggle is true -->
|
||||||
@@ -596,7 +612,18 @@
|
|||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{#if $settingsState.devMode}
|
{#if $settingsState.devMode && matchesSearch(
|
||||||
|
"Developer Mode",
|
||||||
|
"Verbose logging",
|
||||||
|
"Delay loading screen",
|
||||||
|
"Sensitive Hider",
|
||||||
|
"Mock Notices",
|
||||||
|
"Show Privacy Notification",
|
||||||
|
"Show Theme of the Month",
|
||||||
|
"Export cloud settings JSON",
|
||||||
|
"API Base URL",
|
||||||
|
"GitHub latest version override",
|
||||||
|
)}
|
||||||
<div class="flex-col p-1 my-1 bg-gradient-to-br from-white rounded-xl border shadow-sm to-zinc-100 border-zinc-200/50 dark:border-zinc-700/40 dark:to-zinc-900/50 dark:from-zinc-900/40">
|
<div class="flex-col p-1 my-1 bg-gradient-to-br from-white rounded-xl border shadow-sm to-zinc-100 border-zinc-200/50 dark:border-zinc-700/40 dark:to-zinc-900/50 dark:from-zinc-900/40">
|
||||||
<div class="flex justify-between items-center px-5 py-4">
|
<div class="flex justify-between items-center px-5 py-4">
|
||||||
<div class="pr-4">
|
<div class="pr-4">
|
||||||
|
|||||||
@@ -5,6 +5,9 @@
|
|||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import Shortcuts from "@/seqta/content/links.json"
|
import Shortcuts from "@/seqta/content/links.json"
|
||||||
|
|
||||||
|
let { searchQuery = "" } = $props<{ searchQuery?: string }>();
|
||||||
|
const q = $derived(searchQuery.trim().toLowerCase());
|
||||||
|
|
||||||
let isLoaded = $state(false);
|
let isLoaded = $state(false);
|
||||||
let fileInput = $state<HTMLInputElement | null>(null);
|
let fileInput = $state<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
@@ -106,6 +109,7 @@
|
|||||||
|
|
||||||
<div class="flex flex-col pt-4 divide-y divide-zinc-100 dark:divide-zinc-700">
|
<div class="flex flex-col pt-4 divide-y divide-zinc-100 dark:divide-zinc-700">
|
||||||
{#if isLoaded}
|
{#if isLoaded}
|
||||||
|
{#if !q}
|
||||||
<div>
|
<div>
|
||||||
<MotionDiv
|
<MotionDiv
|
||||||
initial={{ opacity: 0, height: 0 }}
|
initial={{ opacity: 0, height: 0 }}
|
||||||
@@ -207,9 +211,11 @@
|
|||||||
</button>
|
</button>
|
||||||
</MotionDiv>
|
</MotionDiv>
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<!-- Custom Shortcuts Section -->
|
<!-- Custom Shortcuts Section -->
|
||||||
{#each ($settingsState.customshortcuts ?? []) as shortcut, index}
|
{#each ($settingsState.customshortcuts ?? []) as shortcut, index}
|
||||||
|
{#if !q || shortcut.name.toLowerCase().includes(q)}
|
||||||
<div class="flex justify-between items-center px-4 py-3">
|
<div class="flex justify-between items-center px-4 py-3">
|
||||||
{shortcut.name}
|
{shortcut.name}
|
||||||
<button aria-label="Delete Shortcut" onclick={() => deleteCustomShortcut(index)}>
|
<button aria-label="Delete Shortcut" onclick={() => deleteCustomShortcut(index)}>
|
||||||
@@ -218,9 +224,11 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
{#each Object.entries(Shortcuts) as shortcut}
|
{#each Object.entries(Shortcuts) as shortcut}
|
||||||
|
{#if !q || shortcut[1].DisplayName?.toLowerCase().includes(q) || shortcut[0].toLowerCase().includes(q)}
|
||||||
<div class="flex justify-between items-center px-4 py-3">
|
<div class="flex justify-between items-center px-4 py-3">
|
||||||
<div class="pr-4">
|
<div class="pr-4">
|
||||||
<!-- Use DisplayName if it exists, otherwise use the key (shortcut[0]) as a fallback -->
|
<!-- Use DisplayName if it exists, otherwise use the key (shortcut[0]) as a fallback -->
|
||||||
@@ -228,6 +236,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<Switch state={$settingsState.shortcuts.find(s => s.name === shortcut[0])?.enabled ?? false} onChange={() => switchChange(shortcut[0])} />
|
<Switch state={$settingsState.shortcuts.find(s => s.name === shortcut[0])?.enabled ?? false} onChange={() => switchChange(shortcut[0])} />
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
{:else}
|
{:else}
|
||||||
<div class="p-4 text-center">
|
<div class="p-4 text-center">
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export const WHATS_NEW_CHANGELOG: WhatsNewRelease[] = [
|
|||||||
"Removed white background around SEQTA Learn mobile App icons in settings.",
|
"Removed white background around SEQTA Learn mobile App icons in settings.",
|
||||||
"Re-added the kitten back to the 404 Error pages.",
|
"Re-added the kitten back to the 404 Error pages.",
|
||||||
"Improved the sidebar to be more stable and performant.",
|
"Improved the sidebar to be more stable and performant.",
|
||||||
|
"Fixed Analytics page not respecting no animations setting. (#474)",
|
||||||
"Fixed dropdown contrast and readability in settings and across SEQTA pages.",
|
"Fixed dropdown contrast and readability in settings and across SEQTA pages.",
|
||||||
"Fixed Analytics sidebar item not hiding when toggled off in Edit Sidebar.",
|
"Fixed Analytics sidebar item not hiding when toggled off in Edit Sidebar.",
|
||||||
"Fixed timetable subject colour picker not reopening after closing (#221).",
|
"Fixed timetable subject colour picker not reopening after closing (#221).",
|
||||||
|
|||||||
Reference in New Issue
Block a user