fix(drawer): fix drawer implementation with hook (#817)

* fix(drawer): fix drawer implementation with hook

* fix(drawer): fix drawer implementation

- Make drawer implementation as the same as dialog

* chore(drawer): documentation updates
This commit is contained in:
jeremywu 2020-12-07 00:13:05 +08:00 committed by GitHub
parent 2280dd5c5e
commit 9ad78debcf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 368 additions and 224 deletions

View File

@ -1,5 +1,6 @@
import { nextTick } from 'vue'
import { mount } from '@vue/test-utils'
import { rAF } from '@element-plus/test-utils/tick'
import Dialog from '../'
const AXIOM = 'Rem is the best girl'
@ -27,6 +28,8 @@ describe('Dialog.vue', () => {
},
})
await nextTick()
await rAF()
await nextTick()
expect(wrapper.find('.el-dialog__body').text()).toEqual(AXIOM)
})
@ -214,6 +217,8 @@ describe('Dialog.vue', () => {
})
expect(wrapper.vm.visible).toBe(true)
await nextTick()
await rAF()
await nextTick()
await wrapper.find('.el-dialog__headerbtn').trigger('click')
await wrapper.setProps({
// manually setting this prop because that Transition is not available in testing,
@ -221,7 +226,9 @@ describe('Dialog.vue', () => {
modelValue: false,
})
await nextTick()
expect(wrapper.html()).toBeFalsy()
await rAF()
await nextTick()
expect(wrapper.find('.el-dialog__body').exists()).toBe(false)
})
})
})

View File

@ -6,3 +6,4 @@ Dialog.install = (app: App): void => {
}
export default Dialog
export { default as useDialog } from './src/useDialog'

View File

@ -4,11 +4,11 @@ export interface UseDialogProps {
closeOnPressEscape: boolean
closeDelay: number
destroyOnClose: boolean
fullscreen: boolean
fullscreen?: boolean
lockScroll: boolean
modelValue: boolean
openDelay: number
top: string
width: string
top?: string
width?: string
zIndex?: number
}

View File

@ -1,66 +1,65 @@
<template>
<template v-if="destroyOnClose && !visible"></template>
<template v-else>
<teleport to="body" :disabled="!appendToBody">
<transition
name="dialog-fade"
@after-enter="afterEnter"
@after-leave="afterLeave"
<teleport to="body" :disabled="!appendToBody">
<transition
name="dialog-fade"
@after-enter="afterEnter"
@after-leave="afterLeave"
>
<el-overlay
v-show="visible"
:z-index="zIndex"
:mask="modal"
@click="onModalClick"
>
<el-overlay
v-if="visible"
:z-index="zIndex"
:mask="modal"
@click="onModalClick"
<div
ref="dialogRef"
v-trap-focus
:class="[
'el-dialog',
{
'is-fullscreen': fullscreen,
'el-dialog--center': center,
},
customClass,
]"
aria-modal="true"
role="dialog"
:aria-label="title || 'dialog'"
:style="style"
@click="$event.stopPropagation()"
>
<div
ref="dialogRef"
v-trap-focus
:class="[
'el-dialog',
{
'is-fullscreen': fullscreen,
'el-dialog--center': center,
},
customClass,
]"
aria-modal="true"
role="dialog"
:aria-label="title || 'dialog'"
:style="style"
@click="$event.stopPropagation()"
>
<div class="el-dialog__header">
<slot name="header">
<span class="el-dialog__title">
{{ title }}
</span>
</slot>
<button
v-if="showClose"
aria-label="close"
class="el-dialog__headerbtn"
type="button"
@click="handleClose"
>
<i class="el-dialog__close el-icon el-icon-close"></i>
</button>
</div>
<div class="el-dialog__header">
<slot name="header">
<span class="el-dialog__title">
{{ title }}
</span>
</slot>
<button
v-if="showClose"
aria-label="close"
class="el-dialog__headerbtn"
type="button"
@click="handleClose"
>
<i class="el-dialog__close el-icon el-icon-close"></i>
</button>
</div>
<template v-if="rendered">
<div class="el-dialog__body">
<slot></slot>
</div>
<div v-if="$slots.footer" class="el-dialog__footer">
<slot name="footer"></slot>
</div>
</template>
<div v-if="$slots.footer" class="el-dialog__footer">
<slot name="footer"></slot>
</div>
</el-overlay>
</transition>
</teleport>
</template>
</div>
</el-overlay>
</transition>
</teleport>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { defineComponent, ref } from 'vue'
import { TrapFocus } from '@element-plus/directives'
import { isValidWidthUnit } from '@element-plus/utils/validators'
@ -166,7 +165,11 @@ export default defineComponent({
UPDATE_MODEL_EVENT,
],
setup(props, ctx) {
return useDialog(props, ctx as SetupContext)
const dialogRef = ref<HTMLElement>(null)
return {
...useDialog(props, ctx as SetupContext, dialogRef),
dialogRef,
}
},
})
</script>

View File

@ -1,4 +1,4 @@
import { computed, ref, watch, nextTick, onMounted } from 'vue'
import { computed, ref, watch, nextTick, onMounted, CSSProperties } from 'vue'
import isServer from '@element-plus/utils/isServer'
import { UPDATE_MODEL_EVENT } from '@element-plus/utils/constants'
@ -6,8 +6,9 @@ import PopupManager from '@element-plus/utils/popup-manager'
import { clearTimer } from '@element-plus/utils/util'
import { useLockScreen, useRestoreActive, useModal } from '@element-plus/hooks'
import type { UseDialogProps } from './dialog'
import type { Ref } from 'vue'
import type { SetupContext } from '@vue/runtime-core'
import type { UseDialogProps } from './dialog'
export const CLOSE_EVENT = 'close'
export const OPEN_EVENT = 'open'
@ -15,17 +16,18 @@ export const CLOSED_EVENT = 'closed'
export const OPENED_EVENT = 'opened'
export { UPDATE_MODEL_EVENT }
export default function useDialog(props: UseDialogProps, ctx: SetupContext) {
export default function(props: UseDialogProps, ctx: SetupContext, targetRef: Ref<HTMLElement>) {
const visible = ref(false)
const closed = ref(false)
const dialogRef = ref(null)
const openTimer = ref<TimeoutHandle>(null)
const closeTimer = ref<TimeoutHandle>(null)
const rendered = ref(false) // when desctroyOnClose is true, we initialize it as false vise versa
const zIndex = ref(props.zIndex || PopupManager.nextZIndex())
const modalRef = ref<HTMLElement>(null)
const style = computed(() => {
const style = {} as CSSStyleDeclaration
const style = {} as CSSProperties
if (!props.fullscreen) {
style.marginTop = props.top
if (props.width) {
@ -37,11 +39,15 @@ export default function useDialog(props: UseDialogProps, ctx: SetupContext) {
function afterEnter() {
ctx.emit(OPENED_EVENT)
rendered.value = true // enables lazy rendering
}
function afterLeave() {
ctx.emit(CLOSED_EVENT)
ctx.emit(UPDATE_MODEL_EVENT, false)
if (props.destroyOnClose) {
rendered.value = false
}
}
function open() {
@ -124,10 +130,11 @@ export default function useDialog(props: UseDialogProps, ctx: SetupContext) {
closed.value = false
open()
ctx.emit(OPEN_EVENT)
zIndex.value = props.zIndex ? zIndex.value++ : PopupManager.nextZIndex()
// this.$el.addEventListener('scroll', this.updatePopper)
nextTick(() => {
if (dialogRef.value) {
dialogRef.value.scrollTop = 0
if (targetRef.value) {
targetRef.value.scrollTop = 0
}
})
} else {
@ -154,6 +161,7 @@ export default function useDialog(props: UseDialogProps, ctx: SetupContext) {
closed,
dialogRef,
style,
rendered,
modalRef,
visible,
zIndex,

View File

@ -1,7 +1,8 @@
import { nextTick } from 'vue'
import { mount } from '@vue/test-utils'
import { rAF } from '@element-plus/test-utils/tick'
import Drawer from '../src/index.vue'
import Button from '../../button/src/button.vue'
import { nextTick } from 'vue'
jest.useFakeTimers()
@ -29,11 +30,13 @@ describe('Drawer', () => {
visible: true,
}),
)
const wrapperEl = wrapper.find('.el-drawer__wrapper').element as HTMLDivElement
await nextTick()
await rAF()
await nextTick()
const wrapperEl = wrapper.find('.el-overlay').element as HTMLDivElement
const headerEl = wrapper.find('.el-drawer__header').element
await nextTick()
expect(document.querySelector('.v-modal')).not.toBeNull()
expect(wrapperEl.style.display).not.toEqual('none')
expect(headerEl.textContent).toEqual(title)
})
@ -53,6 +56,8 @@ describe('Drawer', () => {
}),
)
await nextTick()
await rAF()
await nextTick()
expect(wrapper.find('.el-drawer__body span').element.textContent).toEqual('this is a sentence')
const footerBtns = wrapper.findAll('.el-button')
@ -77,7 +82,9 @@ describe('Drawer', () => {
vm.visible = true
await nextTick()
expect(document.querySelector('.el-drawer__wrapper').parentNode).toEqual(document.body)
await rAF()
await nextTick()
expect(document.querySelector('.el-overlay').parentNode).toEqual(document.body)
})
test('should open and close drawer properly', async () => {
@ -94,8 +101,10 @@ describe('Drawer', () => {
)
const vm = wrapper.vm as any
await nextTick()
await rAF()
await nextTick()
const drawerEl = wrapper.find('.el-drawer__wrapper').element as HTMLDivElement
const drawerEl = wrapper.find('.el-overlay').element as HTMLDivElement
expect(drawerEl.style.display).toEqual('none')
vm.visible = true
@ -117,9 +126,13 @@ describe('Drawer', () => {
)
const vm = wrapper.vm as any
await nextTick()
await rAF()
await nextTick()
expect(wrapper.find('.el-drawer__body span').element.textContent).toEqual(content)
vm.$refs.drawer.closeDrawer()
vm.$refs.drawer.handleClose()
await nextTick()
await rAF()
await nextTick()
expect(wrapper.find('.el-drawer__body').exists()).toBe(false)
})
@ -136,9 +149,15 @@ describe('Drawer', () => {
visible: true,
}),
)
await nextTick()
await rAF()
await nextTick()
const vm = wrapper.vm as any
wrapper.findComponent(Drawer).find('.el-drawer__close-btn').trigger('click')
await wrapper.find('.el-drawer__close-btn').trigger('click')
await nextTick()
await rAF()
await nextTick()
expect(vm.visible).toEqual(false)
})
@ -164,7 +183,7 @@ describe('Drawer', () => {
}),
)
const vm = wrapper.vm as any
vm.$refs.drawer.closeDrawer()
vm.$refs.drawer.handleClose()
expect(beforeClose).toHaveBeenCalled()
})
@ -256,7 +275,7 @@ describe('Drawer', () => {
const closed = jest.fn()
const wrapper = _mount(
`
<el-drawer
<el-drawer
:title='title'
v-model='visible'
ref="drawer"

View File

@ -5,21 +5,21 @@
@after-enter="afterEnter"
@after-leave="afterLeave"
>
<div
v-show="modelValue"
ref="root"
class="el-drawer__wrapper"
tabindex="-1"
<el-overlay
v-show="visible"
:z-index="zIndex"
:mask="modal"
@click="onModalClick"
>
<div
class="el-drawer__container"
:class="modelValue && 'el-drawer__open'"
:class="{ 'el-drawer__open': visible }"
tabindex="-1"
role="document"
@click.self="handleWrapperClick"
>
<div
ref="drawer"
ref="drawerRef"
v-trap-focus
aria-modal="true"
aria-labelledby="el-drawer__title"
:aria-label="title"
@ -28,6 +28,7 @@
:style="isHorizontal ? 'width: ' + size : 'height: ' + size"
role="dialog"
tabindex="-1"
@click.stop
>
<header
v-if="withHeader"
@ -44,37 +45,46 @@
:aria-label="'close ' + (title || 'drawer')"
class="el-drawer__close-btn"
type="button"
@click="closeDrawer"
@click="handleClose"
>
<i class="el-drawer__close el-icon el-icon-close"></i>
</button>
</header>
<section v-if="state.rendered" class="el-drawer__body">
<slot></slot>
</section>
<template v-if="rendered">
<section class="el-drawer__body">
<slot></slot>
</section>
</template>
</div>
</div>
</div>
</el-overlay>
</transition>
</teleport>
</template>
<script lang='ts'>
<script lang="ts">
import {
defineComponent, ref, computed,
watch, nextTick,
onMounted,
defineComponent,
computed,
ref,
} from 'vue'
import usePopup from '@element-plus/utils/popup/usePopup'
import Utils from '@element-plus/utils/aria'
import { Overlay } from '@element-plus/overlay'
import { useDialog } from '@element-plus/dialog'
import { TrapFocus } from '@element-plus/directives'
import type { PropType } from 'vue'
import type { PropType, SetupContext } from 'vue'
type Hide = (cancel: boolean) => void
type DrawerDirection = 'ltr' | 'rtl' | 'ttb' | 'btt'
export default defineComponent({
name: 'ElDrawer',
components: {
[Overlay.name]: Overlay,
},
directives: {
TrapFocus,
},
props: {
modelValue: Boolean,
appendToBody: {
@ -105,7 +115,7 @@ export default defineComponent({
type: String,
default: '',
},
wrapperClosable: {
closeOnClickModal: {
type: Boolean,
default: true,
},
@ -143,10 +153,6 @@ export default defineComponent({
type: Boolean,
default: true,
},
closeOnClickModal: {
type: Boolean,
default: false,
},
destroyOnClose: {
type: Boolean,
default: false,
@ -156,106 +162,13 @@ export default defineComponent({
emits: ['open', 'opened', 'close', 'closed', 'update:modelValue'],
setup(props, ctx) {
const {
state,
doAfterClose,
updateClosingFlag,
restoreBodyStyle,
} = usePopup(props, doClose)
const drawer = ref<HTMLElement>(null)
const root = ref<HTMLElement>(null)
const prevActiveElement = ref<HTMLElement>(null)
const closed = ref(false)
const isHorizontal = computed(() => props.direction === 'rtl' || props.direction === 'ltr')
function afterEnter() {
ctx.emit('opened')
}
function doClose() {
updateClosingFlag(true)
props.lockScroll && setTimeout(restoreBodyStyle, 200)
state.opened = false
doAfterClose()
}
function afterLeave() {
ctx.emit('closed')
}
function hide(cancel = true) {
if (cancel !== false) {
ctx.emit('update:modelValue', false)
ctx.emit('close')
if (props.destroyOnClose === true) {
state.rendered = false
}
closed.value = true
}
}
function handleWrapperClick() {
if (props.wrapperClosable) {
closeDrawer()
}
}
function closeDrawer() {
if (typeof props.beforeClose === 'function') {
props.beforeClose(hide)
} else {
hide()
}
}
function handleClose() {
// This method here will be called by PopupManger, when the `closeOnPressEscape` was set to true
// pressing `ESC` will call this method, and also close the drawer.
// This method also calls `beforeClose` if there was one.
closeDrawer()
}
watch(
() => props.modelValue,
val => {
state.visible = val
if (val) {
closed.value = false
ctx.emit('open')
prevActiveElement.value = document.activeElement as HTMLElement
nextTick(() => {
Utils.focusFirstDescendant(drawer.value)
})
} else {
if (!closed.value) ctx.emit('close')
nextTick(() => {
prevActiveElement.value?.focus()
})
}
},
)
onMounted(() => {
if (props.modelValue) {
state.rendered = true
state.visible = true
}
})
const drawerRef = ref<HTMLElement>(null)
return {
state,
root,
drawer,
closed,
afterEnter,
afterLeave,
handleWrapperClick,
isHorizontal,
closeDrawer,
handleClose,
...useDialog(props, ctx as SetupContext, drawerRef),
drawerRef,
isHorizontal: computed(() => props.direction === 'rtl' || props.direction === 'ltr'),
}
},
})
</script>

View File

@ -1,5 +1,6 @@
@import "mixins/mixins";
@import "common/var";
@import "./overlay.scss";
@keyframes el-drawer-fade-in {
0% {

View File

@ -209,9 +209,45 @@ Dialog's content can be centered.
The content of Dialog is lazily rendered, which means the default slot is not rendered onto the DOM until it is firstly opened. Therefore, if you need to perform a DOM manipulation or access a component using `ref`, do it in the `open` event callback.
:::
:::tip
If the variable bound to `visible` is managed in Vuex store, the `.sync` can not work properly. In this case, please remove the `.sync` modifier, listen to `open` and `close` events of Dialog, and commit Vuex mutations to update the value of that variable in the event handlers.
:::
### Destroy on Close
When this is feature is enabled, the content under default slot will be destroyed with a `v-if` directive. Enable this when you have perf concerns.
:::demo Note that by enabling this feature, the content will not be rendered before `transition.beforeEnter` dispatched, there will only be `overlay` `header(if any)` `footer(if any)`.
```html
<el-button type="text" @click="centerDialogVisible = true">Click to open Dialog</el-button>
<el-dialog
title="Notice"
v-model="centerDialogVisible"
width="30%"
destroy-on-close
center>
<span>Notice: before dialog gets opened for the first time this node and the one bellow will not be rendered</span>
<div>
<strong>Extra content (Not rendered)</strong>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="centerDialogVisible = false">Cancel</el-button>
<el-button type="primary" @click="centerDialogVisible = false">Confirm</el-button>
</span>
</template>
</el-dialog>
<script>
export default {
data() {
return {
centerDialogVisible: false
};
}
};
</script>
```
### Attributes

View File

@ -2,6 +2,10 @@
Sometimes, `Dialog` does not always satisfy our requirements, let's say you have a massive form, or you need space to display something like `terms & conditions`, `Drawer` has almost identical API with `Dialog`, but it introduces different user experience.
:::tip
Since v-model is natively supported for all components, `visible.sync` has been deprecated, use `v-model="visibilityBinding"` to control the visibility of the current drawer.
:::
### Basic Usage
Callout a temporary drawer, from multiple direction
@ -272,8 +276,7 @@ Drawer provides an API called `destroyOnClose`, which is a flag variable that in
| show-close | Should show close button at the top right of Drawer | boolean | — | true |
| size | Drawer's size, if Drawer is horizontal mode, it effects the width property, otherwise it effects the height property, when size is `number` type, it describes the size by unit of pixels; when size is `string` type, it should be used with `x%` notation, other wise it will be interpreted to pixel unit | number / string | - | '30%' |
| title | Drawer's title, can also be set by named slot, detailed descriptions can be found in the slot form | string | — | — |
| model-value | Should Drawer be displayed, also support the `.sync` notation | boolean | — | false |
| wrapperClosable | Indicates whether user can close Drawer by clicking the shadowing layer. | boolean | - | true |
| model-value / v-model | Should Drawer be displayed | boolean | — | false |
| withHeader | Flag that controls the header section's existance, default to true, when withHeader set to false, both `title attribute` and `title slot` won't work | boolean | - | true |
### Drawer Slot

View File

@ -211,11 +211,44 @@ El contenido de Diálogo se puede centrar.
El contenido de Dialog se renderiza en modo lazy, lo que significa que la ranura por defecto no se renderiza en el DOM hasta que se abre por primera vez. Por lo tanto, si necesita realizar una manipulación DOM o acceder a un componente mediante ref, hágalo en el callback del evento `open`.
:::
:::tip
### Destroy on Close (Translation needed)
When this is feature is enabled, the content under default slot will be destroyed with a `v-if` directive. Enable this when you have perf concerns.
Si la variable ligada a `visible` se gestiona en el Vuex store, el `.sync` no puede funcionar correctamente. En este caso, elimine el modificador `.sync`, escuche los eventos de `open` y `close` Dialog, y confirme las mutaciones Vuex para actualizar el valor de esa variable en los manejadores de eventos.
:::demo Note that by enabling this feature, the content will not be rendered before `transition.beforeEnter` dispatched, there will only be `overlay` `header(if any)` `footer(if any)`.
:::
```html
<el-button type="text" @click="centerDialogVisible = true">Click to open Dialog</el-button>
<el-dialog
title="Notice"
v-model="centerDialogVisible"
width="30%"
destroy-on-close
center>
<span>Notice: before dialog gets opened for the first time this node and the one bellow will not be rendered</span>
<div>
<strong>Extra content (Not rendered)</strong>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="centerDialogVisible = false">Cancel</el-button>
<el-button type="primary" @click="centerDialogVisible = false">Confirm</el-button>
</span>
</template>
</el-dialog>
<script>
export default {
data() {
return {
centerDialogVisible: false
};
}
};
</script>
```
### Atributo

View File

@ -2,6 +2,11 @@
A veces, `Dialog` no siempre satisface nuestros requisitos, digamos que tiene un formulario masivo, o necesita espacio para mostrar algo como `terminos & condiciones`, `Drawer` tiene una API casi idéntica a `Dialog`, pero introduce una experiencia de usuario diferente.
:::tip
#### Translation needed
Since v-model is natively supported for all components, `visible.sync` has been deprecated, use `v-model="visibilityBinding"` to control the visibility of the current drawer.
:::
### Uso básico
Llamada de un drawer temporal, desde varias direcciones
@ -272,8 +277,7 @@ El Drawer proporciona una API llamada "destroyOnClose", que es una variable de b
| show-close | Se mostrará el botón de cerrar en la parte superior derecha del Drawer | boolean | — | true |
| size | Tamaño del Drawer. Si el Drawer está en modo horizontal, afecta a la propiedad width, de lo contrario afecta a la propiedad height, cuando el tamaño es tipo `number`, describe el tamaño por unidad de píxeles; cuando el tamaño es tipo `string`, se debe usar con notación `x%`, de lo contrario se interpretará como unidad de píxeles. | number / string | - | '30%' |
| title | El título del Drawer, también se puede establecer por slot con nombre, las descripciones detalladas se pueden encontrar en el formulario de slot. | string | — | — |
| model-value | Si se muestra el Drawer, también soporta la notación `.sync` | boolean | — | false |
| wrapperClosable | Indica si el usuario puede cerrar el Drawer haciendo clic en la capa de sombreado. | boolean | - | true |
| model-value / v-model | Si se muestra el Drawer | boolean | — | false |
| withHeader | Flag that controls the header section's existance, default to true, when withHeader set to false, both `title attribute` and `title slot` won't work | boolean | - | true |
### Drawer Slot's

View File

@ -88,7 +88,7 @@ Le contenu du modal peut être n'importe quoi, tableau ou formulaire compris.
<el-button @click="dialogFormVisible = false">Annuler</el-button>
<el-button type="primary" @click="dialogFormVisible = false">Confirmer</el-button>
</span>
</template>
</template>
</el-dialog>
<script>
@ -209,9 +209,44 @@ Le contenu du modal peut être centré.
Le contenu de Dialog bénéficie du lazy loading, ce qui signifie que le slot par défaut n'est pas généré par le DOM avant la première ouverture. Si vous avez besoin de manipuler le DOM ou d'accéder à un composant via `ref`, vous pouvez le faire avec la callback de l'évènement `open`.
:::
:::tip
Si la variable liée à `visible` est gérée dans Vuex, le modificateur `.sync` ne peut pas fonctionner. Dans ce cas retirez-le, écoutez les évènements `open` et `close`, et commitez les mutations Vuex pour mettre à jour la valeur de cette variable.
:::
### Destroy on Close (Translation needed)
When this is feature is enabled, the content under default slot will be destroyed with a `v-if` directive. Enable this when you have perf concerns.
:::demo Note that by enabling this feature, the content will not be rendered before `transition.beforeEnter` dispatched, there will only be `overlay` `header(if any)` `footer(if any)`.
```html
<el-button type="text" @click="centerDialogVisible = true">Click to open Dialog</el-button>
<el-dialog
title="Notice"
v-model="centerDialogVisible"
width="30%"
destroy-on-close
center>
<span>Notice: before dialog gets opened for the first time this node and the one bellow will not be rendered</span>
<div>
<strong>Extra content (Not rendered)</strong>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="centerDialogVisible = false">Cancel</el-button>
<el-button type="primary" @click="centerDialogVisible = false">Confirm</el-button>
</span>
</template>
</el-dialog>
<script>
export default {
data() {
return {
centerDialogVisible: false
};
}
};
</script>
```
### Attributs

View File

@ -2,7 +2,11 @@
Sometimes, `Dialog` does not always satisfy our requirements, let's say you have a massive form, or you need space to display something like `terms & conditions`, `Drawer` has almost identical API with `Dialog`, but it introduces different user experience.
### Basic Usage
:::tip
#### Translation needed
Since v-model is natively supported for all components, `visible.sync` has been deprecated, use `v-model="visibilityBinding"` to control the visibility of the current drawer.
:::### Basic Usage
Callout a temporary drawer, from multiple direction
@ -272,8 +276,7 @@ Drawer provides an API called `destroyOnClose`, which is a flag variable that in
| show-close | Should show close button at the top right of Drawer | boolean | — | true |
| size | Drawer's size, if Drawer is horizontal mode, it effects the width property, otherwise it effects the height property, when size is `number` type, it describes the size by unit of pixels; when size is `string` type, it should be used with `x%` notation, other wise it will be interpreted to pixel unit | number / string | - | '30%' |
| title | Drawer's title, can also be set by named slot, detailed descriptions can be found in the slot form | string | — | — |
| model-value | Should Drawer be displayed, also support the `.sync` notation | boolean | — | false |
| wrapperClosable | Indicates whether user can close Drawer by clicking the shadowing layer. | boolean | - | true |
| model-value / v-model | Should Drawer be displayed | boolean | — | false |
| withHeader | Flag that controls the header section's existance, default to true, when withHeader set to false, both `title attribute` and `title slot` won't work | boolean | - | true |
### Drawer Slot

View File

@ -204,9 +204,44 @@ dialogの内容は遅延的にレンダリングされます。つまり、デ
:::
:::tip
Vuexストアで `visible` にバインドされた変数を管理している場合、`.sync` が正しく動作しません。この場合は、`.sync` モディファイアを削除し、Dialog の `open`, `close` イベントをリッスンし、Vuex のミューテーションをコミットして、イベントハンドラでその変数の値を更新してください。
:::
### dialog内の要素を破棄する (translation needed)
When this is feature is enabled, the content under default slot will be destroyed with a `v-if` directive. Enable this when you have perf concerns.
:::demo Note that by enabling this feature, the content will not be rendered before `transition.beforeEnter` dispatched, there will only be `overlay` `header(if any)` `footer(if any)`.
```html
<el-button type="text" @click="centerDialogVisible = true">Click to open Dialog</el-button>
<el-dialog
title="Notice"
v-model="centerDialogVisible"
width="30%"
destroy-on-close
center>
<span>Notice: before dialog gets opened for the first time this node and the one bellow will not be rendered</span>
<div>
<strong>Extra content (Not rendered)</strong>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="centerDialogVisible = false">Cancel</el-button>
<el-button type="primary" @click="centerDialogVisible = false">Confirm</el-button>
</span>
</template>
</el-dialog>
<script>
export default {
data() {
return {
centerDialogVisible: false
};
}
};
</script>
```
### 属性

View File

@ -2,6 +2,11 @@
例えば、巨大なフォームを持っていたり、`terms & conditions` のようなものを表示するためのスペースが必要な場合、`Drawer` は `Dialog` とほぼ同じ API を持っていますが、ユーザーエクスペリエンスが異なります。
:::tip
#### Translation needed
Since v-model is natively supported for all components, `visible.sync` has been deprecated, use `v-model="visibilityBinding"` to control the visibility of the current drawer.
:::
### 基本的な使い方
一時的にDrawerを多方向から呼び出す
@ -272,8 +277,7 @@ Drawerは `destroyOnClose` というAPIを提供しています。これはフ
| show-close | Drawerの右上に閉じるボタンを表示するようにした | boolean | — | true |
| size | Drawerのサイズ, ドローワが水平モードの場合は幅プロパティ, そうでない場合は高さプロパティ, サイズが `number` 型の場合はピクセル単位でサイズを記述します; サイズが `string` 型の場合は `x%` 記法を用います, それ以外の場合はピクセル単位で解釈されます | number / string | - | '30%' |
| title | Drawerのタイトルは、スロットの名前を指定して設定することもできます。 | string | — | — |
| model-value | Drawerを表示する場合は、`.sync` 記法もサポートします。 | boolean | — | false |
| wrapperClosable | シャドウイングレイヤーをクリックしてDrwerを閉じることができるかどうかを示します。 | boolean | - | true |
| model-value / v-model | Drawerを表示する場合は、 | boolean | — | false |
| withHeader | デフォルトは true で、withHeader が false に設定されている場合は `title attribute``title slot` の両方が動作しません。 | boolean | - | true |
### Drawerスロット

View File

@ -185,8 +185,8 @@ Dialog 组件的内容可以是任意的,甚至可以是表格或表单,下
<el-button @click="centerDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="centerDialogVisible = false">确 定</el-button>
</span>
</template>
</template>
</el-dialog>
<script>
@ -205,8 +205,44 @@ Dialog 组件的内容可以是任意的,甚至可以是表格或表单,下
Dialog 的内容是懒渲染的,即在第一次被打开之前,传入的默认 slot 不会被渲染到 DOM 上。因此,如果需要执行 DOM 操作,或通过 `ref` 获取相应组件,请在 `open` 事件回调中进行。
:::
:::tip
如果 `visible` 属性绑定的变量位于 Vuex 的 store 内,那么 `.sync` 不会正常工作。此时需要去除 `.sync` 修饰符,同时监听 Dialog 的 `open``close` 事件,在事件回调中执行 Vuex 中对应的 mutation 更新 `visible` 属性绑定的变量的值。
### 关闭时销毁 DOM 内容
可在 Dialog 没有显示时,销毁 Dialog 里的内容以达到减少 DOM 节点的作用
:::demo 需要注意的是当这个属性被启用时Dialog 内并不会有任何的 DOM 节点存在,除了 `overlay` `header如果有` `footer如果有`
```html
<el-button type="text" @click="centerDialogVisible = true">点击打开 Dialog</el-button>
<el-dialog
title="提示"
v-model="centerDialogVisible"
width="30%"
destroy-on-close
center>
<span>需要注意在 Dialog 打开前是这条内容和下面的内容都是不会被渲染的</span>
<strong>额外的内容</strong>
<template #footer>
<span class="dialog-footer">
<el-button @click="centerDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="centerDialogVisible = false">确 定</el-button>
</span>
</template>
</el-dialog>
<script>
export default {
data() {
return {
centerDialogVisible: false
};
}
};
</script>
```
:::
### Attributes

View File

@ -2,6 +2,10 @@
有些时候, `Dialog` 组件并不满足我们的需求, 比如你的表单很长, 亦或是你需要临时展示一些文档, `Drawer` 拥有和 `Dialog` 几乎相同的 API, 在 UI 上带来不一样的体验.
:::tip
因为 Vue 提供了 `v-model` 的原生支持,所以以前的 `visible.sync` 已经不再适用,请使用 `v-model="visibleBinding"` 的表达式来绑定是否显示抽屉组件
:::
### 基本用法
呼出一个临时的侧边栏, 可以从多个方向呼出
@ -274,8 +278,7 @@ Drawer 提供一个 `destroyOnClose` API, 用来在关闭 Drawer 时销毁子组
| show-close | 是否显示关闭按钮 | boolean | — | true |
| size | Drawer 窗体的大小, 当使用 `number` 类型时, 以像素为单位, 当使用 `string` 类型时, 请传入 'x%', 否则便会以 `number` 类型解释 | number / string | - | '30%' |
| title | Drawer 的标题,也可通过具名 slot (见下表)传入 | string | — | — |
| model-value | 是否显示 Drawer支持 .sync 修饰符 | boolean | — | false |
| wrapperClosable | 点击遮罩层是否可以关闭 Drawer | boolean | - | true |
| model-value / v-model | 是否显示 Drawer | boolean | — | false |
| withHeader | 控制是否显示 header 栏, 默认为 true, 当此项为 false 时, title attribute 和 title slot 均不生效 | boolean | - | true |
### Drawer Slot