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 API from "$lib/api";
|
||||||
import { device } from "$lib/device";
|
import { device } from "$lib/device";
|
||||||
|
|
||||||
import { t } from "$lib/i18n/translations";
|
import { t } from "$lib/i18n/translations";
|
||||||
|
|
||||||
|
import { createDialog } from "$lib/dialogs";
|
||||||
|
import type { DialogInfo } from "$lib/types/dialog";
|
||||||
|
|
||||||
export let url: string;
|
export let url: string;
|
||||||
|
|
||||||
$: buttonText = ">>";
|
$: buttonText = ">>";
|
||||||
$: buttonAltText = $t('a11y.save.download');
|
$: buttonAltText = $t('a11y.save.download');
|
||||||
$: isDisabled = false;
|
$: isDisabled = false;
|
||||||
|
|
||||||
|
let defaultErrorPopup = {
|
||||||
|
id: "save-error",
|
||||||
|
type: "small",
|
||||||
|
title: "",
|
||||||
|
bodySubText: "",
|
||||||
|
buttons: [{
|
||||||
|
text: $t("general.gotit"),
|
||||||
|
color: "gray",
|
||||||
|
action: () => {},
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
const changeDownloadButton = (state: string) => {
|
const changeDownloadButton = (state: string) => {
|
||||||
isDisabled = true;
|
isDisabled = true;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
|
@ -59,14 +75,20 @@
|
||||||
changeDownloadButton("error");
|
changeDownloadButton("error");
|
||||||
restoreDownloadButton();
|
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") {
|
if (response.status === "error" || response.status === "rate-limit") {
|
||||||
changeDownloadButton("error");
|
changeDownloadButton("error");
|
||||||
restoreDownloadButton();
|
restoreDownloadButton();
|
||||||
|
|
||||||
return alert(`error from api: ${response.text}`);
|
return createDialog({
|
||||||
|
...defaultErrorPopup as DialogInfo,
|
||||||
|
bodyText: response.text
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === "redirect") {
|
if (response.status === "redirect") {
|
||||||
|
@ -90,7 +112,10 @@
|
||||||
changeDownloadButton("error");
|
changeDownloadButton("error");
|
||||||
restoreDownloadButton();
|
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 Sidebar from "$components/sidebar/Sidebar.svelte";
|
||||||
import NotchSticker from "$components/misc/NotchSticker.svelte";
|
import NotchSticker from "$components/misc/NotchSticker.svelte";
|
||||||
|
import DialogHolder from "$components/dialog/DialogHolder.svelte";
|
||||||
|
|
||||||
$: reduceMotion =
|
$: reduceMotion =
|
||||||
$settings.appearance.reduceMotion
|
$settings.appearance.reduceMotion
|
||||||
|
@ -33,6 +34,7 @@
|
||||||
{#if device.is.iPhone && app.is.installed}
|
{#if device.is.iPhone && app.is.installed}
|
||||||
<NotchSticker />
|
<NotchSticker />
|
||||||
{/if}
|
{/if}
|
||||||
|
<DialogHolder />
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
|
@ -302,6 +304,15 @@
|
||||||
font-size: 11px;
|
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) {
|
:global(.subtext) {
|
||||||
font-size: 12.5px;
|
font-size: 12.5px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|
Loading…
Reference in a new issue