-
Notifications
You must be signed in to change notification settings - Fork 1
/
Modal.svelte
78 lines (72 loc) · 2.96 KB
/
Modal.svelte
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<!-- @component
The [`Modal`](https://github.com/txstate-etc/svelte-components/blob/main/docs/Modal.md#Modal) component is designed to block out the screen and focus the user on the content inside the Modal.
It provides only the backdrop and a scrollable container for your content. If your content should have a background color, be sure to add it yourself.
Any time the Modal is in the DOM, it will take over the screen. You make it go away by removing it from the DOM.
-->
<script lang="ts">
import { createEventDispatcher, onMount } from 'svelte'
import { randomid } from 'txstate-utils'
import FocusLock, { FocusLockStack } from './FocusLock.svelte'
import { portal } from '$lib/actions'
export let opaque = false
export let containerClass = ''
export let escapable = true
export let hidefocus = true
export let hidefocuslabel: string | undefined = undefined
export let initialfocus: string | undefined = undefined
export let returnfocusto: HTMLElement | undefined = undefined
export let usePortal: HTMLElement | undefined = undefined
/** If you expect any popup menus to be added to the body, we need to know that they
are considered to be part of the focus lock, or else the modal will be dismissed
when the user clicks inside. Use commas to include multiple selectors. */
export let includeselector: string | undefined = undefined
export let focusId = randomid()
const dispatch = createEventDispatcher()
const endmodal = () => {
dispatch('escape')
}
let stackPosition: number
function onFocusLockUpdate () {
stackPosition = FocusLockStack.findIndex(f => f.focusId === focusId)
}
onMount(() => {
onFocusLockUpdate()
document.body.style.marginRight = (window.innerWidth - document.body.clientWidth) + 'px'
document.body.style.overflow = 'hidden'
return () => {
document.body.style.overflow = ''
document.body.style.marginRight = ''
}
})
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div use:portal={usePortal} class="modal-backdrop" style:--modal-z={stackPosition * 10 + 3000} class:opaque on:mousedown|stopPropagation|preventDefault={() => { escapable && endmodal() }}>
<FocusLock bind:focusId class="modal-container {containerClass}" {includeselector} {escapable} on:escape {hidefocus} {hidefocuslabel} {returnfocusto} {initialfocus} on:focuslockupdate={onFocusLockUpdate}>
<slot></slot>
</FocusLock>
</div>
<style>
.modal-backdrop {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
height: 100dvh;
background-color: var(--modal-bg, rgba(0, 0, 0, 0.7));
z-index: var(--modal-z);
display: flex;
justify-content: center;
align-items: center
}
.modal-backdrop.opaque {
background-color: var(--modal-bg-opaque, #4c4c4c);
}
.modal-backdrop :global(.modal-container) {
max-width: 100vw;
max-height: 100vh;
max-height: 100dvh;
overflow: auto;
}
</style>