mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-06 03:34:40 +00:00
fa41542ec6
made code use "DisplayName" in object for display name instead of object name, includes fallback in case the object is missing displayname
162 lines
5.5 KiB
Svelte
162 lines
5.5 KiB
Svelte
<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';
|
|
import Shortcuts from "@/seqta/content/links.json"
|
|
|
|
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 = (shortcut: any) => {
|
|
const value = $settingsState.shortcuts.find(s => s.name === shortcut);
|
|
if (value) {
|
|
value.enabled = !value.enabled;
|
|
settingsState.shortcuts = settingsState.shortcuts;
|
|
} else {
|
|
settingsState.shortcuts = [...settingsState.shortcuts, { name: shortcut, enabled: true }];
|
|
}
|
|
}
|
|
|
|
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>
|
|
|
|
<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="p-2 w-full rounded-lg border-0 transition 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="p-2 my-2 w-full rounded-lg border-0 transition 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(Shortcuts) as shortcut}
|
|
<div class="flex justify-between items-center px-4 py-3">
|
|
<div class="pr-4">
|
|
<!-- Use DisplayName if it exists, otherwise use the key (shortcut[0]) as a fallback -->
|
|
<h2 class="text-sm">{shortcut[1].DisplayName || shortcut[0]}</h2>
|
|
</div>
|
|
<Switch state={$settingsState.shortcuts.find(s => s.name === shortcut[0])?.enabled ?? false} onChange={() => switchChange(shortcut[0])} />
|
|
</div>
|
|
{/each}
|
|
|
|
<!-- Custom Shortcuts Section -->
|
|
{#each $settingsState.customshortcuts as shortcut, index}
|
|
<div class="flex justify-between items-center px-4 py-3">
|
|
{shortcut.name}
|
|
<button aria-label="Delete Shortcut" 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>
|