mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-14 20:19:34 +01:00
feat(Modal): open programmatically (#1319)
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
<span v-html="title" />
|
||||
</template>
|
||||
</UNotifications>
|
||||
<UModals />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
17
docs/components/content/examples/ModalExampleComponent.vue
Normal file
17
docs/components/content/examples/ModalExampleComponent.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<script lang="ts" setup>
|
||||
defineProps({
|
||||
count: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UModal>
|
||||
<UCard>
|
||||
<p>This modal was opened programmatically !</p>
|
||||
<p>Count: {{ count }}</p>
|
||||
</UCard>
|
||||
</UModal>
|
||||
</template>
|
||||
17
docs/components/content/examples/ModalExampleComposable.vue
Normal file
17
docs/components/content/examples/ModalExampleComposable.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { ModalExampleComponent } from '#components'
|
||||
|
||||
const modal = useModal()
|
||||
const count = ref(0)
|
||||
|
||||
function openModal () {
|
||||
count.value += 1
|
||||
modal.open(ModalExampleComponent, {
|
||||
count: count.value
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UButton label="Reveal modal" @click="openModal" />
|
||||
</template>
|
||||
@@ -59,6 +59,26 @@ Set the `fullscreen` prop to `true` to enable it.
|
||||
|
||||
:component-example{component="modal-example-fullscreen"}
|
||||
|
||||
### Control programmatically
|
||||
|
||||
First of all, add the `Modals` component to your app, preferably inside `app.vue`.
|
||||
|
||||
```vue [app.vue]
|
||||
<template>
|
||||
<div>
|
||||
<UContainer>
|
||||
<NuxtPage />
|
||||
</UContainer>
|
||||
|
||||
<UModals />
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
Then, you can use the `useModal` composable to control your modals within your app.
|
||||
|
||||
:component-example{component="modal-example-composable"}
|
||||
|
||||
## Props
|
||||
|
||||
:component-props
|
||||
|
||||
@@ -90,6 +90,10 @@ export default defineNuxtModule<ModuleOptions>({
|
||||
src: resolve(runtimeDir, 'plugins', 'colors')
|
||||
})
|
||||
|
||||
addPlugin({
|
||||
src: resolve(runtimeDir, 'plugins', 'modals')
|
||||
})
|
||||
|
||||
// Components
|
||||
|
||||
addComponentsDir({
|
||||
|
||||
12
src/runtime/components/overlays/Modals.vue
Normal file
12
src/runtime/components/overlays/Modals.vue
Normal file
@@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<component :is="modalState.component" v-bind="modalState.props" v-model="isOpen" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { inject } from 'vue'
|
||||
import { useModal, modalInjectionKey } from '../../composables/useModal'
|
||||
|
||||
const modalState = inject(modalInjectionKey)
|
||||
|
||||
const { isOpen } = useModal()
|
||||
</script>
|
||||
51
src/runtime/composables/useModal.ts
Normal file
51
src/runtime/composables/useModal.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { ref, inject } from 'vue'
|
||||
import type { ShallowRef, Component, InjectionKey } from 'vue'
|
||||
import { createSharedComposable } from '@vueuse/core'
|
||||
import type { ComponentProps } from '../types/component'
|
||||
import type { Modal, ModalState } from '../types/modal'
|
||||
|
||||
export const modalInjectionKey: InjectionKey<ShallowRef<ModalState>> = Symbol('nuxt-ui.modal')
|
||||
|
||||
function _useModal () {
|
||||
const modalState = inject(modalInjectionKey)
|
||||
|
||||
const isOpen = ref(false)
|
||||
|
||||
function open<T extends Component> (component: T, props?: Modal & ComponentProps<T>) {
|
||||
modalState.value = {
|
||||
component,
|
||||
props: props ?? {}
|
||||
}
|
||||
isOpen.value = true
|
||||
}
|
||||
|
||||
function close () {
|
||||
isOpen.value = false
|
||||
modalState.value = {
|
||||
component: 'div',
|
||||
props: {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows updating the modal props
|
||||
*/
|
||||
function patch <T extends Component = {}> (props: Partial<Modal & ComponentProps<T>>) {
|
||||
modalState.value = {
|
||||
...modalState.value,
|
||||
props: {
|
||||
...modalState.value.props,
|
||||
...props
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isOpen,
|
||||
open,
|
||||
close,
|
||||
patch
|
||||
}
|
||||
}
|
||||
|
||||
export const useModal = createSharedComposable(_useModal)
|
||||
13
src/runtime/plugins/modals.ts
Normal file
13
src/runtime/plugins/modals.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { defineNuxtPlugin } from '#imports'
|
||||
import { shallowRef } from 'vue'
|
||||
import { modalInjectionKey } from '../composables/useModal'
|
||||
import type { ModalState } from '../types/modal'
|
||||
|
||||
export default defineNuxtPlugin((nuxtApp) => {
|
||||
const modalState = shallowRef<ModalState>({
|
||||
component: 'div',
|
||||
props: {}
|
||||
})
|
||||
|
||||
nuxtApp.vueApp.provide(modalInjectionKey, modalState)
|
||||
})
|
||||
14
src/runtime/types/component.d.ts
vendored
Normal file
14
src/runtime/types/component.d.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
export type ComponentProps<T> =
|
||||
T extends new () => { $props: infer P } ? NonNullable<P> :
|
||||
T extends (props: infer P, ...args: any) => any ? P :
|
||||
{}
|
||||
|
||||
export type ComponentSlots<T> =
|
||||
T extends new () => { $slots: infer S } ? NonNullable<S> :
|
||||
T extends (props: any, ctx: { slots: infer S; attrs: any; emit: any }, ...args: any) => any ? NonNullable<S> :
|
||||
{}
|
||||
|
||||
export type ComponentEmit<T> =
|
||||
T extends new () => { $emit: infer E } ? NonNullable<E> :
|
||||
T extends (props: any, ctx: { slots: any; attrs: any; emit: infer E }, ...args: any) => any ? NonNullable<E> :
|
||||
{}
|
||||
1
src/runtime/types/index.d.ts
vendored
1
src/runtime/types/index.d.ts
vendored
@@ -16,6 +16,7 @@ export * from './input'
|
||||
export * from './kbd'
|
||||
export * from './link'
|
||||
export * from './meter'
|
||||
export * from './modal'
|
||||
export * from './notification'
|
||||
export * from './popper'
|
||||
export * from './progress'
|
||||
|
||||
18
src/runtime/types/modal.d.ts
vendored
Normal file
18
src/runtime/types/modal.d.ts
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { Component } from 'vue'
|
||||
|
||||
export interface Modal {
|
||||
appear?: boolean
|
||||
overlay?: boolean
|
||||
transition?: boolean
|
||||
preventClose?: boolean
|
||||
fullscreen?: boolean
|
||||
class?: string | Object | string[]
|
||||
ui?: any
|
||||
onClose?: () => void
|
||||
onClosePrevented?: () => void
|
||||
}
|
||||
|
||||
export interface ModalState {
|
||||
component: Component | string
|
||||
props: Modal
|
||||
}
|
||||
Reference in New Issue
Block a user