feat: Some improvements for modal component
Added click outside, persistent and close on Esc
This commit is contained in:
@@ -2,10 +2,23 @@
|
|||||||
import ModalExample from './modal/examples/ModalExample.vue';
|
import ModalExample from './modal/examples/ModalExample.vue';
|
||||||
import ModalSizeExample from './modal/examples/ModalSizeExample.vue';
|
import ModalSizeExample from './modal/examples/ModalSizeExample.vue';
|
||||||
import ModalEscapableExample from './modal/examples/ModalEscapableExample.vue';
|
import ModalEscapableExample from './modal/examples/ModalEscapableExample.vue';
|
||||||
|
import ModalPersistentExample from './modal/examples/ModalPersistentExample.vue';
|
||||||
</script>
|
</script>
|
||||||
# Vue Modal - Flowbite
|
# Vue Modal - Flowbite
|
||||||
|
|
||||||
## Demo
|
#### Use the modal component to show interactive dialogs and notifications to your website users available in multiple sizes, colors, and styles
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
Original reference: [https://flowbite.com/docs/components/modal/](https://flowbite.com/docs/components/modal/)
|
||||||
|
:::
|
||||||
|
|
||||||
|
The modal component can be used as an interactive dialog on top of the main content area of the website to show notifications and gather information using form elements from your website users.
|
||||||
|
|
||||||
|
Get started with multiple sizes, colors, and styles built with the utility classes from Tailwind CSS and the components from Flowbite.
|
||||||
|
|
||||||
|
## Default modal
|
||||||
|
|
||||||
<ModalExample />
|
<ModalExample />
|
||||||
|
|
||||||
@@ -101,3 +114,35 @@ import { Modal } from 'flowbite-vue'
|
|||||||
<Modal :escapable="false" />
|
<Modal :escapable="false" />
|
||||||
</template>
|
</template>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Persistent
|
||||||
|
|
||||||
|
Clicking outside of the element or pressing esc key will not send `close` event.
|
||||||
|
|
||||||
|
Demo:
|
||||||
|
<ModalPersistentExample />
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script setup>
|
||||||
|
import { Modal } from 'flowbite-vue'
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<Modal persistent />
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### Props:
|
||||||
|
|
||||||
|
| Name | Values | Default |
|
||||||
|
|------------|-----------------------------------------------------------|---------|
|
||||||
|
| size | `md`,`lg`, `xl`, `2xl`, `3xl`, `4xl`, `5xl`, `6xl`, `7xl` | 2xl |
|
||||||
|
| escapable | `true`, `false` | `true` |
|
||||||
|
| persistent | `true`, `false` | `true` |
|
||||||
|
|
||||||
|
### Events:
|
||||||
|
| Name | Type |
|
||||||
|
|---------------------|----------------------------------------------------------------------------------|
|
||||||
|
| `close` | Clicked on the close button, pressed `Esc`, or clicked outside the modal content |
|
||||||
|
| `click:outside` | Clicked outside the modal content |
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<button @click="showModal" type="button" class="mt-5 text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
|
<button @click="showModal" type="button" class="mt-5 text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
|
||||||
{{ triggerText }}
|
{{ triggerText }}
|
||||||
</button>
|
</button>
|
||||||
<Modal :escapable="escapable" :size="size" v-if="isShowModal" @close="closeModal">
|
<Modal :escapable="escapable" :size="size" v-if="isShowModal" @close="closeModal" :persistent="persistent">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="flex items-center text-lg">
|
<div class="flex items-center text-lg">
|
||||||
Terms of Service
|
Terms of Service
|
||||||
@@ -32,42 +32,22 @@
|
|||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Modal } from '../../../../src/index'
|
import { Modal } from '../../../../src/index'
|
||||||
import type { PropType } from 'vue'
|
import type { ModalSize } from '../../../../src/components/Modal/types'
|
||||||
import type { ModalSize, ModalPosition } from '../../../../src/components/Modal/types'
|
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
defineProps({
|
interface ModalProps {
|
||||||
children: {
|
size?: ModalSize,
|
||||||
type: Array,
|
escapable?: boolean,
|
||||||
default() {
|
persistent?: boolean,
|
||||||
return []
|
triggerText?: string
|
||||||
},
|
}
|
||||||
},
|
withDefaults(defineProps<ModalProps>(), {
|
||||||
popup: {
|
size: '2xl',
|
||||||
type: Boolean,
|
escapable: true,
|
||||||
default: false,
|
persistent: false,
|
||||||
},
|
triggerText: 'Demo Modal',
|
||||||
position: {
|
|
||||||
type: String as PropType<ModalPosition>,
|
|
||||||
default: 'center',
|
|
||||||
},
|
|
||||||
show: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
type: String as PropType<ModalSize>,
|
|
||||||
default: '2xl',
|
|
||||||
},
|
|
||||||
escapable: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
triggerText: {
|
|
||||||
type: String,
|
|
||||||
default: 'Demo Modal',
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const isShowModal = ref(false)
|
const isShowModal = ref(false)
|
||||||
function closeModal() {
|
function closeModal() {
|
||||||
isShowModal.value = false
|
isShowModal.value = false
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<template>
|
||||||
|
<div class="vp-raw">
|
||||||
|
<ModalExample trigger-text="Persistent" :persistent="true" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import ModalExample from './ModalExample.vue'
|
||||||
|
</script>
|
||||||
@@ -2,12 +2,13 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="bg-gray-900 bg-opacity-50 dark:bg-opacity-80 fixed inset-0 z-40" />
|
<div class="bg-gray-900 bg-opacity-50 dark:bg-opacity-80 fixed inset-0 z-40" />
|
||||||
<div
|
<div
|
||||||
tabindex="-1"
|
tabindex="0"
|
||||||
|
ref="modalRef"
|
||||||
class="overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 w-full md:inset-0 h-modal md:h-full justify-center items-center flex"
|
class="overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 w-full md:inset-0 h-modal md:h-full justify-center items-center flex"
|
||||||
v-on="escapable ? { click: closeModal } : {}"
|
@keyup.esc="closeWithEsc"
|
||||||
|
@click.self="clickOutside"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@click.stop
|
|
||||||
class="relative p-4 w-full h-full md:h-auto"
|
class="relative p-4 w-full h-full md:h-auto"
|
||||||
:class="`${modalSizeClasses[size]}`"
|
:class="`${modalSizeClasses[size]}`"
|
||||||
>
|
>
|
||||||
@@ -16,7 +17,7 @@
|
|||||||
<!-- Modal header -->
|
<!-- Modal header -->
|
||||||
<div class="p-4 rounded-t flex justify-between items-center" :class="$slots.header ? 'border-b border-gray-200 dark:border-gray-600' : ''">
|
<div class="p-4 rounded-t flex justify-between items-center" :class="$slots.header ? 'border-b border-gray-200 dark:border-gray-600' : ''">
|
||||||
<slot name="header" />
|
<slot name="header" />
|
||||||
<button v-if="escapable" @click="closeModal" aria-label="close" type="button" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white">
|
<button v-if="!persistent" @click="closeModal" aria-label="close" type="button" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white">
|
||||||
<slot name="close-icon">
|
<slot name="close-icon">
|
||||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
|
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
|
||||||
</slot>
|
</slot>
|
||||||
@@ -36,34 +37,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { PropType } from 'vue'
|
import type { ModalSize } from './types'
|
||||||
import type { ModalSize, ModalPosition } from './types'
|
import { onMounted, ref, type Ref } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
interface ModalProps {
|
||||||
children: {
|
size?: ModalSize,
|
||||||
type: Array,
|
escapable?: boolean,
|
||||||
default() {
|
persistent?: boolean
|
||||||
return []
|
}
|
||||||
},
|
const props = withDefaults(defineProps<ModalProps>(), {
|
||||||
},
|
size: '2xl',
|
||||||
popup: {
|
escapable: true,
|
||||||
type: Boolean,
|
persistent: false,
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
position: {
|
|
||||||
type: String as PropType<ModalPosition>,
|
|
||||||
default: 'center',
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
type: String as PropType<ModalSize>,
|
|
||||||
default: '2xl',
|
|
||||||
},
|
|
||||||
escapable: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
const emit = defineEmits(['close'])
|
const emit = defineEmits(['close', 'click:outside'])
|
||||||
const modalSizeClasses = {
|
const modalSizeClasses = {
|
||||||
xs: 'max-w-xs',
|
xs: 'max-w-xs',
|
||||||
sm: 'max-w-sm',
|
sm: 'max-w-sm',
|
||||||
@@ -81,12 +68,20 @@ const modalSizeClasses = {
|
|||||||
function closeModal() {
|
function closeModal() {
|
||||||
emit('close')
|
emit('close')
|
||||||
}
|
}
|
||||||
|
function clickOutside() {
|
||||||
addEventListener("keyup", function(e) {
|
if(!props.persistent) {
|
||||||
if (props.escapable) {
|
emit('click:outside')
|
||||||
if (e.key === "Escape") {
|
closeModal()
|
||||||
closeModal();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeWithEsc() {
|
||||||
|
if(props.escapable && !props.persistent) closeModal()
|
||||||
|
}
|
||||||
|
const modalRef: Ref<HTMLElement | null> = ref(null)
|
||||||
|
onMounted(() => {
|
||||||
|
if(modalRef.value) {
|
||||||
|
modalRef.value.focus()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user