web: dialog system & basic small dialog
This commit is contained in:
parent
c5fbff560b
commit
4232c3437b
7 changed files with 161 additions and 4 deletions
|
@ -1,3 +1,4 @@
|
|||
{
|
||||
"cobalt": "cobalt"
|
||||
"cobalt": "cobalt",
|
||||
"gotit": "got it"
|
||||
}
|
||||
|
|
28
web/src/components/dialog/DialogHolder.svelte
Normal file
28
web/src/components/dialog/DialogHolder.svelte
Normal file
|
@ -0,0 +1,28 @@
|
|||
<script lang="ts">
|
||||
import SmallDialog from "./SmallDialog.svelte";
|
||||
import dialogs from "$lib/dialogs";
|
||||
</script>
|
||||
|
||||
<div id="dialog-holder" aria-hidden="true">
|
||||
{#each $dialogs as dialog}
|
||||
{#if dialog.type === "small"}
|
||||
<SmallDialog
|
||||
id={dialog.id}
|
||||
title={dialog.title}
|
||||
bodyText={dialog.bodyText}
|
||||
bodySubText={dialog.bodySubText}
|
||||
buttons={dialog.buttons}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#dialog-holder {
|
||||
position: absolute;
|
||||
padding-top: env(safe-area-inset-bottom);
|
||||
height: calc(100%);
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
55
web/src/components/dialog/SmallDialog.svelte
Normal file
55
web/src/components/dialog/SmallDialog.svelte
Normal file
|
@ -0,0 +1,55 @@
|
|||
<script lang="ts">
|
||||
import { killDialog } from "$lib/dialogs";
|
||||
import type { DialogButton } from "$lib/types/dialog";
|
||||
|
||||
export let id: string;
|
||||
export let title: string = "";
|
||||
export let bodyText: string = "";
|
||||
export let bodySubText: string = "";
|
||||
export let buttons: DialogButton[];
|
||||
|
||||
let dialogParent: HTMLDialogElement;
|
||||
|
||||
const close = () => {
|
||||
if (dialogParent) {
|
||||
dialogParent.close();
|
||||
killDialog();
|
||||
}
|
||||
}
|
||||
|
||||
$: if (dialogParent) {
|
||||
dialogParent.showModal();
|
||||
}
|
||||
</script>
|
||||
|
||||
<dialog id="dialog-{id}" bind:this={dialogParent} class="small-dialog">
|
||||
<div class="popup-header">
|
||||
<h2>{title}</h2>
|
||||
</div>
|
||||
<div class="popup-body">
|
||||
{bodyText}
|
||||
{#if bodySubText}
|
||||
<div class="subtext">{bodySubText}</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="popup-buttons">
|
||||
{#each buttons as button}
|
||||
<button
|
||||
on:click={
|
||||
(async() => {
|
||||
await button.action();
|
||||
close();
|
||||
})
|
||||
}
|
||||
>
|
||||
{button.text}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<style>
|
||||
.small-dialog {
|
||||
max-width: 375px;
|
||||
}
|
||||
</style>
|
|
@ -3,14 +3,30 @@
|
|||
|
||||
import API from "$lib/api";
|
||||
import { device } from "$lib/device";
|
||||
|
||||
import { t } from "$lib/i18n/translations";
|
||||
|
||||
import { createDialog } from "$lib/dialogs";
|
||||
import type { DialogInfo } from "$lib/types/dialog";
|
||||
|
||||
export let url: string;
|
||||
|
||||
$: buttonText = ">>";
|
||||
$: buttonAltText = $t('a11y.save.download');
|
||||
$: isDisabled = false;
|
||||
|
||||
let defaultErrorPopup = {
|
||||
id: "save-error",
|
||||
type: "small",
|
||||
title: "",
|
||||
bodySubText: "",
|
||||
buttons: [{
|
||||
text: $t("general.gotit"),
|
||||
color: "gray",
|
||||
action: () => {},
|
||||
}]
|
||||
}
|
||||
|
||||
const changeDownloadButton = (state: string) => {
|
||||
isDisabled = true;
|
||||
switch (state) {
|
||||
|
@ -59,14 +75,20 @@
|
|||
changeDownloadButton("error");
|
||||
restoreDownloadButton();
|
||||
|
||||
return alert("couldn't access the api");
|
||||
return createDialog({
|
||||
...defaultErrorPopup as DialogInfo,
|
||||
bodyText: "couldn't access the api"
|
||||
})
|
||||
}
|
||||
|
||||
if (response.status === "error" || response.status === "rate-limit") {
|
||||
changeDownloadButton("error");
|
||||
restoreDownloadButton();
|
||||
|
||||
return alert(`error from api: ${response.text}`);
|
||||
return createDialog({
|
||||
...defaultErrorPopup as DialogInfo,
|
||||
bodyText: response.text
|
||||
})
|
||||
}
|
||||
|
||||
if (response.status === "redirect") {
|
||||
|
@ -90,7 +112,10 @@
|
|||
changeDownloadButton("error");
|
||||
restoreDownloadButton();
|
||||
|
||||
return alert("couldn't probe the stream");
|
||||
return createDialog({
|
||||
...defaultErrorPopup as DialogInfo,
|
||||
bodyText: "couldn't probe the stream"
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
23
web/src/lib/dialogs.ts
Normal file
23
web/src/lib/dialogs.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { readable, type Updater } from "svelte/store";
|
||||
import type { DialogInfo } from "$lib/types/dialog";
|
||||
|
||||
let update: (_: Updater<DialogInfo[]>) => void;
|
||||
|
||||
export default readable<DialogInfo[]>(
|
||||
[],
|
||||
(_, _update) => { update = _update }
|
||||
);
|
||||
|
||||
export function createDialog(newData: DialogInfo) {
|
||||
update((popups) => {
|
||||
popups.push(newData);
|
||||
return popups;
|
||||
});
|
||||
}
|
||||
|
||||
export function killDialog() {
|
||||
update((popups) => {
|
||||
popups.pop()
|
||||
return popups;
|
||||
});
|
||||
}
|
14
web/src/lib/types/dialog.ts
Normal file
14
web/src/lib/types/dialog.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
export type DialogButton = {
|
||||
text: string,
|
||||
color: string,
|
||||
action: () => unknown | Promise<unknown>
|
||||
}
|
||||
|
||||
export type DialogInfo = {
|
||||
id: string,
|
||||
type: "small",
|
||||
title: string,
|
||||
bodyText: string,
|
||||
bodySubText: string,
|
||||
buttons: DialogButton[]
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import Sidebar from "$components/sidebar/Sidebar.svelte";
|
||||
import NotchSticker from "$components/misc/NotchSticker.svelte";
|
||||
import DialogHolder from "$components/dialog/DialogHolder.svelte";
|
||||
|
||||
$: reduceMotion =
|
||||
$settings.appearance.reduceMotion
|
||||
|
@ -33,6 +34,7 @@
|
|||
{#if device.is.iPhone && app.is.installed}
|
||||
<NotchSticker />
|
||||
{/if}
|
||||
<DialogHolder />
|
||||
<Sidebar />
|
||||
<div id="content">
|
||||
<slot></slot>
|
||||
|
@ -302,6 +304,15 @@
|
|||
font-size: 11px;
|
||||
}
|
||||
|
||||
:global(dialog) {
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
padding: var(--padding);
|
||||
border-radius: var(--border-radius);
|
||||
border: none;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
:global(.subtext) {
|
||||
font-size: 12.5px;
|
||||
font-weight: 500;
|
||||
|
|
Loading…
Reference in a new issue