mirror of https://github.com/Budibase/budibase.git
8 changed files with 15 additions and 247 deletions
@ -1,217 +0,0 @@ |
|||
<script> |
|||
/** |
|||
* Confirmation is handled as a callback rather than an event to allow |
|||
* handling the result - meaning a parent can prevent the modal closing. |
|||
* |
|||
* A show/hide API is exposed as part of the modal and also via context for |
|||
* children inside the modal. |
|||
* "show" and "hide" events are emitted as visibility changes. |
|||
* |
|||
* Modals are rendered at the top of the DOM tree. |
|||
*/ |
|||
import { createEventDispatcher, setContext } from "svelte" |
|||
import { fade, fly } from "svelte/transition" |
|||
import Portal from "svelte-portal" |
|||
import { Button } from "@budibase/bbui" |
|||
import { ContextKey } from "./context" |
|||
const dispatch = createEventDispatcher() |
|||
|
|||
export let wide = false |
|||
export let padded = true |
|||
export let title = undefined |
|||
export let cancelText = "Cancel" |
|||
export let confirmText = "Confirm" |
|||
export let showCancelButton = true |
|||
export let showConfirmButton = true |
|||
export let onConfirm = () => {} |
|||
export let visible = false |
|||
export let loading = false |
|||
|
|||
let confirmLoading = false |
|||
$: disabled = loading || confirmLoading || $$restProps.disabled |
|||
|
|||
function show() { |
|||
if (visible) { |
|||
return |
|||
} |
|||
visible = true |
|||
dispatch("show") |
|||
} |
|||
|
|||
function hide() { |
|||
if (!visible) { |
|||
return |
|||
} |
|||
visible = false |
|||
dispatch("hide") |
|||
} |
|||
|
|||
async function confirm() { |
|||
loading = true |
|||
if (!onConfirm || (await onConfirm()) !== false) { |
|||
hide() |
|||
} |
|||
loading = false |
|||
} |
|||
|
|||
setContext(ContextKey, { show, hide }) |
|||
</script> |
|||
|
|||
{#if visible} |
|||
<Portal target="#modal-container"> |
|||
<div |
|||
class="overlay" |
|||
on:click|self={hide} |
|||
transition:fade={{ duration: 200 }}> |
|||
<div |
|||
class="scroll-wrapper" |
|||
on:click|self={hide} |
|||
transition:fly={{ y: 50 }}> |
|||
<div class="content-wrapper" on:click|self={hide}> |
|||
<div class="modal" class:wide class:padded> |
|||
{#if title} |
|||
<header> |
|||
<h5>{title}</h5> |
|||
<div class="header-content"> |
|||
<slot name="header" /> |
|||
</div> |
|||
</header> |
|||
{/if} |
|||
<slot /> |
|||
{#if showCancelButton || showConfirmButton} |
|||
<footer> |
|||
<div class="footer-content"> |
|||
<slot name="footer" /> |
|||
</div> |
|||
<div class="buttons"> |
|||
{#if showCancelButton} |
|||
<Button secondary on:click={hide}>{cancelText}</Button> |
|||
{/if} |
|||
{#if showConfirmButton} |
|||
<Button |
|||
primary |
|||
{...$$restProps} |
|||
{disabled} |
|||
on:click={confirm}> |
|||
{confirmText} |
|||
</Button> |
|||
{/if} |
|||
</div> |
|||
</footer> |
|||
{/if} |
|||
<i class="ri-close-line" on:click={hide} /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</Portal> |
|||
{/if} |
|||
|
|||
<style> |
|||
.overlay { |
|||
position: fixed; |
|||
left: 0; |
|||
right: 0; |
|||
top: 0; |
|||
bottom: 0; |
|||
overflow-x: hidden; |
|||
overflow-y: auto; |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: center; |
|||
align-items: center; |
|||
background-color: rgba(0, 0, 0, 0.25); |
|||
} |
|||
|
|||
.scroll-wrapper { |
|||
flex: 1 1 auto; |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: center; |
|||
align-items: flex-start; |
|||
max-height: 100%; |
|||
} |
|||
|
|||
.content-wrapper { |
|||
flex: 1 1 auto; |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: center; |
|||
align-items: flex-start; |
|||
width: 0; |
|||
} |
|||
|
|||
.modal { |
|||
background-color: white; |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: flex-start; |
|||
align-items: stretch; |
|||
box-shadow: 0 0 2.4rem 1.5rem rgba(0, 0, 0, 0.15); |
|||
position: relative; |
|||
flex: 0 0 400px; |
|||
margin: 2rem 0; |
|||
border-radius: var(--border-radius-m); |
|||
gap: var(--spacing-xl); |
|||
} |
|||
.modal.wide { |
|||
flex: 0 0 600px; |
|||
} |
|||
.modal.padded { |
|||
padding: var(--spacing-xl); |
|||
} |
|||
|
|||
header { |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-right: 40px; |
|||
} |
|||
header h5 { |
|||
margin: 0; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.header-content { |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: flex-end; |
|||
align-items: center; |
|||
} |
|||
|
|||
i { |
|||
position: absolute; |
|||
top: var(--spacing-xl); |
|||
right: var(--spacing-xl); |
|||
color: var(--ink); |
|||
font-size: var(--font-size-xl); |
|||
} |
|||
i:hover { |
|||
color: var(--grey-6); |
|||
cursor: pointer; |
|||
} |
|||
|
|||
footer { |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
gap: var(--spacing-m); |
|||
} |
|||
|
|||
.footer-content { |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: flex-start; |
|||
align-items: center; |
|||
} |
|||
|
|||
.buttons { |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: flex-end; |
|||
align-items: center; |
|||
gap: var(--spacing-m); |
|||
} |
|||
</style> |
|||
@ -1,10 +0,0 @@ |
|||
<div id="modal-container" /> |
|||
|
|||
<style> |
|||
#modal-container { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
z-index: 999; |
|||
} |
|||
</style> |
|||
@ -1 +0,0 @@ |
|||
export const ContextKey = "budibase-modal" |
|||
@ -1,3 +0,0 @@ |
|||
export { default as Modal } from "./Modal.svelte" |
|||
export { default as ModalContainer } from "./ModalContainer.svelte" |
|||
export { ContextKey } from "./context" |
|||
Loading…
Reference in new issue