mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-02-11 13:10:26 +08:00
refactor(modal-provider): make it work
This commit is contained in:
parent
e7f5534394
commit
2bf5229543
@ -20,11 +20,11 @@ const appVue = `<template>
|
||||
<n-loading-bar-provider>
|
||||
<n-message-provider>
|
||||
<n-notification-provider>
|
||||
<n-modal-provider>
|
||||
<n-dialog-provider>
|
||||
<demo />
|
||||
</n-dialog-provider>
|
||||
</n-modal-provider>
|
||||
<n-modal-provider>
|
||||
<n-dialog-provider>
|
||||
<demo />
|
||||
</n-dialog-provider>
|
||||
</n-modal-provider>
|
||||
</n-notification-provider>
|
||||
</n-message-provider>
|
||||
</n-loading-bar-provider>
|
||||
|
@ -58,7 +58,7 @@ export interface DialogApiInjection {
|
||||
|
||||
export interface DialogProviderInjection {
|
||||
clickedRef: Ref<boolean>
|
||||
clickPositionRef: Ref<{ x: number, y: number } | null>
|
||||
clickedPositionRef: Ref<{ x: number, y: number } | null>
|
||||
}
|
||||
|
||||
export type DialogReactiveListInjection = Ref<DialogReactive[]>
|
||||
@ -129,7 +129,7 @@ export const NDialogProvider = defineComponent({
|
||||
provide(dialogApiInjectionKey, api)
|
||||
provide(dialogProviderInjectionKey, {
|
||||
clickedRef: useClicked(64),
|
||||
clickPositionRef: useClickPosition()
|
||||
clickedPositionRef: useClickPosition()
|
||||
})
|
||||
provide(dialogReactiveListInjectionKey, dialogListRef)
|
||||
return {
|
||||
|
@ -6,7 +6,7 @@ Basic usage of modal. You can put anything in modal, a card for example.
|
||||
|
||||
<template>
|
||||
<n-button @click="showModal = true">
|
||||
Start Me up
|
||||
Start me up
|
||||
</n-button>
|
||||
<n-modal v-model:show="showModal">
|
||||
<n-card
|
||||
|
@ -6,7 +6,7 @@ Modal can be controlled.
|
||||
|
||||
<template>
|
||||
<n-button @click="handleClick">
|
||||
Start Me up
|
||||
Start me up
|
||||
</n-button>
|
||||
<n-modal :show="showModal">
|
||||
<n-card
|
||||
|
@ -6,7 +6,7 @@ Use fixed position to set the position of the modal.
|
||||
|
||||
<template>
|
||||
<n-button @click="showModal = true">
|
||||
Start Me up
|
||||
Start me up
|
||||
</n-button>
|
||||
<n-modal v-model:show="showModal">
|
||||
<n-card
|
||||
|
@ -2,10 +2,24 @@
|
||||
|
||||
It just pops and shows you something.
|
||||
|
||||
<n-alert title="Prerequisite" type="warning" :bordered="false">
|
||||
If you want to create modal using <n-text code>useModal</n-text>, you need to wrap the component where you call related methods inside <n-text code>n-modal-provider</n-text> and use <n-text code>useModal</n-text> to get the API.
|
||||
</n-alert>
|
||||
|
||||
For example:
|
||||
|
||||
```html
|
||||
<!-- App.vue -->
|
||||
<n-modal-provider>
|
||||
<content />
|
||||
</n-modal-provider>
|
||||
```
|
||||
|
||||
## Demos
|
||||
|
||||
```demo
|
||||
basic.vue
|
||||
reactive.vue
|
||||
controlled.vue
|
||||
mask-closable.vue
|
||||
custom-position.vue
|
||||
@ -13,11 +27,29 @@ preset-card.vue
|
||||
preset-confirm.vue
|
||||
preset-confirm-slot.vue
|
||||
transform-origin.vue
|
||||
reactive.vue
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### ModalProvider Props
|
||||
|
||||
Provided since NEXT_VERSION.
|
||||
|
||||
| Name | Type | Default | Description | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| to | `string \| HTMLElement` | `body` | Container node of the modal content. | NEXT_VERSION |
|
||||
|
||||
### useModal API
|
||||
|
||||
Provided since NEXT_VERSION.
|
||||
|
||||
| Name | Type | Description | Version |
|
||||
| --- | --- | --- | --- |
|
||||
| create | `(options: ModalOptions) => ModalReactive` | Create a modal. | NEXT_VERSION |
|
||||
| destroyAll | `() => void` | Destroy all modals. | NEXT_VERSION |
|
||||
|
||||
`ModalOptions` and `ModalReactive`'s properties are the same as `ModalProps` (properties should use camelCase, for example `auto-focus` property should use `autoFocus` as option property).
|
||||
|
||||
### Modal Props
|
||||
|
||||
| Name | Type | Default | Description | Version |
|
||||
@ -39,6 +71,14 @@ reactive.vue
|
||||
| on-mask-click | `() => void` | `undefined` | Callback on mask is clicked. | |
|
||||
| on-update:show | `(value: boolean) => void` | `undefined` | Callback when modal's display status is changed. | |
|
||||
|
||||
### ModalProvider Props
|
||||
|
||||
Provided since NEXT_VERSION.
|
||||
|
||||
| Name | Type | Default | Description | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| to | `string \| HTMLElement` | `body` | Container node of the modal content. | NEXT_VERSION |
|
||||
|
||||
### Modal with Preset Card Props
|
||||
|
||||
See [Card props](card#Card-Props)
|
||||
|
@ -6,7 +6,7 @@ Use `mask-closable=false` to make modal not emit the event which may close the m
|
||||
|
||||
<template>
|
||||
<n-button @click="showModal = true">
|
||||
Start Me up
|
||||
Start me up
|
||||
</n-button>
|
||||
<n-modal
|
||||
v-model:show="showModal"
|
||||
|
@ -6,7 +6,7 @@ Modal has some presets, which means you can use props & slots of the preset afte
|
||||
|
||||
<template>
|
||||
<n-button @click="showModal = true">
|
||||
Start Me up
|
||||
Start me up
|
||||
</n-button>
|
||||
<n-modal
|
||||
v-model:show="showModal"
|
||||
|
@ -6,7 +6,7 @@ Slots are also related to preset.
|
||||
|
||||
<template>
|
||||
<n-button @click="showModal = true">
|
||||
Start Me up
|
||||
Start me up
|
||||
</n-button>
|
||||
<n-modal v-model:show="showModal" preset="dialog" title="Dialog">
|
||||
<template #header>
|
||||
|
@ -6,7 +6,7 @@ An example of preset `dialog`.
|
||||
|
||||
<template>
|
||||
<n-button @click="showModal = true">
|
||||
Start Me up
|
||||
Start me up
|
||||
</n-button>
|
||||
<n-modal
|
||||
v-model:show="showModal"
|
||||
|
@ -1,11 +1,14 @@
|
||||
<markdown>
|
||||
# Use Modal
|
||||
# Imperative API
|
||||
|
||||
Provided since NEXT_VERSION.
|
||||
|
||||
You can use `useModal.create` to create a modal. (Please make sure this API is called inside `n-modal-provider`.)
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-button @click="handleClick">
|
||||
Come
|
||||
Start me up
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
|
@ -2,10 +2,24 @@
|
||||
|
||||
它会弹出来,然后给你看点东西。
|
||||
|
||||
<n-alert title="使用前提" type="warning" :bordered="false">
|
||||
如果你想通过 <n-text code>useModal</n-text> 使用对话框,你需要把调用其方法的组件放在 <n-text code>n-modal-provider</n-text> 内部并且使用 <n-text code>useModal</n-text> 去获取 API。
|
||||
</n-alert>
|
||||
|
||||
例如:
|
||||
|
||||
```html
|
||||
<!-- App.vue -->
|
||||
<n-modal-provider>
|
||||
<content />
|
||||
</n-modal-provider>
|
||||
```
|
||||
|
||||
## 演示
|
||||
|
||||
```demo
|
||||
basic.vue
|
||||
reactive.vue
|
||||
controlled.vue
|
||||
mask-closable.vue
|
||||
custom-position.vue
|
||||
@ -13,7 +27,6 @@ preset-card.vue
|
||||
preset-confirm.vue
|
||||
preset-confirm-slot.vue
|
||||
transform-origin.vue
|
||||
reactive.vue
|
||||
nested-debug.vue
|
||||
a11y-debug.vue
|
||||
raw-debug.vue
|
||||
@ -33,6 +46,25 @@ mask-click-debug.vue
|
||||
|
||||
## API
|
||||
|
||||
### ModalProvider Props
|
||||
|
||||
自 NEXT_VERSION 开始提供。
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 | 版本 |
|
||||
| ---- | ----------------------- | ------ | -------------- | ------------ |
|
||||
| to | `string \| HTMLElement` | `body` | 模态的挂载位置 | NEXT_VERSION |
|
||||
|
||||
### useModal API
|
||||
|
||||
自 NEXT_VERSION 开始提供。
|
||||
|
||||
| 名称 | 类型 | 说明 | 版本 |
|
||||
| --- | --- | --- | --- |
|
||||
| create | `(options: ModalOptions) => ModalReactive` | 创建模态框 | NEXT_VERSION |
|
||||
| destroyAll | `() => void` | 销毁所有弹出的模态框 | NEXT_VERSION |
|
||||
|
||||
`ModalOptions` 的属性和 `ModalReactive` 属性同 `ModalProps`(属性应使用 camelCase,例如 `auto-focus` 对应 `autoFocus`)。
|
||||
|
||||
### Modal Props
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 | 版本 |
|
||||
|
@ -1,6 +1,9 @@
|
||||
<markdown>
|
||||
# Use Modal
|
||||
# 命令式 API
|
||||
|
||||
自 NEXT_VERSION 开始提供。
|
||||
|
||||
你可以使用 `useModal.create` 来打开一个模态框。(请确保使用此 API 的组件被 `n-modal-provider` 包含。)
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
|
@ -30,7 +30,7 @@ import { modalLight } from '../styles'
|
||||
import type { ModalTheme } from '../styles'
|
||||
import { presetProps, presetPropsKeys } from './presetProps'
|
||||
import NModalBodyWrapper from './BodyWrapper'
|
||||
import { modalInjectionKey } from './interface'
|
||||
import { modalInjectionKey, modalProviderInjectionKey } from './interface'
|
||||
import style from './styles/index.cssr'
|
||||
|
||||
export const modalProps = {
|
||||
@ -86,6 +86,7 @@ export const modalProps = {
|
||||
onMaskClick: Function as PropType<(e: MouseEvent) => void>,
|
||||
// private
|
||||
internalDialog: Boolean,
|
||||
internalModal: Boolean,
|
||||
internalAppear: {
|
||||
type: Boolean as PropType<boolean | undefined>,
|
||||
default: undefined
|
||||
@ -144,6 +145,9 @@ export default defineComponent({
|
||||
const NDialogProvider = props.internalDialog
|
||||
? inject(dialogProviderInjectionKey, null)
|
||||
: null
|
||||
const NModalProvider = props.internalModal
|
||||
? inject(modalProviderInjectionKey, null)
|
||||
: null
|
||||
|
||||
const isComposingRef = useIsComposing()
|
||||
|
||||
@ -220,10 +224,11 @@ export default defineComponent({
|
||||
}
|
||||
provide(modalInjectionKey, {
|
||||
getMousePosition: () => {
|
||||
if (NDialogProvider) {
|
||||
const { clickedRef, clickPositionRef } = NDialogProvider
|
||||
if (clickedRef.value && clickPositionRef.value) {
|
||||
return clickPositionRef.value
|
||||
const mergedProvider = NDialogProvider || NModalProvider
|
||||
if (mergedProvider) {
|
||||
const { clickedRef, clickedPositionRef } = mergedProvider
|
||||
if (clickedRef.value && clickedPositionRef.value) {
|
||||
return clickedPositionRef.value
|
||||
}
|
||||
}
|
||||
if (clickedRef.value) {
|
||||
|
@ -1,44 +1,16 @@
|
||||
// use absolute path to make sure no circular ref of style
|
||||
// this -> modal-index -> modal-style
|
||||
import { h, defineComponent, type PropType, ref, type CSSProperties } from 'vue'
|
||||
import { h, defineComponent, type PropType, ref } from 'vue'
|
||||
import NModal, { modalProps } from './Modal'
|
||||
|
||||
export const exposedModalEnvProps = {
|
||||
...modalProps,
|
||||
onAfterEnter: Function as PropType<() => void>,
|
||||
onAfterLeave: Function as PropType<() => void>,
|
||||
transformOrigin: String as PropType<'center' | 'mouse'>,
|
||||
blockScroll: { type: Boolean, default: true },
|
||||
closeOnEsc: { type: Boolean, default: true },
|
||||
onEsc: Function as PropType<() => void>,
|
||||
autoFocus: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
internalStyle: [String, Object] as PropType<string | CSSProperties>,
|
||||
maskClosable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
onPositiveClick: Function as PropType<
|
||||
(e: MouseEvent) => Promise<unknown> | unknown
|
||||
>,
|
||||
onNegativeClick: Function as PropType<
|
||||
(e: MouseEvent) => Promise<unknown> | unknown
|
||||
>,
|
||||
onClose: Function as PropType<() => Promise<unknown> | unknown>,
|
||||
onMaskClick: Function as PropType<(e: MouseEvent) => void>
|
||||
} as const
|
||||
|
||||
export const NModalEnvironment = defineComponent({
|
||||
name: 'ModalEnvironment',
|
||||
props: {
|
||||
...exposedModalEnvProps,
|
||||
...modalProps,
|
||||
internalKey: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
to: [String, Object] as PropType<string | HTMLElement>,
|
||||
// private
|
||||
onInternalAfterLeave: {
|
||||
type: Function as PropType<(key: string) => void>,
|
||||
@ -52,10 +24,10 @@ export const NModalEnvironment = defineComponent({
|
||||
if (onInternalAfterLeave) onInternalAfterLeave(internalKey)
|
||||
if (onAfterLeave) onAfterLeave()
|
||||
}
|
||||
function handlePositiveClick (e: MouseEvent): void {
|
||||
function handlePositiveClick (): void {
|
||||
const { onPositiveClick } = props
|
||||
if (onPositiveClick) {
|
||||
void Promise.resolve(onPositiveClick(e)).then((result) => {
|
||||
void Promise.resolve(onPositiveClick()).then((result) => {
|
||||
if (result === false) return
|
||||
hide()
|
||||
})
|
||||
@ -63,10 +35,10 @@ export const NModalEnvironment = defineComponent({
|
||||
hide()
|
||||
}
|
||||
}
|
||||
function handleNegativeClick (e: MouseEvent): void {
|
||||
function handleNegativeClick (): void {
|
||||
const { onNegativeClick } = props
|
||||
if (onNegativeClick) {
|
||||
void Promise.resolve(onNegativeClick(e)).then((result) => {
|
||||
void Promise.resolve(onNegativeClick()).then((result) => {
|
||||
if (result === false) return
|
||||
hide()
|
||||
})
|
||||
@ -122,8 +94,6 @@ export const NModalEnvironment = defineComponent({
|
||||
handleAfterLeave,
|
||||
handleMaskClick,
|
||||
handleEsc,
|
||||
to,
|
||||
maskClosable,
|
||||
show
|
||||
} = this
|
||||
return (
|
||||
@ -133,16 +103,9 @@ export const NModalEnvironment = defineComponent({
|
||||
onUpdateShow={handleUpdateShow}
|
||||
onMaskClick={handleMaskClick}
|
||||
onEsc={handleEsc}
|
||||
to={to}
|
||||
maskClosable={maskClosable}
|
||||
onAfterEnter={this.onAfterEnter}
|
||||
onAfterLeave={handleAfterLeave}
|
||||
closeOnEsc={this.closeOnEsc}
|
||||
blockScroll={this.blockScroll}
|
||||
autoFocus={this.autoFocus}
|
||||
transformOrigin={this.transformOrigin}
|
||||
internalAppear
|
||||
internalDialog
|
||||
internalModal
|
||||
></NModal>
|
||||
)
|
||||
}
|
||||
|
@ -43,13 +43,13 @@ type TypeSafeModalReactive = ModalReactive & {
|
||||
}
|
||||
|
||||
export interface ModalApiInjection {
|
||||
destroy: () => void
|
||||
destroyAll: () => void
|
||||
create: (options: ModalOptions) => ModalReactive
|
||||
}
|
||||
|
||||
export interface ModalProviderInjection {
|
||||
clickedRef: Ref<boolean>
|
||||
clickPositionRef: Ref<{ x: number, y: number } | null>
|
||||
clickedPositionRef: Ref<{ x: number, y: number } | null>
|
||||
}
|
||||
|
||||
export type ModalReactiveListInjection = Ref<ModalReactive[]>
|
||||
@ -61,7 +61,6 @@ interface ModalInst {
|
||||
export type ModalProviderInst = ModalApiInjection
|
||||
|
||||
export const modalProviderProps = {
|
||||
injectionKey: String,
|
||||
to: [String, Object] as PropType<string | HTMLElement>
|
||||
}
|
||||
|
||||
@ -73,6 +72,9 @@ export const NModalProvider = defineComponent({
|
||||
name: 'ModalProvider',
|
||||
props: modalProviderProps,
|
||||
setup () {
|
||||
const clickedRef = useClicked(64)
|
||||
const clickedPositionRef = useClickPosition()
|
||||
|
||||
const modalListRef = ref<TypeSafeModalReactive[]>([])
|
||||
const modalInstRefs: Record<string, ModalInst> = {}
|
||||
function create (options: ModalOptions = {}): ModalReactive {
|
||||
@ -96,7 +98,7 @@ export const NModalProvider = defineComponent({
|
||||
)
|
||||
}
|
||||
|
||||
function destroy (): void {
|
||||
function destroyAll (): void {
|
||||
Object.values(modalInstRefs).forEach((modalInstRef) => {
|
||||
modalInstRef.hide()
|
||||
})
|
||||
@ -104,15 +106,19 @@ export const NModalProvider = defineComponent({
|
||||
|
||||
const api = {
|
||||
create,
|
||||
destroy
|
||||
destroyAll
|
||||
}
|
||||
|
||||
provide(modalApiInjectionKey, api)
|
||||
provide(modalProviderInjectionKey, {
|
||||
clickedRef: useClicked(64),
|
||||
clickPositionRef: useClickPosition()
|
||||
clickedPositionRef: useClickPosition()
|
||||
})
|
||||
provide(modalReactiveListInjectionKey, modalListRef)
|
||||
provide(modalProviderInjectionKey, {
|
||||
clickedRef,
|
||||
clickedPositionRef
|
||||
})
|
||||
return {
|
||||
...api,
|
||||
modalList: modalListRef,
|
||||
@ -127,7 +133,7 @@ export const NModalProvider = defineComponent({
|
||||
NModalEnvironment,
|
||||
omit(modal, ['destroy', 'style'], {
|
||||
internalStyle: modal.style,
|
||||
to: this.to,
|
||||
to: modal.to ?? this.to,
|
||||
ref: ((inst: ModalInst | null) => {
|
||||
if (inst === null) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
|
@ -10,6 +10,14 @@ HTMLElement | ComponentPublicInstance | null
|
||||
export const modalBodyInjectionKey =
|
||||
createInjectionKey<ModalBodyInjection>('n-modal-body')
|
||||
|
||||
export interface ModalProviderInjection {
|
||||
clickedRef: Ref<boolean>
|
||||
clickedPositionRef: Ref<{ x: number, y: number } | null>
|
||||
}
|
||||
|
||||
export const modalProviderInjectionKey =
|
||||
createInjectionKey<ModalProviderInjection>('n-modal-provider')
|
||||
|
||||
export interface ModalInjection {
|
||||
getMousePosition: () => {
|
||||
x: number
|
||||
|
Loading…
Reference in New Issue
Block a user