diff options
Diffstat (limited to 'src/lib/Alerts.svelte')
-rw-r--r-- | src/lib/Alerts.svelte | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/src/lib/Alerts.svelte b/src/lib/Alerts.svelte new file mode 100644 index 0000000..1e4641b --- /dev/null +++ b/src/lib/Alerts.svelte @@ -0,0 +1,184 @@ +<!-- +MIT License + +Copyright (c) 2025 memdmp + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + --> + +<script lang="ts" module> + let ids = $state([] as number[]); + let idCtr = $state(0); + let dialogObject = $state({ + open: false, + title: '', + content: '', + closeBtn: false, + buttons: [ + { + text: 'Close', + }, + ] as { + text: string; + callback?: () => + | { + success: true; + content: string; + } + | { + success: false; + content: any; + }; + }[], + }); + let closeAlert = (m: string) => void 0; + let cancelAlert = (err?: any) => void 0; + let dialogPromises = [] as Promise<string>[]; + let dialogPromise = Promise.resolve(''); + const createDialogPromise = (setAlertTo?: typeof dialogObject) => ( + dialogPromises.push( + (dialogPromise = new Promise<string>((rs, rj) => { + let oldCloseAlert = closeAlert; + let oldCancelAlert = cancelAlert; + closeAlert = (m) => { + closeAlert = oldCloseAlert; + dialogObject = setAlertTo ?? dialogObject; + rs(m); + }; + cancelAlert = (m) => { + cancelAlert = oldCancelAlert; + dialogObject = setAlertTo ?? dialogObject; + rj(m); + }; + })) + ), + dialogPromise + ); + export const alert = async ( + message: string, + options?: { + title?: string; + /** prioritise over existing alerts */ + priority?: boolean; + /** if we can close the dialog with the x at the top */ + canClose?: boolean; + /** button list */ + buttons?: typeof dialogObject.buttons; + } + ) => { + if (!options?.priority) await Promise.allSettled(dialogPromises); + const oldAlert = dialogObject; + dialogObject = { + open: true, + title: options?.title ?? 'Alert', + closeBtn: options?.canClose ?? true, + buttons: options?.buttons ?? [ + { + text: 'Close', + }, + ], + content: message, + }; + return createDialogPromise(oldAlert); + }; + export const confirm = async ( + message: string, + options?: Omit<Parameters<typeof alert>[1], 'buttons'> + ) => { + const rs = await alert(message, { + title: 'Confirmation', + ...(options ?? {}), + buttons: [ + { + text: 'OK', + callback: () => ({ + success: true, + content: 'OK', + }), + }, + { + text: 'Cancel', + callback: () => ({ + success: true, + content: 'Cancel', + }), + }, + ], + }); + return rs === 'OK'; + }; +</script> + +<script lang="ts"> + import { onDestroy, onMount } from 'svelte'; + + let dialog = $state(null as HTMLDialogElement | null); + let id: number = $state(-1); + onMount(() => { + id = idCtr++; + ids.push(id); + }); + onDestroy(() => (ids = ids.filter((v) => v !== id))); + $effect(() => { + dialog?.showModal(); + }); +</script> + +{#if ids[0] === id && dialogObject.open} + <section> + <dialog bind:this={dialog}> + <form method="dialog"> + <div class="heading"> + <div class="wrapper"> + <div class="icon"></div> + <p class="text">{dialogObject.title}</p> + </div> + <button + class="close" + aria-label="Close" + disabled={!dialogObject.closeBtn} + onclick={() => { + cancelAlert(new Error('User Cancelled Alert')); + }} + ></button> + </div> + <div class="content"> + {dialogObject.content} + </div> + <menu class="footer-btns" style="gap: 4px; display: flex;"> + {#each dialogObject.buttons as button} + <button + onclick={(e) => { + const callback = + button.callback ?? + (() => ({ + success: true, + content: button.text, + })); + const result = callback(); + if (result.success) closeAlert(result.content); + else cancelAlert(result.content); + }}>{button.text}</button + > + {/each} + </menu> + </form> + </dialog> + </section> +{/if} |