web: settings reset confirmation, icons for small dialog

- cleaned up dialog i18n
- better red color
- made :active state visible for dialog buttons on mobile
- better body padding in small dialog
- better small dialog typing with optional values
This commit is contained in:
wukko 2024-07-20 21:48:17 +06:00
parent def6e26b9f
commit d7bf98a80b
No known key found for this signature in database
GPG key ID: 3E30B3F26C7B4AA2
10 changed files with 117 additions and 32 deletions

8
web/i18n/en/dialog.json Normal file
View file

@ -0,0 +1,8 @@
{
"button.gotit": "got it",
"button.cancel": "cancel",
"button.erase": "erase",
"erase.title": "erase all settings?",
"erase.body": "are you sure you want to reset all settings? this action is immediate and irreversible."
}

View file

@ -1,4 +1,3 @@
{ {
"cobalt": "cobalt", "cobalt": "cobalt"
"gotit": "got it"
} }

View file

@ -93,5 +93,7 @@
"advanced.debug": "debug", "advanced.debug": "debug",
"advanced.debug.title": "enable debug features", "advanced.debug.title": "enable debug features",
"advanced.debug.description": "gives you access to a page with app & device info useful for debugging.", "advanced.debug.description": "gives you access to a page with app & device info useful for debugging.",
"advanced.data": "settings data",
"advanced.reset": "erase all settings" "advanced.reset": "erase all settings"
} }

View file

@ -1,22 +1,55 @@
<script lang="ts"> <script lang="ts">
import { t } from '$lib/i18n/translations'; import { t } from "$lib/i18n/translations";
import IconTrash from '@tabler/icons-svelte/IconTrash.svelte'; import { resetSettings } from "$lib/state/settings";
import { resetSettings } from '$lib/state/settings';
import IconTrash from "@tabler/icons-svelte/IconTrash.svelte";
import { createDialog } from "$lib/dialogs";
const resetDialog = () => {
createDialog({
id: "wipe-confirm",
type: "small",
icon: "warn-red",
title: $t("dialog.erase.title"),
bodyText: $t("dialog.erase.body"),
buttons: [
{
text: $t("dialog.button.cancel"),
main: false,
action: () => {},
},
{
text: $t("dialog.button.erase"),
color: "red",
main: true,
action: () => resetSettings(),
},
],
});
};
</script> </script>
<button id="setting-button-reset" class="button" on:click={resetSettings}> <button id="setting-button-reset" class="button" on:click={resetDialog}>
<IconTrash /> { $t('settings.advanced.reset') } <IconTrash />
{$t("settings.advanced.reset")}
</button> </button>
<style> <style>
button { #setting-button-reset {
background-color: var(--red); background-color: var(--red);
color: var(--white); color: var(--white);
width: max-content; width: max-content;
text-align: left; text-align: left;
} }
button:hover { #setting-button-reset:hover {
background-color: var(--dark-red); background-color: var(--dark-red);
} }
#setting-button-reset :global(svg) {
stroke-width: 2px;
height: 24px;
width: 24px;
}
</style> </style>

View file

@ -12,6 +12,7 @@
id={dialog.id} id={dialog.id}
title={dialog.title} title={dialog.title}
meowbalt={dialog.meowbalt} meowbalt={dialog.meowbalt}
icon={dialog.icon}
bodyText={dialog.bodyText} bodyText={dialog.bodyText}
bodySubText={dialog.bodySubText} bodySubText={dialog.bodySubText}
buttons={dialog.buttons} buttons={dialog.buttons}

View file

@ -2,13 +2,16 @@
import { tick } from "svelte"; import { tick } from "svelte";
import { killDialog } from "$lib/dialogs"; import { killDialog } from "$lib/dialogs";
import type { DialogButton } from "$lib/types/dialog"; import type { DialogButton, SmallDialogIcons } from "$lib/types/dialog";
import Meowbalt from "$components/misc/Meowbalt.svelte"; import Meowbalt from "$components/misc/Meowbalt.svelte";
import type { MeowbaltEmotions } from "$lib/types/meowbalt"; import type { MeowbaltEmotions } from "$lib/types/meowbalt";
import IconAlertTriangle from "@tabler/icons-svelte/IconAlertTriangle.svelte";
export let id: string; export let id: string;
export let meowbalt: MeowbaltEmotions; export let meowbalt: MeowbaltEmotions | undefined;
export let icon: SmallDialogIcons | undefined;
export let title: string = ""; export let title: string = "";
export let bodyText: string = ""; export let bodyText: string = "";
export let bodySubText: string = ""; export let bodySubText: string = "";
@ -45,12 +48,19 @@
<Meowbalt emotion={meowbalt} /> <Meowbalt emotion={meowbalt} />
</div> </div>
{/if} {/if}
{#if title}
<div class="popup-header">
<h2>{title}</h2>
</div>
{/if}
<div class="popup-body"> <div class="popup-body">
{#if title || icon}
<div class="popup-header">
{#if icon === "warn-red"}
<div class="popup-icon {icon}">
<IconAlertTriangle />
</div>
{/if}
{#if title}
<h2>{title}</h2>
{/if}
</div>
{/if}
{#if bodyText} {#if bodyText}
<div class="body-text" tabindex="-1">{bodyText}</div> <div class="body-text" tabindex="-1">{bodyText}</div>
{/if} {/if}
@ -61,7 +71,7 @@
<div class="popup-buttons"> <div class="popup-buttons">
{#each buttons as button} {#each buttons as button}
<button <button
class="button elevated popup-button" class="button popup-button {button.color}"
class:active={button.main} class:active={button.main}
on:click={async () => { on:click={async () => {
await button.action(); await button.action();
@ -116,8 +126,15 @@
gap: var(--padding); gap: var(--padding);
} }
.popup-body {
gap: 8px;
}
.small-dialog { .small-dialog {
--small-dialog-padding: 18px; --small-dialog-padding: 18px;
align-items: center;
text-align: center;
max-width: 340px; max-width: 340px;
width: calc( width: calc(
100% - var(--padding) * 2 - var(--small-dialog-padding) * 2 100% - var(--padding) * 2 - var(--small-dialog-padding) * 2
@ -144,8 +161,6 @@
.small-dialog.meowbalt-visible { .small-dialog.meowbalt-visible {
padding-top: calc(var(--padding) * 4); padding-top: calc(var(--padding) * 4);
align-items: center;
text-align: center;
} }
.meowbalt-container { .meowbalt-container {
@ -153,8 +168,16 @@
top: -120px; top: -120px;
} }
.popup-header { .popup-header h2 {
color: var(--secondary); color: var(--secondary);
font-size: 19px;
}
.popup-header .popup-icon.warn-red :global(svg) {
stroke-width: 1.5px;
height: 50px;
width: 50px;
stroke: var(--red);
} }
.body-text { .body-text {
@ -175,6 +198,8 @@
flex-direction: row; flex-direction: row;
width: 100%; width: 100%;
gap: calc(var(--padding) / 2); gap: calc(var(--padding) / 2);
overflow: scroll;
border-radius: var(--border-radius);
} }
.popup-button { .popup-button {
@ -182,10 +207,18 @@
height: 40px; height: 40px;
} }
.popup-button.red {
background-color: var(--red);
}
.popup-button:not(.active) { .popup-button:not(.active) {
background-color: var(--button-elevated); background-color: var(--button-elevated);
} }
.popup-button:not(.active):active {
background-color: var(--button-elevated-hover);
}
.popup-button:not(:focus-visible) { .popup-button:not(:focus-visible) {
box-shadow: none; box-shadow: none;
} }

View file

@ -16,16 +16,16 @@
$: buttonAltText = $t('a11y.save.download'); $: buttonAltText = $t('a11y.save.download');
$: isDisabled = false; $: isDisabled = false;
let defaultErrorPopup = { let defaultErrorPopup: DialogInfo = {
id: "save-error", id: "save-error",
type: "small", type: "small",
meowbalt: "error", meowbalt: "error",
buttons: [{ buttons: [{
text: $t("general.gotit"), text: $t("dialog.button.gotit"),
main: true, main: true,
action: () => {}, action: () => {},
}] }]
} as DialogInfo }
const changeDownloadButton = (state: string) => { const changeDownloadButton = (state: string) => {
isDisabled = true; isDisabled = true;

View file

@ -2,17 +2,20 @@ import type { MeowbaltEmotions } from "$lib/types/meowbalt";
export type DialogButton = { export type DialogButton = {
text: string, text: string,
color: "blue" | "red" | "default", color?: "red",
main: boolean, main: boolean,
action: () => unknown | Promise<unknown> action: () => unknown | Promise<unknown>
} }
export type SmallDialogIcons = "warn-red";
export type DialogInfo = { export type DialogInfo = {
id: string, id: string,
type: "small", type: "small",
meowbalt: MeowbaltEmotions | "", meowbalt?: MeowbaltEmotions,
title: string, icon?: SmallDialogIcons,
bodyText: string, title?: string,
bodySubText: string, bodyText?: string,
bodySubText?: string,
buttons: DialogButton[], buttons: DialogButton[],
} }

View file

@ -60,8 +60,9 @@
--white: #ffffff; --white: #ffffff;
--gray: #75757e; --gray: #75757e;
--red: #f92f2f;
--dark-red: #df0707; --red: #ed2236;
--dark-red: #d61c2e;
--green: #51cf5e; --green: #51cf5e;
--blue: #2f8af9; --blue: #2f8af9;
@ -127,6 +128,7 @@
--secondary: #e1e1e1; --secondary: #e1e1e1;
--gray: #818181; --gray: #818181;
--blue: #2a7ce1; --blue: #2a7ce1;
--green: #37aa42; --green: #37aa42;
@ -288,6 +290,10 @@
background-color: var(--secondary); background-color: var(--secondary);
} }
:global(.button.active:active) {
background-color: var(--button-active-hover);
}
/* important is used because active class is toggled by state */ /* important is used because active class is toggled by state */
/* and added to the end of the list, taking priority */ /* and added to the end of the list, taking priority */
:global(.active:focus-visible) { :global(.active:focus-visible) {

View file

@ -15,6 +15,6 @@
/> />
</SettingsCategory> </SettingsCategory>
<SettingsCategory sectionId="settings" title={$t("tabs.settings")}> <SettingsCategory sectionId="data" title={$t("settings.advanced.data")}>
<ResetSettingsButton /> <ResetSettingsButton />
</SettingsCategory> </SettingsCategory>