[web] Improve modals

This commit is contained in:
orosmatthew 2023-08-26 12:30:22 -04:00
parent 863254f846
commit bb722b437e
14 changed files with 175 additions and 36 deletions

View File

@ -0,0 +1,76 @@
<script lang="ts">
import { onMount } from 'svelte';
import type bootstrap from 'bootstrap';
import { beforeNavigate } from '$app/navigation';
export let modalTitle = 'Confirm';
export let modalText = 'Are you sure?';
let confirmModal: HTMLDivElement;
let modal: bootstrap.Modal | undefined;
let confirmAction: (() => void) | undefined;
let cancelAction: (() => void) | undefined;
export function confirm() {
if (confirmAction !== undefined) {
confirmAction();
}
}
export function cancel() {
if (cancelAction !== undefined) {
cancelAction();
}
}
export async function prompt(text = 'Are you sure?', title = 'Confirm'): Promise<boolean> {
modalText = text;
modalTitle = title;
if (modal === undefined) {
return false;
}
modal.show();
return new Promise((resolve) => {
confirmAction = () => {
resolve(true);
modal?.hide();
};
cancelAction = () => {
resolve(false);
modal?.hide();
};
});
}
onMount(async () => {
const bootstrap = await import('bootstrap');
modal = new bootstrap.Modal(confirmModal);
});
beforeNavigate(() => {
modal?.hide();
});
</script>
<div bind:this={confirmModal} class="modal fade" tabindex="-1" data-bs-backdrop="static">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="exampleModalLabel">{modalTitle}</h1>
</div>
<div class="modal-body">
{#if $$slots.default}
<slot />
{:else}
{modalText}
{/if}
</div>
<div class="modal-footer">
<button on:click={cancel} type="button" class="btn btn-secondary">Cancel</button>
<button on:click={confirm} type="button" class="btn btn-primary">Confirm</button>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,46 @@
<script lang="ts">
import { page } from '$app/stores';
import { afterUpdate } from 'svelte';
import { slide } from 'svelte/transition';
let dismissed = false;
let success = false;
let message: string | undefined;
let manualPopup = false;
$: if ($page.form !== null) {
manualPopup = false;
dismissed = false;
success = $page.form.success;
message = $page.form.message;
}
afterUpdate(() => {
if ($page.form !== null) {
success = $page.form.success;
}
});
export function popup(data: { success: boolean; message?: string }) {
dismissed = false;
manualPopup = true;
success = data.success;
message = data.message;
}
</script>
{#if ($page.form !== null && dismissed === false) || (manualPopup === true && dismissed === false)}
<div
transition:slide|local
class={`mt-2 mb-2 alert alert-dismissible alert-${success ? 'success' : 'danger'}`}
>
{success ? 'Success' : message ?? 'Unknown error'}
<button
on:click={() => {
dismissed = true;
}}
type="button"
class="btn-close"
/>
</div>
{/if}

View File

@ -8,13 +8,11 @@
<title>Contests</title> <title>Contests</title>
</svelte:head> </svelte:head>
<h1 style="text-align:center" class="mb-4"><i class="bi bi-flag"></i> Contests</h1> <h1 style="text-align:center" class="mb-1"><i class="bi bi-flag"></i> Contests</h1>
<div class="row"> <div class="d-flex flex-row justify-content-end">
<div class="text-end">
<a href="/admin/contests/create" class="btn btn-outline-success">Create</a> <a href="/admin/contests/create" class="btn btn-outline-success">Create</a>
</div> </div>
</div>
<div class="mt-3 list-group"> <div class="mt-3 list-group">
{#each data.contests as contest} {#each data.contests as contest}

View File

@ -1,26 +1,29 @@
<script lang="ts"> <script lang="ts">
import { enhance } from '$app/forms'; import { enhance } from '$app/forms';
import { page } from '$app/stores'; import { page } from '$app/stores';
import type { Actions, PageData } from './$types'; import ConfirmModal from '$lib/ConfirmModal.svelte';
import FormAlert from '$lib/FormAlert.svelte';
import type { PageData } from './$types';
export let data: PageData; export let data: PageData;
export let form: Actions;
let confirmModal: ConfirmModal;
</script> </script>
<svelte:head> <svelte:head>
<title>{data.name}</title> <title>{data.name}</title>
</svelte:head> </svelte:head>
<ConfirmModal bind:this={confirmModal} />
<h1 style="text-align:center" class="mb-4">{data.name}</h1> <h1 style="text-align:center" class="mb-4">{data.name}</h1>
<FormAlert />
{#if data.activeTeams !== 0} {#if data.activeTeams !== 0}
<div class="alert alert-success">In Progress</div> <div class="alert alert-success">In Progress</div>
{/if} {/if}
{#if form && !form.success}
<div class="alert alert-danger">An error occured</div>
{/if}
<div class="row"> <div class="row">
<div class="col-6"> <div class="col-6">
<a href="/admin/contests" class="btn btn-outline-primary">All Contests</a> <a href="/admin/contests" class="btn btn-outline-primary">All Contests</a>
@ -28,12 +31,12 @@
<div class="col-6 text-end"> <div class="col-6 text-end">
<form <form
method="POST" method="POST"
use:enhance={({ cancel }) => { use:enhance={async ({ cancel }) => {
if (!confirm('Are you sure?')) { if ((await confirmModal.prompt('Are you sure?')) !== true) {
cancel(); cancel();
} }
return async ({ update }) => { return async ({ update }) => {
update(); await update();
}; };
}} }}
> >

View File

@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import { enhance } from '$app/forms'; import { enhance } from '$app/forms';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import FormAlert from '$lib/FormAlert.svelte';
import type { Actions, PageData } from './$types'; import type { Actions, PageData } from './$types';
export let data: PageData; export let data: PageData;
@ -39,14 +40,12 @@
<title>Create Contest</title> <title>Create Contest</title>
</svelte:head> </svelte:head>
<h1 style="text-align:center" class="mb-4">Create Contest</h1> <h1 style="text-align:center" class="mb-4"><i class="bi bi-flag"></i> Create Contest</h1>
<FormAlert />
<a href="/admin/contests" class="mb-3 btn btn-outline-secondary">Cancel</a> <a href="/admin/contests" class="mb-3 btn btn-outline-secondary">Cancel</a>
{#if form && !form.success}
<div class="alert alert-danger">Invalid entry</div>
{/if}
<form method="POST" action="?/create" use:enhance> <form method="POST" action="?/create" use:enhance>
<h4>Name</h4> <h4>Name</h4>
<input name="name" class="form-control" /> <input name="name" class="form-control" />

View File

@ -6,6 +6,7 @@
import { enhance } from '$app/forms'; import { enhance } from '$app/forms';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { stretchTextarea } from '$lib/util'; import { stretchTextarea } from '$lib/util';
import FormAlert from '$lib/FormAlert.svelte';
export let data: PageData; export let data: PageData;
export let form: Actions; export let form: Actions;
@ -58,11 +59,7 @@
<h1 style="text-align:center" class="mb-4">Review Submission</h1> <h1 style="text-align:center" class="mb-4">Review Submission</h1>
{#if form && !form.success} <FormAlert />
<div class="alert alert-danger">Submission was not successful</div>
{:else if form && form.success}
<div class="alert alert-success">Success!</div>
{/if}
<div class="mb-3 col"> <div class="mb-3 col">
<a href="/admin/reviews" class="btn btn-outline-primary">All Reviews</a> <a href="/admin/reviews" class="btn btn-outline-primary">All Reviews</a>

View File

@ -8,7 +8,7 @@
<title>Problems</title> <title>Problems</title>
</svelte:head> </svelte:head>
<h1 style="text-align:center" class="mb-4"><i class="bi bi-question-circle"></i> Problems</h1> <h1 style="text-align:center" class="mb-1"><i class="bi bi-question-circle"></i> Problems</h1>
<div class="row mb-3"> <div class="row mb-3">
<div class="text-end"> <div class="text-end">

View File

@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { page } from '$app/stores'; import { page } from '$app/stores';
import ConfirmModal from '$lib/ConfirmModal.svelte';
import { stretchTextarea } from '$lib/util'; import { stretchTextarea } from '$lib/util';
import type { Actions, PageData } from './$types'; import type { Actions, PageData } from './$types';
@ -11,7 +12,7 @@
export let form: Actions; export let form: Actions;
async function deleteProblem() { async function deleteProblem() {
const sure = confirm('Are you sure?'); const sure = await confirmModal.prompt('Are you sure?');
if (!sure) { if (!sure) {
return; return;
} }
@ -23,9 +24,15 @@
error = true; error = true;
} }
} }
let confirmModal: ConfirmModal;
</script> </script>
<h1 style="text-align:center" class="mb-4">{data.problemData.friendlyName}</h1> <ConfirmModal bind:this={confirmModal} />
<h1 style="text-align:center" class="mb-1">
<i class="bi bi-question-circle"></i> Problem - {data.problemData.friendlyName}
</h1>
{#if error} {#if error}
<div class="alert alert-danger"> <div class="alert alert-danger">

View File

@ -27,7 +27,7 @@
<title>Reviews</title> <title>Reviews</title>
</svelte:head> </svelte:head>
<h1 style="text-align:center" class="mb-4"><i class="bi bi-eye"></i> Reviews</h1> <h1 style="text-align:center" class="mb-1"><i class="bi bi-eye"></i> Reviews</h1>
<div class="mb-3 text-end"> <div class="mb-3 text-end">
{#if updating} {#if updating}

View File

@ -25,7 +25,7 @@
<title>Admin Scoreboard</title> <title>Admin Scoreboard</title>
</svelte:head> </svelte:head>
<h1 style="text-align:center" class="mb-4"><i class="bi bi-trophy"></i> Admin Scoreboards</h1> <h1 style="text-align:center" class="mb-1"><i class="bi bi-trophy"></i> Admin Scoreboards</h1>
<div class="text-end"> <div class="text-end">
{#if updating} {#if updating}

View File

@ -5,6 +5,7 @@
import 'diff2html/bundles/css/diff2html.min.css'; import 'diff2html/bundles/css/diff2html.min.css';
import { enhance } from '$app/forms'; import { enhance } from '$app/forms';
import { stretchTextarea } from '$lib/util'; import { stretchTextarea } from '$lib/util';
import ConfirmModal from '$lib/ConfirmModal.svelte';
export let data: PageData; export let data: PageData;
export let form: Actions; export let form: Actions;
@ -25,12 +26,16 @@
} }
} }
}); });
let confirmModal: ConfirmModal;
</script> </script>
<svelte:head> <svelte:head>
<title>Submission</title> <title>Submission</title>
</svelte:head> </svelte:head>
<ConfirmModal bind:this={confirmModal} />
<h1 style="text-align:center" class="mb-4">Submission</h1> <h1 style="text-align:center" class="mb-4">Submission</h1>
{#if form && !form.success} {#if form && !form.success}
@ -45,12 +50,12 @@
<form <form
method="POST" method="POST"
action="?/delete" action="?/delete"
use:enhance={({ cancel }) => { use:enhance={async ({ cancel }) => {
if (!confirm('Are you sure?')) { if ((await confirmModal.prompt('Are you sure?')) !== true) {
cancel(); cancel();
} }
return async ({ update }) => { return async ({ update }) => {
update(); await update();
}; };
}} }}
> >

View File

@ -16,7 +16,7 @@
<title>Teams</title> <title>Teams</title>
</svelte:head> </svelte:head>
<h1 style="text-align:center" class="mb-4"><i class="bi bi-people"></i> Teams</h1> <h1 style="text-align:center" class="mb-1"><i class="bi bi-people"></i> Teams</h1>
{#if form && !form.success} {#if form && !form.success}
<div class="alert alert-danger">Invalid action</div> <div class="alert alert-danger">Invalid action</div>

View File

@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import { enhance } from '$app/forms'; import { enhance } from '$app/forms';
import ConfirmModal from '$lib/ConfirmModal.svelte';
import { genPassword } from '../util'; import { genPassword } from '../util';
import type { Actions, PageData } from './$types'; import type { Actions, PageData } from './$types';
@ -16,12 +17,16 @@
const passEntry = document.getElementById('pass_entry') as HTMLInputElement; const passEntry = document.getElementById('pass_entry') as HTMLInputElement;
passEntry.value = genPassword(); passEntry.value = genPassword();
} }
let confirmModal: ConfirmModal;
</script> </script>
<svelte:head> <svelte:head>
<title>Team</title> <title>Team</title>
</svelte:head> </svelte:head>
<ConfirmModal bind:this={confirmModal} />
<h1 style="text-align:center" class="mb-4">{data.team.name}</h1> <h1 style="text-align:center" class="mb-4">{data.team.name}</h1>
<div class="row"> <div class="row">
@ -32,12 +37,12 @@
<form <form
method="POST" method="POST"
action="?/delete" action="?/delete"
use:enhance={({ cancel }) => { use:enhance={async ({ cancel }) => {
if (!confirm('Are you sure?')) { if ((await confirmModal.prompt('Are you sure?')) !== true) {
cancel(); cancel();
} }
return async ({ update }) => { return async ({ update }) => {
update(); await update();
}; };
}} }}
> >

View File

@ -7,6 +7,9 @@ const config = {
kit: { kit: {
adapter: adapter() adapter: adapter()
},
vitePlugin: {
inspector: true
} }
}; };