123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- <template>
- <transition name="fade" appear @leave="onTransitionLeaveStart">
- <div
- ref="modal"
- class="inset-0 transition z-10 z-50 fixed hide-scrollbar overflow-y-auto"
- >
- <div
- class="flex min-h-screen text-center items-end justify-center sm:block"
- >
- <transition name="fade" appear>
- <div
- class="bg-primaryDark opacity-90 inset-0 transition fixed"
- @touchstart="!dialog ? close() : null"
- @touchend="!dialog ? close() : null"
- @mouseup="!dialog ? close() : null"
- @mousedown="!dialog ? close() : null"
- ></div>
- </transition>
- <span
- v-if="placement === 'center'"
- class="hidden sm:h-screen sm:inline-block sm:align-middle"
- aria-hidden="true"
- >​</span
- >
- <transition
- appear
- enter-active-class="transition"
- enter-class="translate-y-4 scale-95"
- enter-to-class="translate-y-0 scale-100"
- leave-active-class="transition"
- leave-class="translate-y-0 scale-100"
- leave-to-class="translate-y-4 scale-95"
- >
- <div
- class="
- bg-primary
- shadow-lg
- text-left
- w-full
- transform
- transition-all
- inline-block
- align-bottom
- overflow-hidden
- sm:max-w-md sm:align-middle
- md:rounded-lg
- "
- :class="[
- { 'mt-24 md:mb-8': placement === 'top' },
- { 'p-4': !fullWidth },
- ]"
- >
- <div
- v-if="title"
- class="flex mb-4 pl-2 items-center justify-between"
- >
- <h3 class="heading">{{ title }}</h3>
- <span class="flex">
- <slot name="actions"></slot>
- <ButtonSecondary
- v-if="dimissible"
- class="rounded"
- svg="x"
- @click.native="close"
- />
- </span>
- </div>
- <div
- class="flex flex-col max-h-md overflow-y-auto hide-scrollbar"
- :class="{ 'py-2': !fullWidth }"
- >
- <slot name="body"></slot>
- </div>
- <div
- v-if="hasFooterSlot"
- class="flex flex-1 mt-4 p-2 items-center justify-between"
- >
- <slot name="footer"></slot>
- </div>
- </div>
- </transition>
- </div>
- </div>
- </transition>
- </template>
- <script lang="ts">
- import { defineComponent } from "@nuxtjs/composition-api"
- import { useKeybindingDisabler } from "~/helpers/keybindings"
- const PORTAL_DOM_ID = "hoppscotch-modal-portal"
- // Why ?
- const stack = (() => {
- const stack: number[] = []
- return {
- push: stack.push.bind(stack),
- pop: stack.pop.bind(stack),
- peek: () => (stack.length === 0 ? undefined : stack[stack.length - 1]),
- }
- })()
- export default defineComponent({
- props: {
- dialog: {
- type: Boolean,
- default: false,
- },
- title: {
- type: String,
- default: "",
- },
- dimissible: {
- type: Boolean,
- default: true,
- },
- placement: {
- type: String,
- default: "top",
- },
- fullWidth: {
- type: Boolean,
- default: false,
- },
- },
- setup() {
- const { disableKeybindings, enableKeybindings } = useKeybindingDisabler()
- return {
- disableKeybindings,
- enableKeybindings,
- }
- },
- data() {
- return {
- stackId: Math.random(),
- // when doesn't fire on unmount, we should manually remove the modal from DOM
- // (for example, when the parent component of this modal gets destroyed)
- shouldCleanupDomOnUnmount: true,
- }
- },
- computed: {
- hasFooterSlot(): boolean {
- return !!this.$slots.footer
- },
- },
- mounted() {
- const $portal = this.$getPortal()
- $portal.appendChild(this.$refs.modal as any)
- stack.push(this.stackId)
- document.addEventListener("keydown", this.onKeyDown)
- this.disableKeybindings()
- },
- beforeDestroy() {
- const $modal = this.$refs.modal
- if (this.shouldCleanupDomOnUnmount && $modal) {
- this.$getPortal().removeChild($modal as any)
- }
- stack.pop()
- document.removeEventListener("keydown", this.onKeyDown)
- },
- methods: {
- close() {
- this.$emit("close")
- this.enableKeybindings()
- },
- onKeyDown(e: KeyboardEvent) {
- if (e.key === "Escape" && this.stackId === stack.peek()) {
- e.preventDefault()
- this.close()
- }
- },
- onTransitionLeaveStart() {
- this.close()
- this.shouldCleanupDomOnUnmount = false
- },
- $getPortal() {
- let $el = document.querySelector("#" + PORTAL_DOM_ID)
- if ($el) {
- return $el
- }
- $el = document.createElement("DIV")
- $el.id = PORTAL_DOM_ID
- document.body.appendChild($el)
- return $el
- },
- },
- })
- </script>
|