feat: move svelte interface to 'src/interface'

This commit is contained in:
sethburkart123
2024-11-01 17:37:20 +11:00
parent fe82365c24
commit 9de6e8feaf
57 changed files with 33 additions and 33 deletions
+176
View File
@@ -0,0 +1,176 @@
<script lang="ts">
import Switch from "../../components/Switch.svelte"
import Button from "../../components/Button.svelte"
import Slider from "../../components/Slider.svelte"
import Select from "@/interface/components/Select.svelte"
import browser from "webextension-polyfill"
import type { SettingsList } from "@/interface/types/SettingsProps"
import { settingsState } from "@/seqta/utils/listeners/SettingsState.ts"
import PickerSwatch from "@/interface/components/PickerSwatch.svelte"
import hideSensitiveContent from "@/seqta/ui/dev/hideSensitiveContent"
const { showColourPicker } = $props<{ showColourPicker: () => void }>();
</script>
{#snippet Setting({ title, description, Component, props }: SettingsList) }
<div class="flex items-center justify-between px-4 py-3">
<div class="pr-4">
<h2 class="text-sm font-bold">{title}</h2>
<p class="text-xs">{description}</p>
</div>
<div>
<Component {...props} />
</div>
</div>
{/snippet}
<div class="flex flex-col divide-y divide-zinc-100 dark:divide-zinc-700">
{#each [
{
title: "Transparency Effects",
description: "Enables transparency effects on certain elements such as blur. (May impact battery life)",
id: 1,
Component: Switch,
props: {
state: $settingsState.transparencyEffects,
onChange: (isOn: boolean) => settingsState.transparencyEffects = isOn
}
},
{
title: "Animated Background",
description: "Adds an animated background to BetterSEQTA. (May impact battery life)",
id: 2,
Component: Switch,
props: {
state: $settingsState.animatedbk,
onChange: (isOn: boolean) => settingsState.animatedbk = isOn
}
},
{
title: "Animated Background Speed",
description: "Controls the speed of the animated background.",
id: 3,
Component: Slider,
props: {
state: $settingsState.bksliderinput,
onChange: (value: number) => settingsState.bksliderinput = `${value}`
}
},
{
title: "Custom Theme Colour",
description: "Customise the overall theme colour of SEQTA Learn.",
id: 4,
Component: PickerSwatch,
props: {
onClick: showColourPicker
}
},
{
title: "Edit Sidebar Layout",
description: "Customise the sidebar layout.",
id: 5,
Component: Button,
props: {
onClick: () => browser.runtime.sendMessage({ type: 'currentTab', info: 'EditSidebar' }),
text: "Edit"
}
},
{
title: "Animations",
description: "Enables animations on certain pages.",
id: 6,
Component: Switch,
props: {
state: $settingsState.animations,
onChange: (isOn: boolean) => settingsState.animations = isOn
}
},
{
title: "Notification Collector",
description: "Uncaps the 9+ limit for notifications, showing the real number.",
id: 7,
Component: Switch,
props: {
state: $settingsState.notificationcollector,
onChange: (isOn: boolean) => settingsState.notificationcollector = isOn
}
},
{
title: "Lesson Alerts",
description: "Sends a native browser notification ~5 minutes prior to lessons.",
id: 8,
Component: Switch,
props: {
state: $settingsState.lessonalert,
onChange: (isOn: boolean) => settingsState.lessonalert = isOn
}
},
{
title: "12 Hour Time",
description: "Prefer 12 hour time format for SEQTA",
id: 9,
Component: Switch,
props: {
state: $settingsState.timeFormat === "12",
onChange: (isOn: boolean) => settingsState.timeFormat = isOn ? "12" : "24"
}
},
{
title: "Default Page",
description: "The page to load when SEQTA Learn is opened.",
id: 10,
Component: Select,
props: {
state: $settingsState.defaultPage,
onChange: (value: string) => settingsState.defaultPage = value,
options: [
{ value: 'home', label: 'Home' },
{ value: 'dashboard', label: 'Dashboard' },
{ value: 'timetable', label: 'Timetable' },
{ value: 'welcome', label: 'Welcome' },
{ value: 'messages', label: 'Messages' },
{ value: 'documents', label: 'Documents' },
{ value: 'reports', label: 'Reports' },
]
}
},
{
title: "BetterSEQTA+",
description: "Enables BetterSEQTA+ features",
id: 11,
Component: Switch,
props: {
state: $settingsState.onoff,
onChange: (isOn: boolean) => settingsState.onoff = isOn
}
}
] as option}
{@render Setting(option)}
{/each}
{#if $settingsState.devMode}
<div class="flex items-center justify-between px-4 py-3 mt-4 pt-[1.75rem]">
<div class="pr-4">
<h2 class="text-sm font-bold">Developer Mode</h2>
<p class="text-xs">Enables developer mode, allowing you to test new features and changes.</p>
</div>
<div>
<Switch state={$settingsState.devMode} onChange={(isOn: boolean) => settingsState.devMode = isOn} />
</div>
</div>
<div class="flex items-center justify-between px-4 py-3">
<div class="pr-4">
<h2 class="text-sm font-bold">Sensitive Hider</h2>
<p class="text-xs">Replace sensitive content with mock data</p>
</div>
<div>
<Button
onClick={() => hideSensitiveContent()}
text="Hide"
/>
</div>
</div>
{/if}
</div>
@@ -0,0 +1,159 @@
<script lang="ts">
import MotionDiv from '@/interface/components/MotionDiv.svelte';
import { settingsState } from "@/seqta/utils/listeners/SettingsState.ts"
import Switch from "@/interface/components/Switch.svelte"
import { onMount } from 'svelte';
let isLoaded = $state(false);
onMount(async () => {
// Wait for settingsState to be initialized
await new Promise<void>((resolve) => {
const checkState = () => {
if ($settingsState?.shortcuts) {
isLoaded = true;
resolve();
} else {
setTimeout(checkState, 100);
}
};
checkState();
});
});
const switchChange = (index: number) => {
const updatedShortcuts = [...settingsState.shortcuts];
updatedShortcuts[index].enabled = !updatedShortcuts[index].enabled;
settingsState.shortcuts = updatedShortcuts;
}
let isFormVisible = $state(false);
let newTitle = $state("");
let newURL = $state("");
const toggleForm = () => {
isFormVisible = !isFormVisible;
};
const formatUrl = (inputUrl: string) => {
const protocolRegex = /^(http:\/\/|https:\/\/|ftp:\/\/)/;
return protocolRegex.test(inputUrl) ? inputUrl : `https://${inputUrl}`;
};
const isValidTitle = (title: string) => title.trim() !== "";
const isValidURL = (url: string) => {
const pattern = new RegExp("^(https?:\\/\\/)?[\\w.-]+(?:\\.[\\w\\-]+)*(?::\\d+)?(/[\\w\\-./]*)*$", "i");
return pattern.test(url);
};
const addNewCustomShortcut = () => {
if (isValidTitle(newTitle) && isValidURL(newURL)) {
const newShortcut = { name: newTitle.trim(), url: formatUrl(newURL).trim(), icon: newTitle[0] };
settingsState.customshortcuts = [...settingsState.customshortcuts, newShortcut];
newTitle = "";
newURL = "";
isFormVisible = false;
} else {
alert("Please enter a valid title and URL.");
}
};
const deleteCustomShortcut = (index: number) => {
settingsState.customshortcuts = settingsState.customshortcuts.filter((_, i) => i !== index);
};
</script>
{#snippet Shortcuts([index, Shortcut]: [string, { name: string, enabled: boolean }]) }
<div class="flex items-center justify-between px-4 py-3">
<div class="pr-4">
<h2 class="text-sm">{Shortcut.name}</h2>
</div>
<Switch state={Shortcut.enabled} onChange={() => switchChange(parseInt(index))} />
</div>
{/snippet}
<div class="flex flex-col pt-4 divide-y divide-zinc-100 dark:divide-zinc-700">
{#if isLoaded}
<div>
<MotionDiv
initial={{ opacity: 0, height: 0 }}
animate={isFormVisible ? { opacity: 1, height: 'auto' } : { opacity: 0, height: 0 }}
exit={{ opacity: 0, height: 0 }}
transition={{
type: 'spring',
config: { stiffness: 400, damping: 25 }
}}
>
{#if isFormVisible}
<div class="flex flex-col items-center">
<MotionDiv
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0, duration: 0.2 }}
class="w-full"
>
<input
class="w-full p-2 transition border-0 rounded-lg placeholder-zinc-300 bg-zinc-100 dark:bg-zinc-700 focus:bg-zinc-200/50 dark:focus:bg-zinc-600"
type="text"
placeholder="Shortcut Name"
bind:value={newTitle}
/>
</MotionDiv>
<MotionDiv
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.05, duration: 0.2 }}
class="w-full"
>
<input
class="w-full p-2 my-2 transition border-0 rounded-lg placeholder-zinc-300 bg-zinc-100 dark:bg-zinc-700 focus:bg-zinc-200/50 dark:focus:bg-zinc-600"
type="text"
placeholder="URL eg. https://google.com"
bind:value={newURL}
/>
</MotionDiv>
</div>
{/if}
</MotionDiv>
<MotionDiv
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
>
<button
class="w-full px-4 py-2 mb-4 text-[13px] dark:text-white transition rounded-xl bg-zinc-200 dark:bg-zinc-700/50"
onclick={isFormVisible ? addNewCustomShortcut : toggleForm}
>
{#if isFormVisible}
Add
{:else}
Add Custom Shortcut
{/if}
</button>
</MotionDiv>
</div>
{#each Object.entries($settingsState.shortcuts) as shortcut}
{@render Shortcuts(shortcut)}
{/each}
<!-- Custom Shortcuts Section -->
{#each $settingsState.customshortcuts as shortcut, index}
<div class="flex items-center justify-between px-4 py-3">
{shortcut.name}
<button onclick={() => deleteCustomShortcut(index)}>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width={1.5} stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
{/each}
{:else}
<div class="p-4 text-center">
Loading shortcuts...
</div>
{/if}
</div>
+36
View File
@@ -0,0 +1,36 @@
<script lang="ts">
import BackgroundSelector from "@/interface/components/themes/BackgroundSelector.svelte"
import ThemeSelector from "@/interface/components/themes/ThemeSelector.svelte"
import { standalone } from "@/interface/utils/standalone.svelte"
// backgrounds
let selectedBackground = $state<string | null>(null);
let selectNoBackground = $state<() => void>(() => { });
let clearTheme = $derived(selectedBackground !== null);
let editMode = $state<boolean>(false);
</script>
<div class="py-4">
{#if !standalone.standalone}
<button
onclick={() => selectNoBackground()}
class="w-full px-4 py-2 mb-4 text-[13px] dark:text-white transition rounded-xl bg-zinc-200 dark:bg-zinc-700/50">
{ clearTheme ? 'Clear Theme' : 'Select a Theme' }
</button>
<div class="relative w-full">
<button
onclick={() => editMode = !editMode}
class="absolute top-0 right-0 z-10 w-8 h-8 text-lg rounded-xl font-IconFamily bg-zinc-100 dark:bg-zinc-700">{editMode ? '\ue9e4' : '\uec38'}</button>
<BackgroundSelector isEditMode={editMode} bind:selectedBackground={selectedBackground} bind:selectNoBackground={selectNoBackground} />
<ThemeSelector isEditMode={editMode} />
</div>
{:else}
<div class="flex items-center justify-center w-full h-full">
<div class="text-lg">
Open SEQTA and use the embedded settings to access theme settings. 🫠
</div>
</div>
{/if}
</div>