Drawer: Add drawer component (#16577)

This commit is contained in:
jeremywu 2019-07-24 16:57:59 +08:00 committed by iamkun
parent 2c7911e580
commit 9617e9345a
16 changed files with 1875 additions and 2 deletions

View File

@ -78,5 +78,6 @@
"infinite-scroll": "./packages/infinite-scroll/index.js",
"page-header": "./packages/page-header/index.js",
"cascader-panel": "./packages/cascader-panel/index.js",
"avatar": "./packages/avatar/index.js"
"avatar": "./packages/avatar/index.js",
"drawer": "./packages/drawer/index.js"
}

View File

@ -0,0 +1,21 @@
.demo-drawer {
&__content {
display: flex;
flex-direction: column;
height: 100%;
form {
flex: 1;
}
}
&__footer {
display: flex;
button {
flex: 1;
}
}
}
.el-drawer__body {
padding: 20px;
}

View File

@ -43,4 +43,5 @@
@import "./image.scss";
@import "./infinite-scroll.scss";
@import "./avatar.scss";
@import "./drawer.scss";

View File

@ -0,0 +1,262 @@
## Drawer
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
Callout a temporary drawer, from multiple direction
:::demo You must set `visible` for `Drawer` like `Dialog` does to control the visibility of `Drawer` itself, it's `boolean` type. `Drawer` has to parts: `title` & `body`, the `title` is a named slot, you can also set the title through attribute named `title`, default to an empty string, the `body` part is the main area of `Drawer`, which contains user defined content. When opening, `Drawer` expand itself from the **right corner to left** which size is **30%** of the browser window by default. You can change that default behavior by setting `direction` and `size` attribute. This show case also demonstrated how to use the `before-close` API, check the Attribute section for more detail
```html
<el-radio-group v-model="direction">
<el-radio label="ltr">left to right</el-radio>
<el-radio label="rtl">right to left</el-radio>
<el-radio label="ttb">top to bottom</el-radio>
<el-radio label="btt">bottom to top</el-radio>
</el-radio-group>
<el-button @click="drawer = true" type="primary" style="margin-left: 16px;">
open
</el-button>
<el-drawer
title="I am the title"
:visible.sync="drawer"
:direction="direction"
:before-close="handleClose">
<span>Hi, there!</span>
</el-drawer>
<script>
export default {
data() {
return {
drawer: false,
direction: 'rtl',
};
},
methods: {
handleClose(done) {
this.$confirm('Are you sure you want to close this?')
.then(_ => {
done();
})
.catch(_ => {});
}
}
};
</script>
```
:::
### Customization Content
Like `Dialog`, `Drawer` can do many diverse interaction as you wanted.
:::demo
```html
<el-button type="text" @click="table = true">Open Drawer with nested table</el-button>
<el-button type="text" @click="dialog = true">Open Drawer with nested form</el-button>
<el-drawer
title="I have a nested table inside!"
:visible.sync="table"
direction="rtl"
size="50%">
<el-table :data="gridData">
<el-table-column property="date" label="Date" width="150"></el-table-column>
<el-table-column property="name" label="Name" width="200"></el-table-column>
<el-table-column property="address" label="Address"></el-table-column>
</el-table>
</el-drawer>
<el-drawer
title="I have a nested form inside!"
:before-close="handleClose"
:visible.sync="dialog"
direction="ltr"
custom-class="demo-drawer"
ref="drawer"
>
<div class="demo-drawer__content">
<el-form :model="form">
<el-form-item label="Name" :label-width="formLabelWidth">
<el-input v-model="form.name" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="Area" :label-width="formLabelWidth">
<el-select v-model="form.region" placeholder="Please select activity area">
<el-option label="Area1" value="shanghai"></el-option>
<el-option label="Area2" value="beijing"></el-option>
</el-select>
</el-form-item>
</el-form>
<div class="demo-drawer__footer">
<el-button @click="dialog = false">Cancel</el-button>
<el-button type="primary" @click="$refs.drawer.closeDrawer()" :loading="loading">{{ loading ? 'Submitting ...' : 'Submit' }}</el-button>
</div>
</div>
</el-drawer>
<script>
export default {
data() {
return {
table: false,
dialog: false,
loading: false,
gridData: [{
date: '2016-05-02',
name: 'Peter Parker',
address: 'Queens, New York City'
}, {
date: '2016-05-04',
name: 'Peter Parker',
address: 'Queens, New York City'
}, {
date: '2016-05-01',
name: 'Peter Parker',
address: 'Queens, New York City'
}, {
date: '2016-05-03',
name: 'Peter Parker',
address: 'Queens, New York City'
}],
form: {
name: '',
region: '',
date1: '',
date2: '',
delivery: false,
type: [],
resource: '',
desc: ''
},
formLabelWidth: '80px'
};
},
methods: {
handleClose(done) {
this.$confirm('Do you want to submit?')
.then(_ => {
this.loading = true;
setTimeout(() => {
this.loading = false;
done();
}, 2000);
})
.catch(_ => {});
}
}
}
</script>
```
:::
### Nested Drawer
You can also have multiple layer of `Drawer` just like `Dialog`.
:::demo If you need multiple Drawer in different layer, you must set the `append-to-body` attribute to **true**
```html
<el-button @click="drawer = true" type="primary" style="margin-left: 16px;">
open
</el-button>
<el-drawer
title="I'm outer Drawer"
:visible.sync="drawer"
size="50%">
<div>
<el-button @click="innerDrawer = true">Click me!</el-button>
<el-drawer
title="I'm inner Drawer"
:append-to-body="true"
:before-close="handleClose"
:visible.sync="innerDrawer">
<p>_(:зゝ∠)_</p>
</el-drawer>
</div>
</el-drawer>
<script>
export default {
data() {
return {
drawer: false,
innerDrawer: false,
};
},
methods: {
handleClose(done) {
this.$confirm('You still have unsaved data, proceed?')
.then(_ => {
done();
})
.catch(_ => {});
}
}
};
</script>
```
:::
:::tip
The content inside Drawer should be lazy rendered, which means that the content inside Drawer will not impact the initial render performance, therefore any DOM operation should be performed through `ref` or after `open` event emitted.
:::
:::tip
Drawer provides an API called `destroyOnClose`, which is a flag variable that indicates should destroy the children content inside Drawer after Drawer was closed. You can use this API when you need your `mounted` life cycle to be called every time the Drawer opens.
:::
:::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.
:::
### Drawer Attributes
| Parameter| Description | Type | Acceptable Values | Defaults |
|---------- |-------------- |---------- |-------------------------------- |-------- |
| append-to-body | Controls should Drawer be inserted to DocumentBody Element, nested Drawer must assign this param to **true**| boolean | — | false |
| before-close | If set, closing procedure will be halted | function(done), done is function type that accepts a boolean as parameter, calling done with true or without parameter will abort the close procedure | — | — |
| close-on-press-escape | Indicates whether Drawer can be closed by pressing ESC | boolean | — | true |
| custom-class | Extra class names for Drawer | string | — | — |
| destroy-on-close | Indicates whether children should be destroyed after Drawer closed | boolean | - | false |
| modal | Should show shadowing layer | boolean | — | true |
| modal-append-to-body | Indicates should shadowing layer be insert into DocumentBody element | boolean | — | true |
| direction | Drawer's opening direction | Direction | rtl / ltr / ttb / tbb | rtl |
| 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 | — | — |
| visible | 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 |
### Drawer Slot
| Name | Description |
|------|--------|
| — | Drawer's Content |
| title | Drawer Title Section |
### Drawer Methods
| Name | Description |
| ---- | --- |
| closeDrawer | In order to close Drawer, this method will call `before-close`. |
### Drawer Events
| Event Name | Description | Parameter |
|---------- |-------- |---------- |
| open | Triggered before Drawer opening animation begins | — |
| opened | Triggered after Drawer opening animation ended | — |
| close | Triggered before Drawer closing animation begins | — |
| closed | Triggered after Drawer closing animation ended | — |

262
examples/docs/es/drawer.md Normal file
View File

@ -0,0 +1,262 @@
## Drawer
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
Callout a temporary drawer, from multiple direction
:::demo You must set `visible` for `Drawer` like `Dialog` does to control the visibility of `Drawer` itself, it's `boolean` type. `Drawer` has to parts: `title` & `body`, the `title` is a named slot, you can also set the title through attribute named `title`, default to an empty string, the `body` part is the main area of `Drawer`, which contains user defined content. When opening, `Drawer` expand itself from the **right corner to left** which size is **30%** of the browser window by default. You can change that default behavior by setting `direction` and `size` attribute. This show case also demonstrated how to use the `before-close` API, check the Attribute section for more detail
```html
<el-radio-group v-model="direction">
<el-radio label="ltr">left to right</el-radio>
<el-radio label="rtl">right to left</el-radio>
<el-radio label="ttb">top to bottom</el-radio>
<el-radio label="btt">bottom to top</el-radio>
</el-radio-group>
<el-button @click="drawer = true" type="primary" style="margin-left: 16px;">
open
</el-button>
<el-drawer
title="I am the title"
:visible.sync="drawer"
:direction="direction"
:before-close="handleClose">
<span>Hi, there!</span>
</el-drawer>
<script>
export default {
data() {
return {
drawer: false,
direction: 'rtl',
};
},
methods: {
handleClose(done) {
this.$confirm('Are you sure you want to close this?')
.then(_ => {
done();
})
.catch(_ => {});
}
}
};
</script>
```
:::
### Customization Content
Like `Dialog`, `Drawer` can do many diverse interaction as you wanted.
:::demo
```html
<el-button type="text" @click="table = true">Open Drawer with nested table</el-button>
<el-button type="text" @click="dialog = true">Open Drawer with nested form</el-button>
<el-drawer
title="I have a nested table inside!"
:visible.sync="table"
direction="rtl"
size="50%">
<el-table :data="gridData">
<el-table-column property="date" label="Date" width="150"></el-table-column>
<el-table-column property="name" label="Name" width="200"></el-table-column>
<el-table-column property="address" label="Address"></el-table-column>
</el-table>
</el-drawer>
<el-drawer
title="I have a nested form inside!"
:before-close="handleClose"
:visible.sync="dialog"
direction="ltr"
custom-class="demo-drawer"
ref="drawer"
>
<div class="demo-drawer__content">
<el-form :model="form">
<el-form-item label="Name" :label-width="formLabelWidth">
<el-input v-model="form.name" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="Area" :label-width="formLabelWidth">
<el-select v-model="form.region" placeholder="Please select activity area">
<el-option label="Area1" value="shanghai"></el-option>
<el-option label="Area2" value="beijing"></el-option>
</el-select>
</el-form-item>
</el-form>
<div class="demo-drawer__footer">
<el-button @click="dialog = false">Cancel</el-button>
<el-button type="primary" @click="$refs.drawer.closeDrawer()" :loading="loading">{{ loading ? 'Submitting ...' : 'Submit' }}</el-button>
</div>
</div>
</el-drawer>
<script>
export default {
data() {
return {
table: false,
dialog: false,
loading: false,
gridData: [{
date: '2016-05-02',
name: 'Peter Parker',
address: 'Queens, New York City'
}, {
date: '2016-05-04',
name: 'Peter Parker',
address: 'Queens, New York City'
}, {
date: '2016-05-01',
name: 'Peter Parker',
address: 'Queens, New York City'
}, {
date: '2016-05-03',
name: 'Peter Parker',
address: 'Queens, New York City'
}],
form: {
name: '',
region: '',
date1: '',
date2: '',
delivery: false,
type: [],
resource: '',
desc: ''
},
formLabelWidth: '80px'
};
},
methods: {
handleClose(done) {
this.$confirm('Do you want to submit?')
.then(_ => {
this.loading = true;
setTimeout(() => {
this.loading = false;
done();
}, 2000);
})
.catch(_ => {});
}
}
}
</script>
```
:::
### Nested Drawer
You can also have multiple layer of `Drawer` just like `Dialog`.
:::demo If you need multiple Drawer in different layer, you must set the `append-to-body` attribute to **true**
```html
<el-button @click="drawer = true" type="primary" style="margin-left: 16px;">
open
</el-button>
<el-drawer
title="I'm outer Drawer"
:visible.sync="drawer"
size="50%">
<div>
<el-button @click="innerDrawer = true">Click me!</el-button>
<el-drawer
title="I'm inner Drawer"
:append-to-body="true"
:before-close="handleClose"
:visible.sync="innerDrawer">
<p>_(:зゝ∠)_</p>
</el-drawer>
</div>
</el-drawer>
<script>
export default {
data() {
return {
drawer: false,
innerDrawer: false,
};
},
methods: {
handleClose(done) {
this.$confirm('You still have unsaved data, proceed?')
.then(_ => {
done();
})
.catch(_ => {});
}
}
};
</script>
```
:::
:::tip
The content inside Drawer should be lazy rendered, which means that the content inside Drawer will not impact the initial render performance, therefore any DOM operation should be performed through `ref` or after `open` event emitted.
:::
:::tip
Drawer provides an API called `destroyOnClose`, which is a flag variable that indicates should destroy the children content inside Drawer after Drawer was closed. You can use this API when you need your `mounted` life cycle to be called every time the Drawer opens.
:::
:::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.
:::
### Drawer Attributes
| Parameter| Description | Type | Acceptable Values | Defaults |
|---------- |-------------- |---------- |-------------------------------- |-------- |
| append-to-body | Controls should Drawer be inserted to DocumentBody Element, nested Drawer must assign this param to **true**| boolean | — | false |
| before-close | If set, closing procedure will be halted | function(done), done is function type that accepts a boolean as parameter, calling done with true or without parameter will abort the close procedure | — | — |
| close-on-press-escape | Indicates whether Drawer can be closed by pressing ESC | boolean | — | true |
| custom-class | Extra class names for Drawer | string | — | — |
| destroy-on-close | Indicates whether children should be destroyed after Drawer closed | boolean | - | false |
| modal | Should show shadowing layer | boolean | — | true |
| modal-append-to-body | Indicates should shadowing layer be insert into DocumentBody element | boolean | — | true |
| direction | Drawer's opening direction | Direction | rtl / ltr / ttb / tbb | rtl |
| 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 | — | — |
| visible | 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 |
### Drawer Slot
| Name | Description |
|------|--------|
| — | Drawer's Content |
| title | Drawer Title Section |
### Drawer Methods
| Name | Description |
| ---- | --- |
| closeDrawer | In order to close Drawer, this method will call `before-close`. |
### Drawer Events
| Event Name | Description | Parameter |
|---------- |-------- |---------- |
| open | Triggered before Drawer opening animation begins | — |
| opened | Triggered after Drawer opening animation ended | — |
| close | Triggered before Drawer closing animation begins | — |
| closed | Triggered after Drawer closing animation ended | — |

View File

@ -0,0 +1,262 @@
## Drawer
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
Callout a temporary drawer, from multiple direction
:::demo You must set `visible` for `Drawer` like `Dialog` does to control the visibility of `Drawer` itself, it's `boolean` type. `Drawer` has to parts: `title` & `body`, the `title` is a named slot, you can also set the title through attribute named `title`, default to an empty string, the `body` part is the main area of `Drawer`, which contains user defined content. When opening, `Drawer` expand itself from the **right corner to left** which size is **30%** of the browser window by default. You can change that default behavior by setting `direction` and `size` attribute. This show case also demonstrated how to use the `before-close` API, check the Attribute section for more detail
```html
<el-radio-group v-model="direction">
<el-radio label="ltr">left to right</el-radio>
<el-radio label="rtl">right to left</el-radio>
<el-radio label="ttb">top to bottom</el-radio>
<el-radio label="btt">bottom to top</el-radio>
</el-radio-group>
<el-button @click="drawer = true" type="primary" style="margin-left: 16px;">
open
</el-button>
<el-drawer
title="I am the title"
:visible.sync="drawer"
:direction="direction"
:before-close="handleClose">
<span>Hi, there!</span>
</el-drawer>
<script>
export default {
data() {
return {
drawer: false,
direction: 'rtl',
};
},
methods: {
handleClose(done) {
this.$confirm('Are you sure you want to close this?')
.then(_ => {
done();
})
.catch(_ => {});
}
}
};
</script>
```
:::
### Customization Content
Like `Dialog`, `Drawer` can do many diverse interaction as you wanted.
:::demo
```html
<el-button type="text" @click="table = true">Open Drawer with nested table</el-button>
<el-button type="text" @click="dialog = true">Open Drawer with nested form</el-button>
<el-drawer
title="I have a nested table inside!"
:visible.sync="table"
direction="rtl"
size="50%">
<el-table :data="gridData">
<el-table-column property="date" label="Date" width="150"></el-table-column>
<el-table-column property="name" label="Name" width="200"></el-table-column>
<el-table-column property="address" label="Address"></el-table-column>
</el-table>
</el-drawer>
<el-drawer
title="I have a nested form inside!"
:before-close="handleClose"
:visible.sync="dialog"
direction="ltr"
custom-class="demo-drawer"
ref="drawer"
>
<div class="demo-drawer__content">
<el-form :model="form">
<el-form-item label="Name" :label-width="formLabelWidth">
<el-input v-model="form.name" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="Area" :label-width="formLabelWidth">
<el-select v-model="form.region" placeholder="Please select activity area">
<el-option label="Area1" value="shanghai"></el-option>
<el-option label="Area2" value="beijing"></el-option>
</el-select>
</el-form-item>
</el-form>
<div class="demo-drawer__footer">
<el-button @click="dialog = false">Cancel</el-button>
<el-button type="primary" @click="$refs.drawer.closeDrawer()" :loading="loading">{{ loading ? 'Submitting ...' : 'Submit' }}</el-button>
</div>
</div>
</el-drawer>
<script>
export default {
data() {
return {
table: false,
dialog: false,
loading: false,
gridData: [{
date: '2016-05-02',
name: 'Peter Parker',
address: 'Queens, New York City'
}, {
date: '2016-05-04',
name: 'Peter Parker',
address: 'Queens, New York City'
}, {
date: '2016-05-01',
name: 'Peter Parker',
address: 'Queens, New York City'
}, {
date: '2016-05-03',
name: 'Peter Parker',
address: 'Queens, New York City'
}],
form: {
name: '',
region: '',
date1: '',
date2: '',
delivery: false,
type: [],
resource: '',
desc: ''
},
formLabelWidth: '80px'
};
},
methods: {
handleClose(done) {
this.$confirm('Do you want to submit?')
.then(_ => {
this.loading = true;
setTimeout(() => {
this.loading = false;
done();
}, 2000);
})
.catch(_ => {});
}
}
}
</script>
```
:::
### Nested Drawer
You can also have multiple layer of `Drawer` just like `Dialog`.
:::demo If you need multiple Drawer in different layer, you must set the `append-to-body` attribute to **true**
```html
<el-button @click="drawer = true" type="primary" style="margin-left: 16px;">
open
</el-button>
<el-drawer
title="I'm outer Drawer"
:visible.sync="drawer"
size="50%">
<div>
<el-button @click="innerDrawer = true">Click me!</el-button>
<el-drawer
title="I'm inner Drawer"
:append-to-body="true"
:before-close="handleClose"
:visible.sync="innerDrawer">
<p>_(:зゝ∠)_</p>
</el-drawer>
</div>
</el-drawer>
<script>
export default {
data() {
return {
drawer: false,
innerDrawer: false,
};
},
methods: {
handleClose(done) {
this.$confirm('You still have unsaved data, proceed?')
.then(_ => {
done();
})
.catch(_ => {});
}
}
};
</script>
```
:::
:::tip
The content inside Drawer should be lazy rendered, which means that the content inside Drawer will not impact the initial render performance, therefore any DOM operation should be performed through `ref` or after `open` event emitted.
:::
:::tip
Drawer provides an API called `destroyOnClose`, which is a flag variable that indicates should destroy the children content inside Drawer after Drawer was closed. You can use this API when you need your `mounted` life cycle to be called every time the Drawer opens.
:::
:::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.
:::
### Drawer Attributes
| Parameter| Description | Type | Acceptable Values | Defaults |
|---------- |-------------- |---------- |-------------------------------- |-------- |
| append-to-body | Controls should Drawer be inserted to DocumentBody Element, nested Drawer must assign this param to **true**| boolean | — | false |
| before-close | If set, closing procedure will be halted | function(done), done is function type that accepts a boolean as parameter, calling done with true or without parameter will abort the close procedure | — | — |
| close-on-press-escape | Indicates whether Drawer can be closed by pressing ESC | boolean | — | true |
| custom-class | Extra class names for Drawer | string | — | — |
| destroy-on-close | Indicates whether children should be destroyed after Drawer closed | boolean | - | false |
| modal | Should show shadowing layer | boolean | — | true |
| modal-append-to-body | Indicates should shadowing layer be insert into DocumentBody element | boolean | — | true |
| direction | Drawer's opening direction | Direction | rtl / ltr / ttb / tbb | rtl |
| 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 | — | — |
| visible | 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 |
### Drawer Slot
| Name | Description |
|------|--------|
| — | Drawer's Content |
| title | Drawer Title Section |
### Drawer Methods
| Name | Description |
| ---- | --- |
| closeDrawer | In order to close Drawer, this method will call `before-close`. |
### Drawer Events
| Event Name | Description | Parameter |
|---------- |-------- |---------- |
| open | Triggered before Drawer opening animation begins | — |
| opened | Triggered after Drawer opening animation ended | — |
| close | Triggered before Drawer closing animation begins | — |
| closed | Triggered after Drawer closing animation ended | — |

View File

@ -0,0 +1,263 @@
## Drawer 抽屉
有些时候, `Dialog` 组件并不满足我们的需求, 比如你的表单很长, 亦或是你需要临时展示一些文档, `Drawer` 拥有和 `Dialog` 几乎相同的 API, 在 UI 上带来不一样的体验.
### 基本用法
呼出一个临时的侧边栏, 可以从多个方向呼出
:::demo 需要设置 `visible` 属性,它的**类型**是 `boolean`,当为 **true** 时显示 Drawer。Drawer 分为两个部分:`title` 和 `body``title` 需要具名为 **title**`slot`, 也可以通过 `title` 属性来定义,默认值为空。需要注意的是, Drawer 默认是从右往左打开, 当然可以设置对应的 `direction`, 详细请参考 `direction` 用法 最后,本例还展示了 `before-close` 的用法
```html
<el-radio-group v-model="direction">
<el-radio label="ltr">从左往右开</el-radio>
<el-radio label="rtl">从右往左开</el-radio>
<el-radio label="ttb">从上往下开</el-radio>
<el-radio label="btt">从下往上开</el-radio>
</el-radio-group>
<el-button @click="drawer = true" type="primary" style="margin-left: 16px;">
点我打开
</el-button>
<el-drawer
title="我是标题"
:visible.sync="drawer"
:direction="direction"
:before-close="handleClose">
<span>我来啦!</span>
</el-drawer>
<script>
export default {
data() {
return {
drawer: false,
direction: 'rtl',
};
},
methods: {
handleClose(done) {
this.$confirm('确认关闭?')
.then(_ => {
done();
})
.catch(_ => {});
}
}
};
</script>
```
:::
### 自定义内容
`Dialog` 组件一样, `Drawer` 同样可以在其内部嵌套各种丰富的操作
:::demo
```html
<el-button type="text" @click="table = true">打开嵌套表格的 Drawer</el-button>
<el-button type="text" @click="dialog = true">打开嵌套 Form 的 Drawer</el-button>
<el-drawer
title="我嵌套了表格!"
:visible.sync="table"
direction="rtl"
size="50%">
<el-table :data="gridData">
<el-table-column property="date" label="日期" width="150"></el-table-column>
<el-table-column property="name" label="姓名" width="200"></el-table-column>
<el-table-column property="address" label="地址"></el-table-column>
</el-table>
</el-drawer>
<el-drawer
title="我嵌套了 Form !"
:before-close="handleClose"
:visible.sync="dialog"
direction="ltr"
custom-class="demo-drawer"
ref="drawer"
>
<div class="demo-drawer__content">
<el-form :model="form">
<el-form-item label="活动名称" :label-width="formLabelWidth">
<el-input v-model="form.name" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="活动区域" :label-width="formLabelWidth">
<el-select v-model="form.region" placeholder="请选择活动区域">
<el-option label="区域一" value="shanghai"></el-option>
<el-option label="区域二" value="beijing"></el-option>
</el-select>
</el-form-item>
</el-form>
<div class="demo-drawer__footer">
<el-button @click="dialog = false">取 消</el-button>
<el-button type="primary" @click="$refs.drawer.closeDrawer()" :loading="loading">{{ loading ? '提交中 ...' : '确 定' }}</el-button>
</div>
</div>
</el-drawer>
<script>
export default {
data() {
return {
table: false,
dialog: false,
loading: false,
gridData: [{
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
}, {
date: '2016-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
}, {
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
}, {
date: '2016-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
}],
form: {
name: '',
region: '',
date1: '',
date2: '',
delivery: false,
type: [],
resource: '',
desc: ''
},
formLabelWidth: '80px'
};
},
methods: {
handleClose(done) {
this.$confirm('确定要提交表单吗?')
.then(_ => {
this.loading = true;
setTimeout(() => {
this.loading = false;
done();
}, 2000);
})
.catch(_ => {});
}
}
}
</script>
```
:::
### 多层嵌套
`Drawer` 组件也拥有多层嵌套的方法
:::demo 同样, 如果你需要嵌套多层 `Drawer` 请一定要设置 `append-to-body` 属性为 **true**
```html
<el-button @click="drawer = true" type="primary" style="margin-left: 16px;">
点我打开
</el-button>
<el-drawer
title="我是外面的 Drawer"
:visible.sync="drawer"
size="50%">
<div>
<el-button @click="innerDrawer = true">打开里面的!</el-button>
<el-drawer
title="我是里面的"
:append-to-body="true"
:before-close="handleClose"
:visible.sync="innerDrawer">
<p>_(:зゝ∠)_</p>
</el-drawer>
</div>
</el-drawer>
<script>
export default {
data() {
return {
drawer: false,
innerDrawer: false,
};
},
methods: {
handleClose(done) {
this.$confirm('还有未保存的工作哦确定关闭吗?')
.then(_ => {
done();
})
.catch(_ => {});
}
}
};
</script>
```
:::
:::tip
Drawer 的内容是懒渲染的,即在第一次被打开之前,传入的默认 slot 不会被渲染到 DOM 上。因此,如果需要执行 DOM 操作,或通过 `ref` 获取相应组件,请在 `open` 事件回调中进行。
:::
:::tip
Drawer 提供一个 `destroyOnClose` API, 用来在关闭 Drawer 时销毁子组件内容, 例如清理表单内的状态, 在必要时可以将该属性设置为 **true** 用来保证初始状态的一致性
:::
:::tip
如果 `visible` 属性绑定的变量位于 Vuex 的 store 内,那么 `.sync` 不会正常工作。此时需要去除 `.sync` 修饰符,同时监听 Drawer 的 `open``close` 事件,在事件回调中执行 Vuex 中对应的 mutation 更新 `visible` 属性绑定的变量的值。
:::
### Drawer Attributes
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---------- |-------------- |---------- |-------------------------------- |-------- |
| append-to-body | Drawer 自身是否插入至 body 元素上。嵌套的 Drawer 必须指定该属性并赋值为 true | boolean | — | false |
| before-close | 关闭前的回调,会暂停 Drawer 的关闭 | function(done)done 用于关闭 Drawer | — | — |
| close-on-press-escape | 是否可以通过按下 ESC 关闭 Drawer | boolean | — | true |
| custom-class | Drawer 的自定义类名 | string | — | — |
| destroy-on-close | 控制是否在关闭 Drawer 之后将子元素全部销毁 | boolean | - | false |
| modal | 是否需要遮罩层 | boolean | — | true |
| modal-append-to-body | 遮罩层是否插入至 body 元素上,若为 false则遮罩层会插入至 Drawer 的父元素上 | boolean | — | true |
| direction | Drawer 打开的方向 | Direction | rtl / ltr / ttb / tbb | rtl |
| show-close | 是否显示关闭按钮 | boolean | — | true |
| size | Drawer 窗体的大小, 当使用 `number` 类型时, 以像素为单位, 当使用 `string` 类型时, 请传入 'x%', 否则便会以 `number` 类型解释 | number / string | - | '30%' |
| title | Drawer 的标题,也可通过具名 slot (见下表)传入 | string | — | — |
| visible | 是否显示 Drawer支持 .sync 修饰符 | boolean | — | false |
| wrapperClosable | 点击遮罩层是否可以关闭 Drawer | boolean | - | true |
### Drawer Slot
| name | 说明 |
|------|--------|
| — | Drawer 的内容 |
| title | Drawer 标题区的内容 |
### Drawer Methods
| name | 说明 |
| ---- | --- |
| closeDrawer | 用于关闭 Drawer, 该方法会调用传入的 `before-close` 方法 |
### Drawer Events
| 事件名称 | 说明 | 回调参数 |
|---------- |-------- |---------- |
| open | Drawer 打开的回调 | — |
| opened | Drawer 打开动画结束时的回调 | — |
| close | Drawer 关闭的回调 | — |
| closed | Drawer 关闭动画结束时的回调 | — |

View File

@ -283,6 +283,10 @@
{
"path": "/infiniteScroll",
"title": "InfiniteScroll 无限滚动"
},
{
"path": "/drawer",
"title": "Drawer 抽屉"
}
]
}
@ -573,6 +577,10 @@
{
"path": "/avatar",
"title": "Avatar"
},
{
"path": "/drawer",
"title": "Drawer"
}
]
}
@ -863,6 +871,10 @@
{
"path": "/avatar",
"title": "Avatar"
},
{
"path": "/drawer",
"title": "Drawer"
}
]
}
@ -1153,6 +1165,10 @@
{
"path": "/avatar",
"title": "Avatar"
},
{
"path": "/drawer",
"title": "Drawer"
}
]
}

8
packages/drawer/index.js Normal file
View File

@ -0,0 +1,8 @@
import Drawer from './src/main';
/* istanbul ignore next */
Drawer.install = function(Vue) {
Vue.component(Drawer.name, Drawer);
};
export default Drawer;

View File

@ -0,0 +1,167 @@
<template>
<transition
name="el-drawer-fade"
@after-enter="afterEnter"
@after-leave="afterLeave">
<div
class="el-dialog__wrapper"
role="presentation"
v-show="visible">
<div
class="el-drawer__container"
:class="visible && 'el-drawer__open'"
@click.self="handleWrapperClick"
role="document"
tabindex="-1">
<div
aria-modal="true"
aria-labelledby="el-drawer__title"
class="el-drawer"
:class="[direction, customClass]"
:style="isHorizontal ? `width: ${size}` : `height: ${size}`"
ref="drawer"
role="presentation">
<header class="el-drawer__header" id="el-drawer__title">
<slot name="title">
<span role="heading">{{ title }}</span>
</slot>
<button
:aria-label="`close ${title || 'drawer'}`"
class="el-drawer__close-btn"
type="button"
v-if="showClose"
@click="closeDrawer">
<i class="el-dialog__close el-icon el-icon-close"></i>
</button>
</header>
<section class="el-drawer__body" v-if="rendered">
<slot></slot>
</section>
</div>
</div>
</div>
</transition>
</template>
<script>
import Popup from 'element-ui/src/utils/popup';
import Migrating from 'element-ui/src/mixins/migrating';
import emitter from 'element-ui/src/mixins/emitter';
export default {
name: 'ElDrawer',
mixins: [Popup, emitter, Migrating],
props: {
appendToBody: {
type: Boolean,
default: true
},
beforeClose: {
type: Function
},
customClass: {
type: String,
default: ''
},
destroyOnClose: {
type: Boolean,
default: false
},
modal: {
type: Boolean,
default: true
},
direction: {
type: String,
default: 'rtl',
validator(val) {
return ['ltr', 'rtl', 'ttb', 'btt'].indexOf(val) !== -1;
}
},
showClose: {
type: Boolean,
default: true
},
size: {
type: String,
default: '30%'
},
title: {
type: String,
default: ''
},
visible: {
type: Boolean
},
wrapperClosable: {
type: Boolean,
default: true
}
},
computed: {
isHorizontal() {
return this.direction === 'rtl' || this.direction === 'ltr';
}
},
data() {
return {
closed: false
};
},
watch: {
visible(val) {
if (val) {
this.closed = false;
this.$emit('open');
if (this.appendToBody) {
document.body.appendChild(this.$el);
}
} else {
if (!this.closed) this.$emit('close');
}
}
},
methods: {
afterEnter() {
this.$emit('opened');
},
afterLeave() {
this.$emit('closed');
},
hide(cancel) {
if (cancel !== false) {
this.$emit('update:visible', false);
this.$emit('close');
if (this.destroyOnClose === true) {
this.rendered = false;
}
this.closed = true;
}
},
handleWrapperClick() {
if (this.wrapperClosable) {
this.closeDrawer();
}
},
closeDrawer() {
if (typeof this.beforeClose === 'function') {
this.beforeClose(this.hide);
} else {
this.hide();
}
}
},
mounted() {
if (this.visible) {
this.rendered = true;
this.open();
}
},
destroyed() {
// if appendToBody is true, remove DOM node after destroy
if (this.appendToBody && this.$el && this.$el.parentNode) {
this.$el.parentNode.removeChild(this.$el);
}
}
};
</script>

View File

@ -0,0 +1,207 @@
@import "mixins/mixins";
@import "common/var";
@keyframes el-drawer-fade-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@mixin drawer-animation($direction) {
@keyframes #{$direction}-drawer-in {
0% {
@if $direction == ltr {
transform: translate(-100%, 0px);
}
@if $direction == rtl {
transform: translate(100%, 0px);
}
@if $direction == ttb {
transform: translate(0px, -100%);
}
@if $direction == btt {
transform: translate(0px, 100%);
}
}
100% {
@if $direction == ltr {
transform: translate(0px, 0px);
}
@if $direction == rtl {
transform: translate(0px, 0px);
}
@if $direction == ttb {
transform: translate(0px, 0px);
}
@if $direction == btt {
transform: translate(0px, 0px);
}
}
}
@keyframes #{$direction}-drawer-out {
0% {
@if $direction == ltr {
transform: translate(0px, 0px);
}
@if $direction == rtl {
transform: translate(0px, 0px);;
}
@if $direction == ttb {
transform: translate(0px, 0px);
}
@if $direction == btt {
transform: translate(0px, 0);
}
}
100% {
@if $direction == ltr {
transform: translate(-100%, 0px);
}
@if $direction == rtl {
transform: translate(100%, 0px);
}
@if $direction == ttb {
transform: translate(0px, -100%);
}
@if $direction == btt {
transform: translate(0px, 100%);
}
}
}
}
@mixin animation-in($direction) {
.el-drawer__open &.#{$direction} {
animation: #{$direction}-drawer-in 225ms cubic-bezier(0, 0, .2, 1) 0ms;
}
}
@mixin animation-out($direction) {
&.#{$direction} {
animation: #{$direction}-drawer-out 225ms cubic-bezier(0, 0, .2, 1) 0ms;
}
}
@include drawer-animation(rtl)
@include drawer-animation(ltr)
@include drawer-animation(ttb)
@include drawer-animation(btt)
$directions: rtl, ltr, ttb, btt;
@include b(drawer) {
position: absolute;
box-sizing: border-box;
background-color: $--dialog-background-color;
display: flex;
flex-direction: column;
box-shadow: 0 8px 10px -5px rgba(0, 0, 0, 0.2),
0 16px 24px 2px rgba(0, 0, 0, 0.14),
0 6px 30px 5px rgba(0, 0, 0, 0.12);
overflow: hidden;
@each $direction in $directions {
@include animation-out($direction);
@include animation-in($direction);
}
&__header {
align-items: center;
color: rgb(114, 118, 123);
display: flex;
margin-bottom: 32px;
padding: $--dialog-padding-primary;
padding-bottom: 0;
& > :first-child {
flex: 1;
}
}
&__title {
margin: 0;
flex: 1;
line-height: inherit;
font-size: 1rem;
}
&__close-btn {
border: none;
cursor: pointer;
font-size: $--font-size-extra-large;
color: inherit;
background-color: transparent;
}
&__body {
flex: 1;
& > * {
box-sizing: border-box;
}
}
&.ltr, &.rtl {
height: 100%;
top: 0;
bottom: 0;
}
&.ttb, &.btt {
width: 100%;
left: 0;
right: 0;
}
&.ltr {
left: 0;
}
&.rtl {
right: 0;
}
&.ttb {
top: 0;
}
&.btt {
bottom: 0;
}
}
.el-drawer__container {
position: relative;
left: 0;
right: 0;
top: 0;
bottom: 0;
height: 100%;
width: 100%;
}
.el-drawer-fade-enter-active {
animation: el-drawer-fade-in 225ms cubic-bezier(0, 0, 0.2, 1) 0ms;
}
.el-drawer-fade-leave-active {
animation: el-drawer-fade-in 225ms cubic-bezier(0, 0, 0.2, 1) 0ms reverse;
}

View File

@ -76,3 +76,4 @@
@import "./page-header.scss";
@import "./cascader-panel.scss";
@import "./avatar.scss";
@import "./drawer.scss";

View File

@ -80,6 +80,7 @@ import InfiniteScroll from '../packages/infinite-scroll/index.js';
import PageHeader from '../packages/page-header/index.js';
import CascaderPanel from '../packages/cascader-panel/index.js';
import Avatar from '../packages/avatar/index.js';
import Drawer from '../packages/drawer/index.js';
import locale from 'element-ui/src/locale';
import CollapseTransition from 'element-ui/src/transitions/collapse-transition';
@ -159,6 +160,7 @@ const components = [
PageHeader,
CascaderPanel,
Avatar,
Drawer,
CollapseTransition
];
@ -278,5 +280,6 @@ export default {
InfiniteScroll,
PageHeader,
CascaderPanel,
Avatar
Avatar,
Drawer
};

View File

@ -0,0 +1,332 @@
import { createVue, destroyVM, waitImmediate, wait } from '../util';
const title = '我是测试 title';
const content = 'content';
describe('Drawer', () => {
let vm;
afterEach(() => {
destroyVM(vm);
});
it('create', async() => {
vm = createVue(
{
template: `
<el-drawer :title='title' :visible='visible'></el-drawer>
`,
data() {
return {
title,
visible: true
};
}
},
true
);
const drawer = vm.$children[0];
await waitImmediate();
expect(document.querySelector('.v-modal')).to.exist;
expect(vm.$el.querySelector('.el-drawer__header').textContent).to.equal(
title
);
expect(drawer.$el.style.display).to.not.equal('none');
});
it('render correct content', async() => {
vm = createVue(
{
template: `
<el-drawer :title='title' :visible='visible'>
<span>这是一段信息</span>
<el-button @click.native='dialogVisible = false'>取消</el-button>
<el-button type='primary' @click.native='dialogVisible = false'>确定</el-button>
</el-drawer>
`,
data() {
return {
title: 'drawer test',
visible: true
};
}
},
true
);
await waitImmediate();
expect(vm.$el.querySelector('.el-drawer__body span').textContent).to.equal(
'这是一段信息'
);
const footerBtns = vm.$el.querySelectorAll('.el-button');
expect(footerBtns.length).to.equal(2);
expect(footerBtns[0].querySelector('span').textContent).to.equal('取消');
expect(footerBtns[1].querySelector('span').textContent).to.equal('确定');
});
it('should append to body, when append-to-body flag is true', async() => {
vm = createVue(
{
template: `
<el-drawer :title='title' :visible='visible' :append-to-body='true'>
<span> content </span>
</el-drawer>
`,
data() {
return {
title,
visible: true
};
}
},
true
);
await waitImmediate();
expect(vm.$el.parentNode).to.equal(document.body);
});
it('should open and close drawer properly', async() => {
vm = createVue({
template: `
<el-drawer :title='title' :visible='visible' ref='drawer'>
<span>${content}</span>
</el-drawer>
`,
data() {
return {
title,
visible: false
};
}
});
let drawer = vm.$children[0].$el;
expect(drawer.style.display).to.equal('none');
vm.visible = true;
await waitImmediate();
expect(drawer.style.display).not.to.equal('none');
vm.visible = false;
await wait(400);
expect(drawer.style.display).to.equal('none');
});
it('should destroy every child after drawer was closed when destroy-on-close flag is true', async() => {
vm = createVue({
template: `
<el-drawer :title='title' :visible='visible' :append-to-body='true' :destroy-on-close='true' ref='drawer'>
<span>${content}</span>
</el-drawer>
`,
data() {
return {
title,
visible: true
};
}
});
await waitImmediate();
expect(vm.$el.querySelector('.el-drawer__body span').textContent).to.equal(
content
);
vm.$refs.drawer.closeDrawer();
await wait(400);
expect(vm.$el.querySelector('.el-drawer__body')).not.to.exist;
});
it('should close dialog by clicking the close button', async() => {
vm = createVue({
template: `
<el-drawer :title='title' :visible.sync='visible' :append-to-body='true' :destroy-on-close='true' ref='drawer'>
<span>${content}</span>
</el-drawer>
`,
data() {
return {
title,
visible: true
};
}
});
await waitImmediate();
vm.$children[0].$el.querySelector('.el-drawer__close-btn').click();
expect(vm.visible).to.equal(false);
});
it('should invoke before-close', async() => {
const beforeClose = sinon.spy();
vm = createVue({
template: `
<el-drawer
:before-close='beforeClose'
:title='title'
:visible.sync='visible'
:append-to-body='true'
:destroy-on-close='true'
ref='drawer'
>
<span>${content}</span>
</el-drawer>
`,
data() {
return {
title,
visible: true,
beforeClose
};
}
});
await waitImmediate();
vm.$refs.drawer.closeDrawer();
await waitImmediate();
expect(beforeClose.called).to.be.true;
});
it('should not show close button when show-close flag is false', async() => {
vm = createVue({
template: `
<el-drawer :title='title' :visible='visible' ref='drawer' :show-close='false'>
<span>${content}</span>
</el-drawer>
`,
data() {
return {
title,
visible: false
};
}
});
expect(vm.$el.querySelector('.el-drawer__close-btn')).not.to.exist;
});
it('should have custom classes when custom classes were given', async() => {
const classes = 'some-custom-class';
vm = createVue({
template: `
<el-drawer :title='title' :visible='visible' ref='drawer' custom-class='${classes}'>
<span>${content}</span>
</el-drawer>
`,
data() {
return {
title,
visible: false
};
}
});
expect(vm.$el.querySelector(`.${classes}`)).to.exist;
});
describe('directions', () => {
const renderer = direction => {
return createVue({
template: `
<el-drawer :title='title' :visible='visible' direction='${direction}'>
<span>${content}</span>
</el-drawer>
`,
data: {
visible: true,
title
}
});
};
it('should render from left to right', async() => {
vm = renderer('ltr');
await waitImmediate();
expect(vm.$el.querySelector('.ltr')).to.exist;
});
it('should render from right to left', async() => {
vm = renderer('rtl');
await waitImmediate();
expect(vm.$el.querySelector('.rtl')).to.exist;
});
it('should render from top to bottom', async() => {
vm = renderer('ttb');
await waitImmediate();
expect(vm.$el.querySelector('.ttb')).to.exist;
});
it('should render from bottom to top', async() => {
vm = renderer('btt');
await waitImmediate();
expect(vm.$el.querySelector('.btt')).to.exist;
});
});
it('events', async() => {
const open = sinon.spy();
const opened = sinon.spy();
const close = sinon.spy();
const closed = sinon.spy();
vm = createVue({
template: `
<el-drawer
:title='title'
:visible='visible'
ref="drawer"
@open="open"
@opened="opened"
@close="close"
@closed="closed">
<span>${content}</span>
</el-drawer>
`,
data() {
return {
content,
visible: false,
title
};
},
methods: {
close,
closed,
open,
opened
}
});
vm.visible = true;
await wait(400);
expect(open.called).to.be.true;
expect(opened.called).to.be.true;
expect(close.called).to.be.false;
expect(closed.called).to.be.false;
vm.visible = false;
await wait(500);
expect(close.called).to.be.true;
expect(closed.called).to.be.true;
});
describe('size', () => {
const renderer = (size, isVertical) =>
createVue({
template: `
<el-drawer :title='title' :visible='visible' direction='${isVertical ? 'ltr' : 'ttb'}' size='${size}'>
<span>${content}</span>
</el-drawer>
`,
data: {
visible: true,
title
}
});
it('should effect height when drawer is vertical', async() => {
const size = '50%';
vm = renderer(size, true);
expect(vm.$el.querySelector('.el-drawer').style.width).to.equal('50%');
});
it('should effect width when drawer is horizontal', async() => {
const size = '50%';
vm = renderer(size, false);
expect(vm.$el.querySelector('.el-drawer').style.height).to.equal('50%');
});
});
});

63
types/drawer.d.ts vendored Normal file
View File

@ -0,0 +1,63 @@
import { ElementUIComponent } from './component'
import { VNode } from 'vue'
type hide = (shouldCancel: boolean) => void
declare enum Direction {
LTR = 'ltr', // left to right
RTL = 'rtl', // right to left
TTB = 'ttb', // top to bottom
BTT = 'btt' // bottom to top
}
interface DrawerSlots {
/* Main Content Slots */
default: VNode[];
/* Title Slots */
title: VNode[];
[key: string]: VNode[]
}
/** Drawer Component */
export declare class ElDrawer extends ElementUIComponent {
/* Equivalent to `Dialog`'s append to body attribute, when applying nested drawer, make sure this one is set to true */
appendToBody: boolean
/* Hook method called before close drawer, the first parameter is a function which should determine if the drawer should be closed */
beforeClose: (done: hide) => void
/** Whether the Drawer can be closed by pressing ESC */
closeOnPressEscape: boolean
/** Custom class names for Dialog */
customClass: string
/* Determine whether the wrapped children should be destroyed, if true, children's destroyed life cycle method will be called all local state will be destroyed */
destroyOnClose: boolean
/* Equivalent to `Dialog`'s modal attribute, determines whether the dark shadowing background should show */
modal: boolean
/* Equivalent to `Dialog`'s modal-append-to-body attribute, determines whether the shadowing background should be inserted direct to DocumentBody element */
modalAppendToBody: boolean
/* Attributes that controls the drawer's direction of display*/
position: Direction
/* Whether the close button should be rendered to control the drawer's visible state */
showClose: boolean
/* The size of the drawer component, supporting number with unit of pixel, string by percentage e.g. 30% */
size: number | string
/* The Drawer's title, also can be replaced by named slot `title` */
title: string
/* Whether the drawer component should show, also can be decorated by `.sync` */
visible: boolean
/* Flag attribute whi */
wrapperClosable: boolean
$slots: DrawerSlots
}

View File

@ -78,6 +78,7 @@ import { ElBacktop } from './backtop'
import { ElInfiniteScroll } from './infinite-scroll'
import { ElPageHeader } from './page-header'
import { ElAvatar } from './avatar'
import { ElDrawer } from './drawer'
export interface InstallationOptions {
locale: any,
@ -336,3 +337,6 @@ export class PageHeader extends ElPageHeader {}
/** Avatar Component */
export class Avatar extends ElAvatar {}
/** Drawer Component */
export class Drawer extends ElDrawer {}