feat(frontend): custom preference editor

This commit is contained in:
sam 2024-12-15 00:32:11 +01:00
parent 41a008799a
commit 507b9c3f4c
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
10 changed files with 302 additions and 8 deletions

View file

@ -11,15 +11,29 @@
id?: string;
onclick?: MouseEventHandler<HTMLButtonElement>;
outline?: boolean;
active?: boolean;
class?: string;
};
let { icon, tooltip, color = "primary", type, id, onclick, outline }: Props = $props();
let {
icon,
tooltip,
color = "primary",
type,
id,
onclick,
outline,
active,
class: className,
}: Props = $props();
</script>
<button
{id}
{type}
use:tippy={{ content: tooltip }}
class="btn {outline ? `btn-outline-${color}` : `btn-${color}`}"
class="btn {outline ? `btn-outline-${color}` : `btn-${color}`} {className || ''}"
class:active
aria-pressed={active}
{onclick}
>
<Icon name={icon} />

View file

@ -0,0 +1,41 @@
<script lang="ts">
import { type CustomPreference } from "$api/models";
import IconButton from "$components/IconButton.svelte";
import { t } from "$lib/i18n";
import PreferenceIconSelector from "./PreferenceIconSelector.svelte";
import PreferenceSizeEditor from "./PreferenceSizeEditor.svelte";
type Props = { pref: CustomPreference & { id: string | undefined }; remove: () => void };
let { pref = $bindable(), remove }: Props = $props();
</script>
<div class="input-group my-1">
<PreferenceIconSelector bind:icon={pref.icon} />
<input
type="text"
bind:value={pref.tooltip}
class="form-control"
placeholder={$t("editor.tooltip-hint")}
/>
<PreferenceSizeEditor bind:size={pref.size} />
<IconButton
color="secondary"
icon={pref.favourite ? "star-fill" : "star"}
onclick={() => (pref.favourite = !pref.favourite)}
active={pref.favourite}
tooltip={$t("editor.custom-preference-favourite")}
/>
<IconButton
color="secondary"
icon="fonts"
onclick={() => (pref.muted = !pref.muted)}
active={pref.muted}
tooltip={$t("editor.custom-preference-muted")}
/>
<IconButton
color="danger"
icon="trash3"
tooltip={$t("editor.remove-entry")}
onclick={() => remove()}
/>
</div>

View file

@ -0,0 +1,80 @@
<script lang="ts">
import IconButton from "$components/IconButton.svelte";
import { t } from "$lib/i18n";
import icons from "$lib/icons";
import {
ButtonDropdown,
DropdownToggle,
Icon,
DropdownMenu,
DropdownItem,
} from "@sveltestrap/sveltestrap";
import tippy from "tippy.js";
const MAX_VISIBLE_ITEMS = 20;
type Props = { icon: string };
let { icon = $bindable() }: Props = $props();
let search = $state("");
let showAll = $state(false);
let filteredIcons = $derived(
icons
.filter((icon) => icon.toLowerCase().includes(search.toLowerCase()))
.sort()
.slice(0, showAll ? undefined : MAX_VISIBLE_ITEMS),
);
let totalIcons = $derived(
icons.filter((icon) => icon.toLowerCase().includes(search.toLowerCase())).length,
);
</script>
<ButtonDropdown autoClose={false}>
<span use:tippy={{ content: $t("editor.icons-change-icon") }}>
<DropdownToggle color="secondary" caret>
<Icon name={icon} />
<span class="visually-hidden">{$t("editor.icons-change-icon")}</span>
</DropdownToggle>
</span>
<DropdownMenu>
<p class="px-2">
<input
type="text"
class="form-control"
bind:value={search}
placeholder={$t("editor.flag-search-placeholder")}
/>
</p>
<ul class="list-unstyled icon-list text-center">
{#each filteredIcons as selectable}
<IconButton
icon={selectable}
tooltip={selectable}
type="button"
color="secondary"
outline
class="border-0"
onclick={() => (icon = selectable)}
/>
{/each}
</ul>
{#if totalIcons > MAX_VISIBLE_ITEMS || showAll}
<DropdownItem divider />
<DropdownItem toggle onclick={() => (showAll = !showAll)}>
{#if showAll}
{$t("editor.icons-stop-showing-all")}
{:else}
{$t("editor.icons-show-all", { count: totalIcons })}
{/if}
</DropdownItem>
{/if}
</DropdownMenu>
</ButtonDropdown>
<style>
.icon-list {
max-height: 200px;
overflow-y: auto;
}
</style>

View file

@ -0,0 +1,43 @@
<script lang="ts">
import { PreferenceSize } from "$api/models";
import { t } from "$lib/i18n";
import tippy from "$lib/tippy";
import {
ButtonDropdown,
DropdownItem,
DropdownMenu,
DropdownToggle,
} from "@sveltestrap/sveltestrap";
import Type from "svelte-bootstrap-icons/lib/Type.svelte";
type Props = { size: PreferenceSize };
let { size = $bindable() }: Props = $props();
</script>
<ButtonDropdown>
<span use:tippy={{ content: $t("editor.custom-preference-size") }}>
<DropdownToggle color="secondary" caret>
<Type /> <span class="visually-hidden">{$t("editor.custom-preference-size")}</span>
</DropdownToggle>
</span>
<DropdownMenu>
<DropdownItem
active={size === PreferenceSize.Large}
on:click={() => (size = PreferenceSize.Large)}
>
{$t("editor.custom-preference-size-large")}
</DropdownItem>
<DropdownItem
active={size === PreferenceSize.Normal}
on:click={() => (size = PreferenceSize.Normal)}
>
{$t("editor.custom-preference-size-medium")}
</DropdownItem>
<DropdownItem
active={size === PreferenceSize.Small}
on:click={() => (size = PreferenceSize.Small)}
>
{$t("editor.custom-preference-size-small")}
</DropdownItem>
</DropdownMenu>
</ButtonDropdown>