Merge pull request #15764 from element-plus/dev

D2M
This commit is contained in:
kooriookami
2024-02-02 15:54:15 +08:00
committed by GitHub
87 changed files with 820 additions and 637 deletions

View File

@@ -6,6 +6,7 @@ on:
pull_request:
branches:
- master
workflow_dispatch:
jobs:
build:

View File

@@ -1,6 +1,8 @@
name: 🧪 Test SSR
on: pull_request
on:
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.number || github.sha }}

View File

@@ -1,6 +1,8 @@
name: 🧪 Unit Test
on: pull_request
on:
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.number || github.sha }}

View File

@@ -1,5 +1,29 @@
## Changelog
### 2.5.4
_2024-02-02_
#### Features
- Components [dialog] Dialog can drag overflow the viewport (#15643 by @kooriookami)
- Components [message-box] MessageBox can drag overflow the viewport (#15674 by @kooriookami)
- Play custom namespace (#15677 by @btea)
- Components [check-tag] add type prop (#15727 by @kooriookami)
#### Bug fixes
- Components [table] The namespace of the showOverflowTooltip is invalid. (#15627 by @xingyixiang)
- Components [select] Remote search does not show menu when it has options and no query (#15683 by @wen403)
- Docs overlay error caused by sidebar (#15694 by @heygsc)
- Components [select] Fix value with value-key not selected (#15681 by @kooriookami)
- Components [tour] buttonProps & isSameProps errors (#15680 by @Fuphoenixes)
- Components [select] el-option-group error when el-option in a component (#15703 by @Fuphoenixes)
- Components [drawer] click allow focus out (#11965 by @tolking)
- Components [menu] Add menu-item margin when calculating `calcSliceIndex` (#15699 by @zhixiaotong)
- Components [upload] Add crossorigin property (#15524 by @wkasunsampath)
- Components [tabs] slot name use kebab-case style (#15733 by @chenxch)
### 2.5.3
_2024-01-22_

View File

@@ -39,12 +39,26 @@ const onClick = (item: string) => {
const resourceCards = computed(() => [
{
key: 'axure',
title: resourceLang.value.axure,
description: 'Axure RP 9.0',
icon: AxureComponentsSvg,
intro: resourceLang.value.axureIntro,
url: resourceUrl.axure,
key: '2023-figma-ui-kit',
title: resourceLang.value.figma2023,
description: '2023 Figma UI Kit',
icon: FigmaUiKitSvg,
intro: resourceLang.value.figma2023Intro,
url: 'https://www.figma.com/community/file/1305760370797950824/element-plus-design-system-ui-kit',
},
{
key: 'figma-variables',
title: resourceLang.value.figmaVariables,
icon: FigmaVariablesSvg,
intro: resourceLang.value.figmaVariablesIntro,
url: 'https://www.figma.com/community/file/1256091634199852065',
},
{
key: 'figma',
title: resourceLang.value.figma,
icon: FigmaTemplateSvg,
intro: resourceLang.value.figmaIntro,
url: 'https://www.figma.com/community/file/1021254029764378306',
},
{
key: 'sketch',
@@ -55,26 +69,12 @@ const resourceCards = computed(() => [
url: resourceUrl.sketch,
},
{
key: 'figma',
title: resourceLang.value.figma,
icon: FigmaTemplateSvg,
intro: resourceLang.value.figmaIntro,
url: 'https://www.figma.com/community/file/1021254029764378306',
},
{
key: 'figma-variables',
title: resourceLang.value.figmaVariables,
icon: FigmaVariablesSvg,
intro: resourceLang.value.figmaVariablesIntro,
url: 'https://www.figma.com/community/file/1256091634199852065',
},
{
key: '2023-figma-ui-kit',
title: resourceLang.value.figma2023,
description: '2023 Figma UI Kit',
icon: FigmaUiKitSvg,
intro: resourceLang.value.figma2023Intro,
url: 'https://www.figma.com/community/file/1305760370797950824/element-plus-design-system-ui-kit',
key: 'axure',
title: resourceLang.value.axure,
description: 'Axure RP 9.0',
icon: AxureComponentsSvg,
intro: resourceLang.value.axureIntro,
url: resourceUrl.axure,
},
])
</script>

View File

@@ -42,6 +42,7 @@
@include respond-to('md') {
width: calc(var(--vp-sidebar-width-small));
z-index: var(--sidebar-z-index-mobile);
}
@include respond-to('lg') {

View File

@@ -99,7 +99,7 @@ dialog/destroy-on-close
Try to drag the `header` part.
:::demo Set `draggable` to `true` to drag.
:::demo Set `draggable` to `true` to drag. Set `overflow` ^(2.5.4) to `true` can drag overflow the viewport.
dialog/draggable-dialog
@@ -116,7 +116,7 @@ When using `modal` = false, please make sure that `append-to-body` was set to **
### Attributes
| Name | Description | Type | Default |
| -------------------------- | ---------------------------------------------------------------------------------------------------- | ----------------------------------- | ------- |
|----------------------------|------------------------------------------------------------------------------------------------------| ----------------------------------- | ------- |
| model-value / v-model | visibility of Dialog | ^[boolean] | — |
| title | title of Dialog. Can also be passed with a named slot (see the following table) | ^[string] | '' |
| width | width of Dialog, default is 50% | ^[string] / ^[number] | '' |
@@ -135,6 +135,7 @@ When using `modal` = false, please make sure that `append-to-body` was set to **
| show-close | whether to show a close button | ^[boolean] | true |
| before-close | callback before Dialog closes, and it will prevent Dialog from closing, use done to close the dialog | ^[Function]`(done: DoneFn) => void` | — |
| draggable | enable dragging feature for Dialog | ^[boolean] | false |
| overflow ^(2.5.4) | draggable Dialog can overflow the viewport | ^[boolean] | false |
| center | whether to align the header and footer in center | ^[boolean] | false |
| align-center ^(2.2.16) | whether to align the dialog both horizontally and vertically | ^[boolean] | false |
| destroy-on-close | destroy elements in Dialog when closed | ^[boolean] | false |

View File

@@ -115,12 +115,14 @@ Drawer provides an API called `destroyOnClose`, which is a flag variable that in
### Events
| Name | Description | Type |
| ------ | ------------------------------------------------ | ----------------------- |
| open | Triggered before Drawer opening animation begins | ^[Function]`() => void` |
| opened | Triggered after Drawer opening animation ended | ^[Function]`() => void` |
| close | Triggered before Drawer closing animation begins | ^[Function]`() => void` |
| closed | Triggered after Drawer closing animation ended | ^[Function]`() => void` |
| Name | Description | Type |
| ---------------- | ------------------------------------------------ | ----------------------- |
| open | Triggered before Drawer opening animation begins | ^[Function]`() => void` |
| opened | Triggered after Drawer opening animation ended | ^[Function]`() => void` |
| close | Triggered before Drawer closing animation begins | ^[Function]`() => void` |
| closed | Triggered after Drawer closing animation ended | ^[Function]`() => void` |
| open-auto-focus | triggers after Drawer opens and content focused | ^[Function]`() => void` |
| close-auto-focus | triggers after Drawer closed and content focused | ^[Function]`() => void` |
### Slots

View File

@@ -113,7 +113,7 @@ message-box/customized-icon
MessageBox can be draggable.
:::demo Setting `draggable` to `true` allows user to drag MessageBox.
:::demo Setting `draggable` to `true` allows user to drag MessageBox. Set `overflow` ^(2.5.4) to `true` can drag overflow the viewport.
message-box/draggable
@@ -159,7 +159,7 @@ The corresponding methods are: `ElMessageBox`, `ElMessageBox.alert`, `ElMessageB
### Options
| Name | Description | Type | Default |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------ |
|------------------------------|------------------------------------------------------------------------------------------------------------------------------------------| ---------------------------------------------------------------------------------- | ------------------------------------------------ |
| autofocus | auto focus when open MessageBox | ^[boolean] | true |
| title | title of the MessageBox | ^[string] | '' |
| message | content of the MessageBox | ^[string] / ^[VNode] / ^[Function]`() => VNode` ^(2.2.17) | — |
@@ -191,6 +191,7 @@ The corresponding methods are: `ElMessageBox`, `ElMessageBox.alert`, `ElMessageB
| input-error-message | error message when validation fails | ^[string] | Illegal input |
| center | whether to align the content in center | ^[boolean] | false |
| draggable | whether MessageBox is draggable | ^[boolean] | false |
| overflow ^(2.5.4) | draggable MessageBox can overflow the viewport | ^[boolean] | false |
| round-button | whether to use round button | ^[boolean] | false |
| button-size | custom size of confirm and cancel buttons | ^[string]`'small' \| 'default' \| 'large'` | default |
| append-to ^(2.2.19) | set the root element for the message box | ^[string] / ^[HTMLElement] | — |

View File

@@ -108,10 +108,11 @@ tabs/customized-trigger
## Tabs Slots
| Name | Description | Subtags |
| ---------------- | ------------------------- | -------- |
| - | customize default content | Tab-pane |
| addIcon ^(2.4.0) | customize add button icon | - |
| Name | Description | Subtags |
| ------------------------------ | ------------------------- | -------- |
| - | customize default content | Tab-pane |
| addIcon ^(2.4.0) ^(deprecated) | customize add button icon | - |
| add-icon ^(2.5.4) | customize add button icon | - |
## Tab-pane Attributes

View File

@@ -53,7 +53,7 @@ tag/theme
:::
## Rounded <el-tag>> 2.1.7</el-tag>
## Rounded
Tag can also be rounded like button.
@@ -63,9 +63,9 @@ tag/rounded
:::
## Checkable tag
## Checkable Tag
Sometimes because of the business needs, we might need checkbox like tag, but **button like checkbox** cannot meet our needs, here comes `check-tag`
Sometimes because of the business needs, we might need checkbox like tag, but **button like checkbox** cannot meet our needs, here comes `check-tag`. You can use `type` prop in ^(2.5.4).
:::demo basic check-tag usage, the API is rather simple.
@@ -77,16 +77,16 @@ tag/checkable
### Tag Attributes
| Name | Description | Type | Default |
| ------------------- | ------------------------------------ | ----------------------------------------------------------- | ------- |
| type | type of Tag | ^[enum]`'success' \| 'info' \| 'warning' \| 'danger' \| ''` | '' |
| closable | whether Tag can be removed | ^[boolean] | false |
| disable-transitions | whether to disable animations | ^[boolean] | false |
| hit | whether Tag has a highlighted border | ^[boolean] | false |
| color | background color of the Tag | ^[string] | '' |
| size | size of Tag | ^[enum]`'large' \| 'default' \| 'small' \| ''` | '' |
| effect | theme of Tag | ^[enum]`'dark' \| 'light' \| 'plain'` | light |
| round | whether Tag is rounded | ^[boolean] | false |
| Name | Description | Type | Default |
|---------------------|--------------------------------------|--------------------------------------------------------------------|---------|
| type | type of Tag | ^[enum]`'primary' \| 'success' \| 'info' \| 'warning' \| 'danger'` | primary |
| closable | whether Tag can be removed | ^[boolean] | false |
| disable-transitions | whether to disable animations | ^[boolean] | false |
| hit | whether Tag has a highlighted border | ^[boolean] | false |
| color | background color of the Tag | ^[string] | — |
| size | size of Tag | ^[enum]`'large' \| 'default' \| 'small'` | |
| effect | theme of Tag | ^[enum]`'dark' \| 'light' \| 'plain'` | light |
| round | whether Tag is rounded | ^[boolean] | false |
### Tag Events
@@ -105,9 +105,10 @@ tag/checkable
### CheckTag Attributes
| Name | Description | Type | Default |
| ------------------------- | ----------- | ---------- | ------- |
| checked / v-model:checked | is checked | ^[boolean] | false |
| Name | Description | Type | Default |
|---------------------------|------------------|--------------------------------------------------------------------|---------|
| checked / v-model:checked | is checked | ^[boolean] | false |
| type ^(2.5.4) | type of CheckTag | ^[enum]`'primary' \| 'success' \| 'info' \| 'warning' \| 'danger'` | primary |
### CheckTag Events

View File

@@ -47,33 +47,34 @@ time-picker/range
### Attributes
| Name | Description | Type | Default |
| --------------------- | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ----------- |
| model-value / v-model | binding value, if it is an array, the length should be 2 | ^[number] / ^[string] / ^[object]`Date \| [Date, Date] \| [number, number] \| [string, string]` | '' |
| readonly | whether TimePicker is read only | ^[boolean] | false |
| disabled | whether TimePicker is disabled | ^[boolean] | false |
| editable | whether the input is editable | ^[boolean] | true |
| clearable | whether to show clear button | ^[boolean] | true |
| size | size of Input | ^[enum]`'large' \| 'default' \| 'small'` | — |
| placeholder | placeholder in non-range mode | ^[string] | '' |
| start-placeholder | placeholder for the start time in range mode | ^[string] | — |
| end-placeholder | placeholder for the end time in range mode | ^[string] | — |
| is-range | whether to pick a time range | ^[boolean] | false |
| arrow-control | whether to pick time using arrow buttons | ^[boolean] | false |
| popper-class | custom class name for TimePicker's dropdown | ^[string] | '' |
| range-separator | range separator | ^[string] | '-' |
| format | format of the displayed value in the input box | ^[string] see [date formats](/en-US/component/date-picker#date-formats) | — |
| default-value | optional, default date of the calendar | ^[Date] / ^[object]`[Date, Date]` | — |
| id | same as `id` in native input | ^[string] / ^[object]`[string, string]` | — |
| name | same as `name` in native input | ^[string] | '' |
| label ^(a11y) | same as `aria-label` in native input | ^[string] | |
| prefix-icon | Custom prefix icon component | ^[string] / ^[Component] | Clock |
| clear-icon | Custom clear icon component | ^[string] / ^[Component] | CircleClose |
| disabled-hours | To specify the array of hours that cannot be selected | ^[Function]`(role: string, comparingDate?: Dayjs) => number[]` | |
| disabled-minutes | To specify the array of minutes that cannot be selected | ^[Function]`(hour: number, role: string, comparingDate?: Dayjs) => number[]` | — |
| disabled-seconds | To specify the array of seconds that cannot be selected | ^[Function]`(hour: number, minute: number, role: string, comparingDate?: Dayjs) => number[]` | — |
| teleported | whether time-picker dropdown is teleported to the body | ^[boolean] | true |
| tabindex | input tabindex | ^[string] / ^[number] | 0 |
| Name | Description | Type | Default |
| --------------------- | -------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ----------- |
| model-value / v-model | binding value, if it is an array, the length should be 2 | ^[number] / ^[string] / ^[object]`Date \| [Date, Date] \| [number, number] \| [string, string]` | '' |
| readonly | whether TimePicker is read only | ^[boolean] | false |
| disabled | whether TimePicker is disabled | ^[boolean] | false |
| editable | whether the input is editable | ^[boolean] | true |
| clearable | whether to show clear button | ^[boolean] | true |
| size | size of Input | ^[enum]`'large' \| 'default' \| 'small'` | — |
| placeholder | placeholder in non-range mode | ^[string] | '' |
| start-placeholder | placeholder for the start time in range mode | ^[string] | — |
| end-placeholder | placeholder for the end time in range mode | ^[string] | — |
| is-range | whether to pick a time range | ^[boolean] | false |
| arrow-control | whether to pick time using arrow buttons | ^[boolean] | false |
| popper-class | custom class name for TimePicker's dropdown | ^[string] | '' |
| range-separator | range separator | ^[string] | '-' |
| format | format of the displayed value in the input box | ^[string] see [date formats](/en-US/component/date-picker#date-formats) | — |
| default-value | optional, default date of the calendar | ^[Date] / ^[object]`[Date, Date]` | — |
| value-format | optional, format of binding value. If not specified, the binding value will be a Date object | ^[string] see [date formats](/en-US/component/date-picker#date-formats) | — |
| id | same as `id` in native input | ^[string] / ^[object]`[string, string]` | |
| name | same as `name` in native input | ^[string] | '' |
| label ^(a11y) | same as `aria-label` in native input | ^[string] | |
| prefix-icon | Custom prefix icon component | ^[string] / ^[Component] | Clock |
| clear-icon | Custom clear icon component | ^[string] / ^[Component] | CircleClose |
| disabled-hours | To specify the array of hours that cannot be selected | ^[Function]`(role: string, comparingDate?: Dayjs) => number[]` | — |
| disabled-minutes | To specify the array of minutes that cannot be selected | ^[Function]`(hour: number, role: string, comparingDate?: Dayjs) => number[]` | — |
| disabled-seconds | To specify the array of seconds that cannot be selected | ^[Function]`(hour: number, minute: number, role: string, comparingDate?: Dayjs) => number[]` | — |
| teleported | whether time-picker dropdown is teleported to the body | ^[boolean] | true |
| tabindex | input tabindex | ^[string] / ^[number] | 0 |
### Events

View File

@@ -126,10 +126,10 @@ tour-step component configuration with the same name has higher priority
### TourStep slots
| Name | Description |
| ------- | ----------- |
| default | description |
| title | title |
| Name | Description |
| -------- | ----------- |
| default | description |
| header | header |
### TourStep events

View File

@@ -105,6 +105,7 @@ upload/manual
| show-file-list | whether to show the uploaded file list. | ^[boolean] | true |
| drag | whether to activate drag and drop mode. | ^[boolean] | false |
| accept | accepted [file types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-accept), will not work when `thumbnail-mode === true`. | ^[string] | '' |
| crossorigin | native attribute [crossorigin](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin). | ^[enum]`'' \| 'anonymous' \| 'use-credentials'` | — |
| on-preview | hook function when clicking the uploaded files. | ^[Function]`(uploadFile: UploadFile) => void` | — |
| on-remove | hook function when files are removed. | ^[Function]`(uploadFile: UploadFile, uploadFiles: UploadFiles) => void` | — |
| on-success | hook function when uploaded successfully. | ^[Function]`(response: any, uploadFile: UploadFile, uploadFiles: UploadFiles) => void` | — |

View File

@@ -1,22 +1,22 @@
<template>
<el-button text @click="centerDialogVisible = true">
<el-button plain @click="centerDialogVisible = true">
Click to open the Dialog
</el-button>
<el-dialog
v-model="centerDialogVisible"
title="Warning"
width="30%"
width="500"
align-center
>
<span>Open the dialog from the center from the screen</span>
<template #footer>
<span class="dialog-footer">
<div class="dialog-footer">
<el-button @click="centerDialogVisible = false">Cancel</el-button>
<el-button type="primary" @click="centerDialogVisible = false">
Confirm
</el-button>
</span>
</div>
</template>
</el-dialog>
</template>
@@ -25,8 +25,3 @@ import { ref } from 'vue'
const centerDialogVisible = ref(false)
</script>
<style scoped>
.dialog-footer button:first-child {
margin-right: 10px;
}
</style>

View File

@@ -1,22 +1,22 @@
<template>
<el-button text @click="dialogVisible = true">
click to open the Dialog
<el-button plain @click="dialogVisible = true">
Click to open the Dialog
</el-button>
<el-dialog
v-model="dialogVisible"
title="Tips"
width="30%"
width="500"
:before-close="handleClose"
>
<span>This is a message</span>
<template #footer>
<span class="dialog-footer">
<div class="dialog-footer">
<el-button @click="dialogVisible = false">Cancel</el-button>
<el-button type="primary" @click="dialogVisible = false">
Confirm
</el-button>
</span>
</div>
</template>
</el-dialog>
</template>
@@ -37,8 +37,3 @@ const handleClose = (done: () => void) => {
})
}
</script>
<style scoped>
.dialog-footer button:first-child {
margin-right: 10px;
}
</style>

View File

@@ -1,20 +1,20 @@
<template>
<el-button text @click="centerDialogVisible = true">
<el-button plain @click="centerDialogVisible = true">
Click to open the Dialog
</el-button>
<el-dialog v-model="centerDialogVisible" title="Warning" width="30%" center>
<el-dialog v-model="centerDialogVisible" title="Warning" width="500" center>
<span>
It should be noted that the content will not be aligned in center by
default
</span>
<template #footer>
<span class="dialog-footer">
<div class="dialog-footer">
<el-button @click="centerDialogVisible = false">Cancel</el-button>
<el-button type="primary" @click="centerDialogVisible = false">
Confirm
</el-button>
</span>
</div>
</template>
</el-dialog>
</template>
@@ -23,8 +23,3 @@ import { ref } from 'vue'
const centerDialogVisible = ref(false)
</script>
<style scoped>
.dialog-footer button:first-child {
margin-right: 10px;
}
</style>

View File

@@ -1,9 +1,13 @@
<template>
<el-button text @click="dialogTableVisible = true">
open a Table nested Dialog
<el-button plain @click="dialogTableVisible = true">
Open a Table nested Dialog
</el-button>
<el-dialog v-model="dialogTableVisible" title="Shipping address">
<el-button plain @click="dialogFormVisible = true">
Open a Form nested Dialog
</el-button>
<el-dialog v-model="dialogTableVisible" title="Shipping address" width="800">
<el-table :data="gridData">
<el-table-column property="date" label="Date" width="150" />
<el-table-column property="name" label="Name" width="200" />
@@ -11,12 +15,7 @@
</el-table>
</el-dialog>
<!-- Form -->
<el-button text @click="dialogFormVisible = true">
open a Form nested Dialog
</el-button>
<el-dialog v-model="dialogFormVisible" title="Shipping address">
<el-dialog v-model="dialogFormVisible" title="Shipping address" width="500">
<el-form :model="form">
<el-form-item label="Promotion name" :label-width="formLabelWidth">
<el-input v-model="form.name" autocomplete="off" />
@@ -29,12 +28,12 @@
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<div class="dialog-footer">
<el-button @click="dialogFormVisible = false">Cancel</el-button>
<el-button type="primary" @click="dialogFormVisible = false">
Confirm
</el-button>
</span>
</div>
</template>
</el-dialog>
</template>
@@ -80,17 +79,3 @@ const gridData = [
},
]
</script>
<style scoped>
.el-button--text {
margin-right: 15px;
}
.el-select {
width: 300px;
}
.el-input {
width: 300px;
}
.dialog-footer button:first-child {
margin-right: 10px;
}
</style>

View File

@@ -1,8 +1,9 @@
<template>
<el-button @click="visible = true">
<el-button plain @click="visible = true">
Open Dialog with customized header
</el-button>
<el-dialog v-model="visible" :show-close="false">
<el-dialog v-model="visible" :show-close="false" width="500">
<template #header="{ close, titleId, titleClass }">
<div class="my-header">
<h4 :id="titleId" :class="titleClass">This is a custom header!</h4>
@@ -29,5 +30,6 @@ const visible = ref(false)
display: flex;
flex-direction: row;
justify-content: space-between;
gap: 16px;
}
</style>

View File

@@ -1,12 +1,12 @@
<template>
<el-button text @click="centerDialogVisible = true">
<el-button plain @click="centerDialogVisible = true">
Click to open Dialog
</el-button>
<el-dialog
v-model="centerDialogVisible"
title="Notice"
width="30%"
width="500"
destroy-on-close
center
>
@@ -18,12 +18,12 @@
<strong>Extra content (Not rendered)</strong>
</div>
<template #footer>
<span class="dialog-footer">
<div class="dialog-footer">
<el-button @click="centerDialogVisible = false">Cancel</el-button>
<el-button type="primary" @click="centerDialogVisible = false">
Confirm
</el-button>
</span>
</div>
</template>
</el-dialog>
</template>
@@ -33,8 +33,3 @@ import { ref } from 'vue'
const centerDialogVisible = ref(false)
</script>
<style scoped>
.dialog-footer button:first-child {
margin-right: 10px;
}
</style>

View File

@@ -1,17 +1,39 @@
<template>
<el-button text @click="dialogVisible = true">
Click to open Dialog
<el-button plain @click="dialogVisible = true">
Open a draggable Dialog
</el-button>
<el-dialog v-model="dialogVisible" title="Tips" width="30%" draggable>
<el-button plain @click="dialogOverflowVisible = true">
Open a overflow draggable Dialog
</el-button>
<el-dialog v-model="dialogVisible" title="Tips" width="500" draggable>
<span>It's a draggable Dialog</span>
<template #footer>
<span class="dialog-footer">
<div class="dialog-footer">
<el-button @click="dialogVisible = false">Cancel</el-button>
<el-button type="primary" @click="dialogVisible = false">
Confirm
</el-button>
</span>
</div>
</template>
</el-dialog>
<el-dialog
v-model="dialogOverflowVisible"
title="Tips"
width="500"
draggable
overflow
>
<span>It's a overflow draggable Dialog</span>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogOverflowVisible = false">Cancel</el-button>
<el-button type="primary" @click="dialogOverflowVisible = false">
Confirm
</el-button>
</div>
</template>
</el-dialog>
</template>
@@ -20,4 +42,5 @@
import { ref } from 'vue'
const dialogVisible = ref(false)
const dialogOverflowVisible = ref(false)
</script>

View File

@@ -1,47 +0,0 @@
<template>
<el-button text @click="dialogVisible = true">
click to open the Dialog
</el-button>
<div>
<p>Close dialog and the input will be focused</p>
<el-input ref="inputRef" placeholder="Please input" />
</div>
<el-dialog
v-model="dialogVisible"
destroy-on-close
title="Tips"
width="30%"
@close-auto-focus="handleCloseAutoFocus"
>
<span>This is a message</span>
<el-divider />
<el-input placeholder="Initially focused" />
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">Cancel</el-button>
<el-button type="primary" @click="dialogVisible = false">
Confirm
</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { ElInput } from 'element-plus'
const dialogVisible = ref(false)
const inputRef = ref<InstanceType<typeof ElInput>>()
const handleCloseAutoFocus = () => {
inputRef.value?.focus()
}
</script>
<style scoped>
.dialog-footer button:first-child {
margin-right: 10px;
}
</style>

View File

@@ -1,22 +1,23 @@
<template>
<el-button text @click="outerVisible = true">
open the outer Dialog
<el-button plain @click="outerVisible = true">
Open the outer Dialog
</el-button>
<el-dialog v-model="outerVisible" title="Outer Dialog">
<template #default>
<el-dialog
v-model="innerVisible"
width="30%"
title="Inner Dialog"
append-to-body
/>
</template>
<el-dialog v-model="outerVisible" title="Outer Dialog" width="800">
<span>This is the outer Dialog</span>
<el-dialog
v-model="innerVisible"
width="500"
title="Inner Dialog"
append-to-body
>
<span>This is the inner Dialog</span>
</el-dialog>
<template #footer>
<div class="dialog-footer">
<el-button @click="outerVisible = false">Cancel</el-button>
<el-button type="primary" @click="innerVisible = true">
open the inner Dialog
Open the inner Dialog
</el-button>
</div>
</template>
@@ -29,8 +30,3 @@ import { ref } from 'vue'
const outerVisible = ref(false)
const innerVisible = ref(false)
</script>
<style scoped>
.dialog-footer button:first-child {
margin-right: 10px;
}
</style>

View File

@@ -78,4 +78,9 @@ import {
font-size: 14px;
margin-bottom: 20px;
}
.block-col-2 .el-dropdown-link {
display: flex;
align-items: center;
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<el-button text @click="open">Click to open the Message Box</el-button>
<el-button plain @click="open">Click to open the Message Box</el-button>
</template>
<script lang="ts" setup>

View File

@@ -1,5 +1,5 @@
<template>
<el-button text @click="open">Click to open Message Box</el-button>
<el-button plain @click="open">Click to open Message Box</el-button>
</template>
<script lang="ts" setup>

View File

@@ -1,5 +1,5 @@
<template>
<el-button text @click="open">Click to open the Message Box</el-button>
<el-button plain @click="open">Click to open the Message Box</el-button>
</template>
<script lang="ts" setup>

View File

@@ -1,5 +1,5 @@
<template>
<el-button text @click="open">Click to open Message Box</el-button>
<el-button plain @click="open">Click to open Message Box</el-button>
</template>
<script lang="ts" setup>

View File

@@ -1,5 +1,5 @@
<template>
<el-button text @click="open">Click to open Message Box</el-button>
<el-button plain @click="open">Click to open Message Box</el-button>
</template>
<script lang="ts" setup>

View File

@@ -1,5 +1,5 @@
<template>
<el-button text @click="open">Click to open Message Box</el-button>
<el-button plain @click="open">Click to open Message Box</el-button>
</template>
<script lang="ts" setup>

View File

@@ -1,5 +1,8 @@
<template>
<el-button text @click="open">Click to open Message Box</el-button>
<el-button plain @click="open">Open a draggable Message Box</el-button>
<el-button plain @click="open2">
Open a overflow draggable Message Box
</el-button>
</template>
<script lang="ts" setup>
@@ -29,4 +32,30 @@ const open = () => {
})
})
}
const open2 = () => {
ElMessageBox.confirm(
'proxy will permanently delete the file. Continue?',
'Warning',
{
confirmButtonText: 'OK',
cancelButtonText: 'Cancel',
type: 'warning',
draggable: true,
overflow: true,
}
)
.then(() => {
ElMessage({
type: 'success',
message: 'Delete completed',
})
})
.catch(() => {
ElMessage({
type: 'info',
message: 'Delete canceled',
})
})
}
</script>

View File

@@ -1,5 +1,5 @@
<template>
<el-button text @click="open">Click to open Message Box</el-button>
<el-button plain @click="open">Click to open Message Box</el-button>
</template>
<script lang="ts" setup>

View File

@@ -1,5 +1,5 @@
<template>
<el-button text @click="open">Click to open Message Box</el-button>
<el-button plain @click="open">Click to open Message Box</el-button>
</template>
<script lang="ts" setup>

View File

@@ -7,7 +7,7 @@ addIcon
editable
@edit="handleTabsEdit"
>
<template #addIcon>
<template #add-icon>
<el-icon><Select /></el-icon>
</template>
<el-tab-pane

View File

@@ -1,7 +1,10 @@
<template>
<el-tag>Tag 1</el-tag>
<el-tag class="ml-2" type="success">Tag 2</el-tag>
<el-tag class="ml-2" type="info">Tag 3</el-tag>
<el-tag class="ml-2" type="warning">Tag 4</el-tag>
<el-tag class="ml-2" type="danger">Tag 5</el-tag>
<div class="flex gap-2">
<el-tag type="primary">Tag 1</el-tag>
<el-tag type="success">Tag 2</el-tag>
<el-tag type="info">Tag 3</el-tag>
<el-tag type="warning">Tag 4</el-tag>
<el-tag type="danger">Tag 5</el-tag>
</div>
</template>
<script setup lang="ts"></script>

View File

@@ -1,16 +1,58 @@
<template>
<div>
<el-check-tag checked style="margin-right: 8px">Checked</el-check-tag>
<div class="flex gap-2">
<el-check-tag checked>Checked</el-check-tag>
<el-check-tag :checked="checked" @change="onChange">Toggle me</el-check-tag>
</div>
<div class="flex gap-2 mt-4">
<el-check-tag :checked="checked1" type="primary" @change="onChange1">
Tag 1
</el-check-tag>
<el-check-tag :checked="checked2" type="success" @change="onChange2">
Tag 2
</el-check-tag>
<el-check-tag :checked="checked3" type="info" @change="onChange3">
Tag 3
</el-check-tag>
<el-check-tag :checked="checked4" type="warning" @change="onChange4">
Tag 4
</el-check-tag>
<el-check-tag :checked="checked5" type="danger" @change="onChange5">
Tag 5
</el-check-tag>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const checked = ref(false)
const checked1 = ref(true)
const checked2 = ref(true)
const checked3 = ref(true)
const checked4 = ref(true)
const checked5 = ref(true)
const onChange = (status: boolean) => {
checked.value = status
}
const onChange1 = (status: boolean) => {
checked1.value = status
}
const onChange2 = (status: boolean) => {
checked2.value = status
}
const onChange3 = (status: boolean) => {
checked3.value = status
}
const onChange4 = (status: boolean) => {
checked4.value = status
}
const onChange5 = (status: boolean) => {
checked5.value = status
}
</script>

View File

@@ -1,26 +1,27 @@
<template>
<el-tag
v-for="tag in dynamicTags"
:key="tag"
class="mx-1"
closable
:disable-transitions="false"
@close="handleClose(tag)"
>
{{ tag }}
</el-tag>
<el-input
v-if="inputVisible"
ref="InputRef"
v-model="inputValue"
class="ml-1 w-20"
size="small"
@keyup.enter="handleInputConfirm"
@blur="handleInputConfirm"
/>
<el-button v-else class="button-new-tag ml-1" size="small" @click="showInput">
+ New Tag
</el-button>
<div class="flex gap-2">
<el-tag
v-for="tag in dynamicTags"
:key="tag"
closable
:disable-transitions="false"
@close="handleClose(tag)"
>
{{ tag }}
</el-tag>
<el-input
v-if="inputVisible"
ref="InputRef"
v-model="inputValue"
class="w-20"
size="small"
@keyup.enter="handleInputConfirm"
@blur="handleInputConfirm"
/>
<el-button v-else class="button-new-tag" size="small" @click="showInput">
+ New Tag
</el-button>
</div>
</template>
<script lang="ts" setup>

View File

@@ -1,20 +1,16 @@
<template>
<el-tag
v-for="tag in tags"
:key="tag.name"
class="mx-1"
closable
:type="tag.type"
>
{{ tag.name }}
</el-tag>
<div class="flex gap-2">
<el-tag v-for="tag in tags" :key="tag.name" closable :type="tag.type">
{{ tag.name }}
</el-tag>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const tags = ref([
{ name: 'Tag 1', type: '' },
{ name: 'Tag 1', type: 'primary' },
{ name: 'Tag 2', type: 'success' },
{ name: 'Tag 3', type: 'info' },
{ name: 'Tag 4', type: 'warning' },

View File

@@ -1,34 +1,31 @@
<template>
<div class="flex flex-wrap gap-2 my-2">
<div class="flex gap-2">
<el-tag
v-for="item in items"
:key="item.label"
:type="item.type"
class="mx-1"
effect="dark"
round
>
{{ item.label }}
</el-tag>
</div>
<div class="flex flex-wrap gap-2">
<div class="flex gap-2 mt-4">
<el-tag
v-for="item in items"
:key="item.label"
:type="item.type"
class="mx-1"
effect="light"
round
>
{{ item.label }}
</el-tag>
</div>
<div class="flex flex-wrap gap-2 my-2">
<div class="flex gap-2 mt-4">
<el-tag
v-for="item in items"
:key="item.label"
:type="item.type"
class="mx-1"
effect="plain"
round
>
@@ -45,10 +42,10 @@ import type { TagProps } from 'element-plus'
type Item = { type: TagProps['type']; label: string }
const items = ref<Array<Item>>([
{ type: '', label: 'Tag 1' },
{ type: 'primary', label: 'Tag 1' },
{ type: 'success', label: 'Tag 2' },
{ type: 'info', label: 'Tag 3' },
{ type: 'danger', label: 'Tag 4' },
{ type: 'warning', label: 'Tag 5' },
{ type: 'warning', label: 'Tag 4' },
{ type: 'danger', label: 'Tag 5' },
])
</script>

View File

@@ -1,13 +1,7 @@
<template>
<el-row>
<el-tag class="mx-1" size="large">Large</el-tag>
<el-tag class="mx-1">Default</el-tag>
<el-tag class="mx-1" size="small">Small</el-tag>
</el-row>
<el-row class="mt-4">
<el-tag class="mx-1" size="large" closable>Large</el-tag>
<el-tag class="mx-1" closable>Default</el-tag>
<el-tag class="mx-1" size="small" closable>Small</el-tag>
</el-row>
<div class="flex gap-2">
<el-tag size="large">Large</el-tag>
<el-tag>Default</el-tag>
<el-tag size="small">Small</el-tag>
</div>
</template>

View File

@@ -1,69 +1,36 @@
<template>
<div class="tag-group my-2 flex flex-wrap gap-1 items-center">
<span class="tag-group__title m-1 line-height-2">Dark</span>
<div class="flex gap-2">
<span>Dark</span>
<el-tag
v-for="item in items"
:key="item.label"
:type="item.type"
class="mx-1"
effect="dark"
>
{{ item.label }}
</el-tag>
<el-tag
v-for="item in items"
:key="item.label"
:type="item.type"
class="mx-1"
effect="dark"
closable
>
{{ item.label }}
</el-tag>
</div>
<div class="tag-group my-2 flex flex-wrap gap-1 items-center">
<span class="tag-group__title m-1">Light</span>
<div class="flex gap-2 mt-4">
<span>Light</span>
<el-tag
v-for="item in items"
:key="item.label"
class="mx-1"
:type="item.type"
effect="light"
>
{{ item.label }}
</el-tag>
<el-tag
v-for="item in items"
:key="item.label"
class="mx-1"
:type="item.type"
effect="light"
closable
>
{{ item.label }}
</el-tag>
</div>
<div class="tag-group my-2 flex flex-wrap gap-1 items-center">
<span class="tag-group__title m-1">Plain</span>
<div class="flex gap-2 mt-4">
<span>Plain</span>
<el-tag
v-for="item in items"
:key="item.label"
class="mx-1"
:type="item.type"
effect="plain"
>
{{ item.label }}
</el-tag>
<el-tag
v-for="item in items"
:key="item.label"
class="mx-1"
:type="item.type"
effect="plain"
closable
>
{{ item.label }}
</el-tag>
</div>
</template>
@@ -75,10 +42,10 @@ import type { TagProps } from 'element-plus'
type Item = { type: TagProps['type']; label: string }
const items = ref<Array<Item>>([
{ type: '', label: 'Tag 1' },
{ type: 'primary', label: 'Tag 1' },
{ type: 'success', label: 'Tag 2' },
{ type: 'info', label: 'Tag 3' },
{ type: 'danger', label: 'Tag 4' },
{ type: 'warning', label: 'Tag 5' },
{ type: 'warning', label: 'Tag 4' },
{ type: 'danger', label: 'Tag 5' },
])
</script>

View File

@@ -4,14 +4,15 @@
<el-divider />
<el-space>
<el-button ref="ref1">upload</el-button>
<el-button ref="ref2" type="primary">save</el-button>
<el-button ref="ref1">Upload</el-button>
<el-button ref="ref2" type="primary">Save</el-button>
<el-button ref="ref3" :icon="MoreFilled" />
</el-space>
<el-tour v-model="open">
<el-tour-step :target="ref1?.$el" title="Upload File">
<img
style="width: 240px"
src="https://element-plus.org/images/element-plus-logo.svg"
alt="tour.png"
/>

View File

@@ -4,8 +4,8 @@
<el-divider />
<el-space>
<el-button ref="ref1">upload</el-button>
<el-button ref="ref2" type="primary">save</el-button>
<el-button ref="ref1">Upload</el-button>
<el-button ref="ref2" type="primary">Save</el-button>
<el-button ref="ref3" :icon="MoreFilled" />
</el-space>

View File

@@ -4,8 +4,8 @@
<el-divider />
<el-space>
<el-button ref="ref1">upload</el-button>
<el-button ref="ref2" type="primary">save</el-button>
<el-button ref="ref1">Upload</el-button>
<el-button ref="ref2" type="primary">Save</el-button>
<el-button ref="ref3" :icon="MoreFilled" />
</el-space>

View File

@@ -4,8 +4,8 @@
<el-divider />
<el-space>
<el-button ref="ref1">upload</el-button>
<el-button ref="ref2" type="primary">save</el-button>
<el-button ref="ref1">Upload</el-button>
<el-button ref="ref2" type="primary">Save</el-button>
<el-button ref="ref3" :icon="MoreFilled" />
</el-space>

View File

@@ -4,8 +4,8 @@
<el-divider />
<el-space>
<el-button id="btn1">upload</el-button>
<el-button id="btn2" type="primary">save</el-button>
<el-button id="btn1">Upload</el-button>
<el-button id="btn2" type="primary">Save</el-button>
<el-button ref="btnRef" :icon="MoreFilled" />
</el-space>

View File

@@ -12,6 +12,14 @@ export const checkTagProps = buildProps({
type: Boolean,
default: false,
},
/**
* @description type of Tag
*/
type: {
type: String,
values: ['primary', 'success', 'info', 'warning', 'danger'],
default: 'primary',
},
} as const)
export type CheckTagProps = ExtractPropTypes<typeof checkTagProps>

View File

@@ -17,7 +17,11 @@ const props = defineProps(checkTagProps)
const emit = defineEmits(checkTagEmits)
const ns = useNamespace('check-tag')
const containerKls = computed(() => [ns.b(), ns.is('checked', props.checked)])
const containerKls = computed(() => [
ns.b(),
ns.is('checked', props.checked),
ns.m(props.type || 'primary'),
])
const handleChange = () => {
const checked = !props.checked

View File

@@ -15,17 +15,14 @@ export const dialogContentProps = buildProps({
closeIcon: {
type: iconPropType,
},
/**
* @deprecated will be removed in version 2.4.0, please use class
*/
customClass: {
type: String,
default: '',
},
/**
* @description enable dragging feature for Dialog
*/
draggable: Boolean,
/**
* @description draggable Dialog can overflow the viewport
*/
overflow: Boolean,
/**
* @description whether the Dialog takes up full screen
*/

View File

@@ -1,6 +1,9 @@
<template>
<div :ref="composedDialogRef" :class="dialogKls" :style="style" tabindex="-1">
<header ref="headerRef" :class="ns.e('header')">
<header
ref="headerRef"
:class="[ns.e('header'), { 'show-close': showClose }]"
>
<slot name="header">
<span role="heading" :aria-level="ariaLevel" :class="ns.e('title')">
{{ title }}
@@ -52,11 +55,11 @@ const dialogKls = computed(() => [
ns.is('draggable', props.draggable),
ns.is('align-center', props.alignCenter),
{ [ns.m('center')]: props.center },
props.customClass,
])
const composedDialogRef = composeRefs(focusTrapRef, dialogRef)
const draggable = computed(() => props.draggable)
useDraggable(dialogRef, headerRef, draggable)
const overflow = computed(() => props.overflow)
useDraggable(dialogRef, headerRef, draggable, overflow)
</script>

View File

@@ -41,11 +41,11 @@
v-if="rendered"
ref="dialogContentRef"
v-bind="$attrs"
:custom-class="customClass"
:center="center"
:align-center="alignCenter"
:close-icon="closeIcon"
:draggable="draggable"
:overflow="overflow"
:fullscreen="fullscreen"
:show-close="showClose"
:title="title"
@@ -104,18 +104,6 @@ useDeprecated(
computed(() => !!slots.title)
)
useDeprecated(
{
scope: 'el-dialog',
from: 'custom-class',
replacement: 'class',
version: '2.3.0',
ref: 'https://element-plus.org/en-US/component/dialog.html#attributes',
type: 'Attribute',
},
computed(() => !!props.customClass)
)
const ns = useNamespace('dialog')
const dialogRef = ref<HTMLElement>()
const headerRef = ref<HTMLElement>()

View File

@@ -229,23 +229,6 @@ describe('Drawer', () => {
expect(wrapper.find('.el-drawer__close-btn').exists()).toBe(false)
})
test('should have custom classes when custom classes were given', async () => {
const classes = 'some-custom-class'
const wrapper = _mount(
`
<el-drawer :title='title' v-model='visible' ref='drawer' custom-class='${classes}'>
<span>${content}</span>
</el-drawer>
`,
() => ({
title,
visible: true,
})
)
expect(wrapper.find(`.${classes}`).exists()).toBe(true)
})
test('drawer header should have slot props', async () => {
const wrapper = _mount(
`

View File

@@ -18,6 +18,9 @@
:trapped="visible"
:focus-trap-el="drawerRef"
:focus-start-el="focusStartRef"
@focus-after-trapped="onOpenAutoFocus"
@focus-after-released="onCloseAutoFocus"
@focusout-prevented="onFocusoutPrevented"
@release-requested="onCloseRequested"
>
<div
@@ -27,7 +30,7 @@
:aria-labelledby="!title ? titleId : undefined"
:aria-describedby="bodyId"
v-bind="$attrs"
:class="[ns.b(), direction, visible && 'open', customClass]"
:class="[ns.b(), direction, visible && 'open']"
:style="
isHorizontal ? 'width: ' + drawerSize : 'height: ' + drawerSize
"
@@ -112,17 +115,6 @@ useDeprecated(
},
computed(() => !!slots.title)
)
useDeprecated(
{
scope: 'el-drawer',
from: 'custom-class',
replacement: 'class',
version: '2.3.0',
ref: 'https://element-plus.org/en-US/component/drawer.html#attributes',
type: 'Attribute',
},
computed(() => !!props.customClass)
)
const drawerRef = ref<HTMLElement>()
const focusStartRef = ref<HTMLElement>()
@@ -137,6 +129,9 @@ const {
titleId,
bodyId,
onModalClick,
onOpenAutoFocus,
onCloseAutoFocus,
onFocusoutPrevented,
onCloseRequested,
handleClose,
} = useDialog(props, drawerRef)

View File

@@ -255,6 +255,13 @@ export default defineComponent({
}
}
const calcMenuItemWidth = (menuItem: HTMLElement) => {
const computedStyle = getComputedStyle(menuItem)
const marginLeft = Number.parseInt(computedStyle.marginLeft, 10)
const marginRight = Number.parseInt(computedStyle.marginRight, 10)
return menuItem.offsetWidth + marginLeft + marginRight || 0
}
const calcSliceIndex = () => {
if (!menu.value) return -1
const items = Array.from(menu.value?.childNodes ?? []).filter(
@@ -264,19 +271,14 @@ export default defineComponent({
(item.nodeName !== '#text' || item.nodeValue)
) as HTMLElement[]
const moreItemWidth = 64
const paddingLeft = Number.parseInt(
getComputedStyle(menu.value!).paddingLeft,
10
)
const paddingRight = Number.parseInt(
getComputedStyle(menu.value!).paddingRight,
10
)
const computedMenuStyle = getComputedStyle(menu.value!)
const paddingLeft = Number.parseInt(computedMenuStyle.paddingLeft, 10)
const paddingRight = Number.parseInt(computedMenuStyle.paddingRight, 10)
const menuWidth = menu.value!.clientWidth - paddingLeft - paddingRight
let calcWidth = 0
let sliceIndex = 0
items.forEach((item, index) => {
calcWidth += item.offsetWidth || 0
calcWidth += calcMenuItemWidth(item)
if (calcWidth <= menuWidth - moreItemWidth) {
sliceIndex = index + 1
}

View File

@@ -38,7 +38,7 @@
<div
v-if="title !== null && title !== undefined"
ref="headerRef"
:class="ns.e('header')"
:class="[ns.e('header'), { 'show-close': showClose }]"
>
<div :class="ns.e('title')">
<el-icon
@@ -178,7 +178,7 @@ import { ElIcon } from '@element-plus/components/icon'
import ElFocusTrap from '@element-plus/components/focus-trap'
import { useGlobalComponentSettings } from '@element-plus/components/config-provider'
import type { ComponentPublicInstance, DefineComponent, PropType } from 'vue'
import type { ComponentPublicInstance, PropType } from 'vue'
import type { ComponentSize } from '@element-plus/constants'
import type {
Action,
@@ -231,6 +231,7 @@ export default defineComponent({
},
center: Boolean,
draggable: Boolean,
overflow: Boolean,
roundButton: {
default: false,
type: Boolean,
@@ -365,7 +366,8 @@ export default defineComponent({
)
const draggable = computed(() => props.draggable)
useDraggable(rootRef, headerRef, draggable)
const overflow = computed(() => props.overflow)
useDraggable(rootRef, headerRef, draggable, overflow)
onMounted(async () => {
await nextTick()
@@ -499,5 +501,5 @@ export default defineComponent({
t,
}
},
}) as DefineComponent
})
</script>

View File

@@ -102,6 +102,9 @@ export interface ElMessageBoxOptions {
/** Whether MessageBox can be drag */
draggable?: boolean
/** Draggable MessageBox can overflow the viewport */
overflow?: boolean
/** Content of the MessageBox */
message?: string | VNode | (() => VNode)

View File

@@ -358,8 +358,7 @@ const useSelect = (props: ISelectV2Props, emit) => {
// methods
const toggleMenu = () => {
if (selectDisabled.value) return
if (props.filterable && props.remote && isFunction(props.remoteMethod))
return
if (states.menuVisibleOnFocus) {
// controlled by automaticDropdown
states.menuVisibleOnFocus = false

View File

@@ -1,5 +1,5 @@
// @ts-nocheck
import { markRaw, nextTick } from 'vue'
import { defineComponent, markRaw, nextTick } from 'vue'
import { mount } from '@vue/test-utils'
import { afterEach, describe, expect, it, test, vi } from 'vitest'
import { EVENT_CODE } from '@element-plus/constants'
@@ -515,6 +515,34 @@ describe('Select', () => {
expect(wrapper.find(`.${PLACEHOLDER_CLASS_NAME}`).text()).toBe('Option D')
})
test('set default value to object with value-key', async () => {
wrapper = _mount(
`
<el-select v-model="value" value-key="id">
<el-option
v-for="item in options"
:key="item.id"
:label="item.label"
:value="item"
/>
</el-select>
`,
() => ({
options: [
{ id: 1, label: 'Option A', desc: 'Option A - 230506' },
{ id: 2, label: 'Option B', desc: 'Option B - 230506' },
{ id: 3, label: 'Option C', desc: 'Option C - 230506' },
{ id: 4, label: 'Option A', desc: 'Option A - 230507' },
],
value: { id: 3 },
})
)
await nextTick()
const options = getOptions()
expect(wrapper.find(`.${PLACEHOLDER_CLASS_NAME}`).text()).toBe('Option C')
expect(Array.from(options[2].classList)).toContain('is-selected')
})
test('sync set value and options', async () => {
wrapper = _mount(
`
@@ -1850,6 +1878,69 @@ describe('Select', () => {
expect(vm.value).toBe('Shanghai')
})
test('el-option-group should visible when el-option in a component', async () => {
const Options = defineComponent({
components: {
'el-option': Option,
},
props: {
options: {
type: Array,
default: () => [],
},
},
template: `
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
`,
})
wrapper = mount({
template: `
<el-select v-model="value">
<el-option-group
v-for="group in options"
:key="group.label"
:label="group.label"
>
<Options :options="group.options" />
</el-option-group>
</el-select>
`,
components: {
'el-select': Select,
'el-option-group': Group,
Options,
},
data() {
return {
value: '',
options: [
{
label: 'Popular cities',
options: [
{
value: 'Shanghai',
label: 'Shanghai',
},
{
value: 'Beijing',
label: 'Beijing',
},
],
},
],
}
},
})
expect(wrapper.findComponent(Group).vm.visible).toBe(true)
})
test('tag of disabled option is not closable', async () => {
wrapper = _mount(
`

View File

@@ -71,6 +71,8 @@ export default defineComponent({
children.push(child.component.proxy)
} else if (child.children?.length) {
children.push(...flattedChildren(child))
} else if (child.component?.subTree) {
children.push(...flattedChildren(child.component.subTree))
}
})
}

View File

@@ -11,10 +11,10 @@ export function useOption(props, states) {
// computed
const itemSelected = computed(() => {
if (!select.props.multiple) {
return isEqual(props.value, select.props.modelValue)
} else {
if (select.props.multiple) {
return contains(select.props.modelValue as unknown[], props.value)
} else {
return contains([select.props.modelValue] as unknown[], props.value)
}
})

View File

@@ -687,8 +687,7 @@ export const useSelect = (props: ISelectProps, emit) => {
const toggleMenu = () => {
if (selectDisabled.value) return
if (props.filterable && props.remote && isFunction(props.remoteMethod))
return
if (states.menuVisibleOnFocus) {
// controlled by automaticDropdown
states.menuVisibleOnFocus = false

View File

@@ -362,7 +362,7 @@ export function createTablePopper(
removePopper?.()
},
})
vm.appContext = table.appContext
vm.appContext = { ...table.appContext, ...table }
const container = document.createElement('div')
render(vm, container)
vm.component!.exposed!.onOpen()

View File

@@ -151,6 +151,18 @@ const Tabs = defineComponent({
computed(() => !!props.activeName)
)
useDeprecated(
{
from: '"addIcon"',
replacement: '"add-icon"',
scope: 'ElTabs',
version: '2.6.0',
ref: 'https://element-plus.org/en-US/component/tabs.html#slots',
type: 'Slot',
},
computed(() => !!slots.addIcon)
)
watch(
() => props.activeName,
(modelValue) => setCurrentName(modelValue)
@@ -178,7 +190,8 @@ const Tabs = defineComponent({
})
return () => {
const addSlot = slots.addIcon
const addSlot = slots['add-icon'] || slots['addIcon']
const isCamelCase = addSlot && slots['addIcon']
const newButton =
props.editable || props.addable ? (
<span
@@ -190,7 +203,7 @@ const Tabs = defineComponent({
}}
>
{addSlot ? (
renderSlot(slots, 'addIcon')
renderSlot(slots, isCamelCase ? 'addIcon' : 'add-icon')
) : (
<ElIcon class={ns.is('icon-plus')}>
<Plus />

View File

@@ -10,8 +10,8 @@ export const tagProps = buildProps({
*/
type: {
type: String,
values: ['success', 'info', 'warning', 'danger', ''],
default: '',
values: ['primary', 'success', 'info', 'warning', 'danger'],
default: 'primary',
},
/**
* @description whether Tag can be removed
@@ -28,17 +28,13 @@ export const tagProps = buildProps({
/**
* @description background color of the Tag
*/
color: {
type: String,
default: '',
},
color: String,
/**
* @description size of Tag
*/
size: {
type: String,
values: componentSizes,
default: '',
},
/**
* @description theme of Tag

View File

@@ -50,7 +50,7 @@ const containerKls = computed(() => {
return [
ns.b(),
ns.is('closable', closable),
ns.m(type),
ns.m(type || 'primary'),
ns.m(tagSize.value),
ns.m(effect),
ns.is('hit', hit),

View File

@@ -163,4 +163,31 @@ describe('Tour.vue', () => {
expect(style.transform).toBe('translate3d(-50%, -50%, 0)')
expect(style.maxWidth).toBe('100vw')
})
test('button props', () => {
mount({
setup() {
return () => (
<Tour modelValue={true} current={1}>
<TourStep title="first" description="cover description." />
<TourStep
title="second"
description="cover description."
prevButtonProps={{
children: '上一步',
class: 'prev-btn',
}}
nextButtonProps={{
children: '下一步',
class: 'next-btn',
}}
/>
</Tour>
)
},
})
expect(document.querySelector('.prev-btn span')?.innerHTML).toBe('上一步')
expect(document.querySelector('.next-btn span')?.innerHTML).toBe('下一步')
})
})

View File

@@ -1,7 +1,5 @@
import {
camelize,
computed,
isVNode,
onBeforeUnmount,
onMounted,
ref,
@@ -19,7 +17,6 @@ import {
shift,
} from '@floating-ui/dom'
import {
hasOwn,
isArray,
isClient,
isFunction,
@@ -30,11 +27,9 @@ import {
import type {
CSSProperties,
Component,
ComputedRef,
InjectionKey,
Ref,
SetupContext,
VNode,
} from 'vue'
import type { UseNamespaceReturn } from '@element-plus/hooks'
import type { PosInfo, TourGap, TourMask } from './types'
@@ -45,6 +40,7 @@ import type {
Strategy,
VirtualElement,
} from '@floating-ui/dom'
import type { TourStepProps } from './step'
export const useTarget = (
target: Ref<
@@ -150,8 +146,9 @@ export const useTarget = (
}
export interface TourContext {
currentStep: Ref<TourStepProps | undefined>
current: Ref<number>
total: ComputedRef<number>
total: Ref<number>
showClose: Ref<boolean>
closeIcon: Ref<string | Component>
mergedType: Ref<'default' | 'primary' | undefined>
@@ -173,51 +170,6 @@ function isInViewPort(element: HTMLElement) {
return top >= 0 && left >= 0 && right <= viewWidth && bottom <= viewHeight
}
const isSameProps = (a: Record<string, any>, b: Record<string, any>) => {
if (Object.keys(a).length !== Object.keys(b).length) return false
for (const key in a) {
if (a[key] !== b[key]) {
return false
}
}
return true
}
export function isSameSteps(a: any[], b: any[]) {
if (a.length !== b.length) return false
for (const [index] of a.entries()) {
if (isSameProps(a[index], b[index])) {
return false
}
}
return true
}
export const getNormalizedProps = (node: VNode, booleanKeys: string[]) => {
if (!isVNode(node)) {
return {}
}
const raw = node.props || {}
const type = (node.type as any)?.props || {}
const props: Record<string, any> = {}
Object.keys(type).forEach((key) => {
if (hasOwn(type[key], 'default')) {
props[key] = type[key].default
}
})
Object.keys(raw).forEach((key) => {
const cameKey = camelize(key)
props[cameKey] = raw[key]
if (booleanKeys.includes(cameKey) && props[cameKey] === '') {
props[cameKey] = true
}
})
return props
}
export const useFloating = (
referenceRef: Ref<HTMLElement | VirtualElement | null>,
contentRef: Ref<HTMLElement | null>,

View File

@@ -25,7 +25,7 @@ export const tourStepProps = buildProps({
*/
showClose: {
type: Boolean,
default: true,
default: undefined,
},
/**
* @description custom close icon, default is Close
@@ -38,7 +38,7 @@ export const tourStepProps = buildProps({
*/
showArrow: {
type: Boolean,
default: true,
default: undefined,
},
/**
* @description position of the guide card relative to the target element
@@ -49,6 +49,7 @@ export const tourStepProps = buildProps({
*/
mask: {
type: definePropType<TourMask>([Boolean, Object]),
default: undefined,
},
/**
* @description custom style for content
@@ -73,6 +74,7 @@ export const tourStepProps = buildProps({
*/
scrollIntoViewOptions: {
type: definePropType<boolean | ScrollIntoViewOptions>([Boolean, Object]),
default: undefined,
},
/**
* @description type, affects the background color and text color

View File

@@ -10,7 +10,7 @@
<component :is="mergedCloseIcon" />
</el-icon>
</button>
<header :class="ns.e('header')">
<header :class="[ns.e('header'), { 'show-close': showClose }]">
<slot name="header">
<span role="heading" :class="ns.e('title')">
{{ title }}
@@ -43,7 +43,7 @@
v-if="current > 0"
size="small"
:type="mergedType"
v-bind="prevButtonProps"
v-bind="filterButtonProps(prevButtonProps)"
@click="onPrev"
>
{{ prevButtonProps?.children ?? t('el.tour.previous') }}
@@ -52,13 +52,12 @@
v-if="current <= total - 1"
size="small"
:type="mergedType === 'primary' ? 'default' : 'primary'"
v-bind="nextButtonProps"
v-bind="filterButtonProps(nextButtonProps)"
@click="onNext"
>
{{
nextButtonProps?.children ?? current === total - 1
? t('el.tour.finish')
: t('el.tour.next')
nextButtonProps?.children ??
(current === total - 1 ? t('el.tour.finish') : t('el.tour.next'))
}}
</el-button>
</div>
@@ -66,7 +65,8 @@
</template>
<script lang="ts" setup>
import { computed, inject } from 'vue'
import { computed, inject, watch } from 'vue'
import { omit } from 'lodash-es'
import { ElButton } from '@element-plus/components/button'
import { ElIcon } from '@element-plus/components/icon'
import { CloseComponents } from '@element-plus/utils'
@@ -74,6 +74,8 @@ import { useLocale } from '@element-plus/hooks'
import { tourStepEmits, tourStepProps } from './step'
import { tourKey } from './helper'
import type { TourBtnProps } from './types'
defineOptions({
name: 'ElTourStep',
})
@@ -86,6 +88,7 @@ const { Close } = CloseComponents
const { t } = useLocale()
const {
currentStep,
current,
total,
showClose,
@@ -99,11 +102,26 @@ const {
onChange,
} = inject(tourKey)!
watch(
props,
(val) => {
currentStep.value = val
},
{
immediate: true,
}
)
const mergedShowClose = computed(() => props.showClose ?? showClose.value)
const mergedCloseIcon = computed(
() => props.closeIcon ?? closeIcon.value ?? Close
)
const filterButtonProps = (btnProps?: TourBtnProps) => {
if (!btnProps) return
return omit(btnProps, ['children', 'onClick'])
}
const onPrev = () => {
current.value -= 1
if (props.prevButtonProps?.onClick) {

View File

@@ -1,6 +1,5 @@
import { defineComponent } from 'vue'
import { flattedChildren, isArray } from '@element-plus/utils'
import { getNormalizedProps, isSameSteps } from './helper'
import type { FlattenVNodes } from '@element-plus/utils'
import type { Component, VNode } from 'vue'
@@ -12,14 +11,14 @@ export default defineComponent({
default: 0,
},
},
emits: ['update-steps'],
emits: ['update-total'],
setup(props, { slots, emit }) {
let cachedSteps: any[] = []
let cacheTotal = 0
return () => {
const children = slots.default?.()!
const filteredSteps: any[] = []
const result: VNode[] = []
let total = 0
function filterSteps(children?: FlattenVNodes) {
if (!isArray(children)) return
@@ -27,9 +26,8 @@ export default defineComponent({
const name = ((item?.type || {}) as Component)?.name
if (name === 'ElTourStep') {
const booleanKeys = ['showArrow', 'mask', 'scrollIntoViewOptions']
filteredSteps.push(getNormalizedProps(item, booleanKeys))
result.push(item)
total += 1
}
})
}
@@ -38,9 +36,9 @@ export default defineComponent({
filterSteps(flattedChildren(children![0]?.children))
}
if (!isSameSteps(filteredSteps, cachedSteps)) {
cachedSteps = filteredSteps
emit('update-steps', filteredSteps)
if (cacheTotal !== total) {
cacheTotal = total
emit('update-total', total)
}
if (result.length) {

View File

@@ -19,7 +19,7 @@
:style="mergedContentStyle"
@close="onEscClose"
>
<el-tour-steps :current="current" @update-steps="onUpdateSteps">
<el-tour-steps :current="current" @update-total="onUpdateTotal">
<slot />
</el-tour-steps>
</el-tour-content>
@@ -39,7 +39,7 @@ import ElTourContent from './content.vue'
import ElTourSteps from './steps'
import { tourEmits, tourProps } from './tour'
import { tourKey, useTarget } from './helper'
import type { UsedTourStepProps } from './types'
import type { TourStepProps } from './step'
defineOptions({
name: 'ElTour',
@@ -49,15 +49,14 @@ const props = defineProps(tourProps)
const emit = defineEmits(tourEmits)
const ns = useNamespace('tour')
const steps = ref<UsedTourStepProps[]>([])
const total = ref(0)
const currentStep = ref<TourStepProps>()
const current = useVModel(props, 'current', emit, {
passive: true,
})
const total = computed(() => steps.value.length)
const currentStep = computed(() => steps.value[current.value] || {})
const currentTarget = computed(() => currentStep.value.target)
const currentTarget = computed(() => currentStep.value?.target)
const kls = computed(() => [
ns.b(),
@@ -65,14 +64,14 @@ const kls = computed(() => [
])
const mergedPlacement = computed(
() => currentStep.value.placement || props.placement
() => currentStep.value?.placement || props.placement
)
const mergedContentStyle = computed(
() => currentStep.value.contentStyle ?? props.contentStyle
() => currentStep.value?.contentStyle ?? props.contentStyle
)
const mergedMask = computed(() => currentStep.value.mask ?? props.mask)
const mergedMask = computed(() => currentStep.value?.mask ?? props.mask)
const mergedShowMask = computed(() => !!mergedMask.value && props.modelValue)
const mergedMaskStyle = computed(() =>
isBoolean(mergedMask.value) ? undefined : mergedMask.value
@@ -80,13 +79,13 @@ const mergedMaskStyle = computed(() =>
const mergedShowArrow = computed(
() =>
!!currentTarget.value && (currentStep.value.showArrow ?? props.showArrow)
!!currentTarget.value && (currentStep.value?.showArrow ?? props.showArrow)
)
const mergedScrollIntoViewOptions = computed(
() => currentStep.value.scrollIntoViewOptions ?? props.scrollIntoViewOptions
() => currentStep.value?.scrollIntoViewOptions ?? props.scrollIntoViewOptions
)
const mergedType = computed(() => currentStep.value.type ?? props.type)
const mergedType = computed(() => currentStep.value?.type ?? props.type)
const { nextZIndex } = useZIndex()
const nowZIndex = nextZIndex()
@@ -109,10 +108,6 @@ watch(
}
)
const onUpdateSteps = (v: any) => {
steps.value = v
}
const onEscClose = () => {
if (props.closeOnPressEscape) {
emit('update:modelValue', false)
@@ -120,9 +115,14 @@ const onEscClose = () => {
}
}
const onUpdateTotal = (val: number) => {
total.value = val
}
const slots = useSlots()
provide(tourKey, {
currentStep,
current,
total,
showClose: toRef(props, 'showClose'),

View File

@@ -1,5 +1,5 @@
import type { CSSProperties, VNode } from 'vue'
import type { Placement } from '@floating-ui/dom'
import type { ButtonProps } from '@element-plus/components/button'
export type TourMask =
| boolean
@@ -13,12 +13,11 @@ export interface TourGap {
radius?: number
}
export interface TourBtnProps {
children: VNode | string
onClick: () => void
className?: string
style?: CSSProperties
}
export type TourBtnProps = {
children?: VNode | string
onClick?: () => void
} & Partial<ButtonProps> &
Record<string, any>
export interface PosInfo {
left: number
@@ -27,13 +26,3 @@ export interface PosInfo {
width: number
radius: number
}
export interface UsedTourStepProps {
target?: string | HTMLElement | (() => HTMLElement | null) | null
showArrow?: boolean
placement?: Placement
contentStyle?: CSSProperties
mask?: TourMask
type?: 'default' | 'primary'
scrollIntoViewOptions?: boolean | ScrollIntoViewOptions
}

View File

@@ -536,6 +536,100 @@ describe('Tree.vue', () => {
expect(treeVm.getCheckedNodes().length).toEqual(0)
})
test('setCheckedNodes with disabled node', async () => {
const nodes = [
{
id: 1,
label: 'Level one 1',
children: [
{
id: 4,
label: 'Level two 1-1',
children: [
{
id: 9,
label: 'Level three 1-1-1',
},
{
id: 10,
label: 'Level three 1-1-2',
},
],
},
],
},
{
id: 2,
label: 'Level one 2',
children: [
{
id: 5,
disabled: true,
label: 'Level two 2-1',
},
{
id: 6,
label: 'Level two 2-2',
},
],
},
{
id: 3,
label: 'Level one 3',
children: [
{
id: 7,
label: 'Level two 3-1',
},
{
id: 8,
label: 'Level two 3-2',
},
],
},
]
const wrapper = mount(
Object.assign({
components: {
'el-tree': Tree,
},
template: `
<el-tree ref="tree" :data="data" :props="defaultProps" default-expand-all show-checkbox node-key="id"></el-tree>
`,
data() {
return {
data: nodes,
defaultProps: {
children: 'children',
label: 'label',
},
}
},
})
)
const treeWrapper = wrapper.findComponent(Tree)
const treeVm = treeWrapper.vm as InstanceType<typeof Tree>
expect(treeVm.getCheckedNodes().length).toEqual(0)
function flattenTree(data) {
const result = []
function flatten(node) {
result.push(node)
if (node?.children?.length) {
node.children.forEach(flatten)
}
}
data.forEach(flatten)
return result
}
const list = flattenTree(nodes).filter((item) => !item.disabled)
treeVm.setCheckedNodes(list)
expect(treeVm.getCheckedNodes().length).toEqual(8)
})
test('setCheckedKeys', async () => {
const { wrapper } = getTreeVm(
`:props="defaultProps" show-checkbox node-key="id"`

View File

@@ -23,6 +23,12 @@ export const uploadListProps = buildProps({
values: uploadListTypes,
default: 'text',
},
/**
* @description set HTML attribute: crossorigin.
*/
crossorigin: {
type: definePropType<'anonymous' | 'use-credentials' | ''>(String),
},
} as const)
export type UploadListProps = ExtractPropTypes<typeof uploadListProps>

View File

@@ -22,6 +22,7 @@
"
:class="nsUpload.be('list', 'item-thumbnail')"
:src="file.url"
:crossorigin="crossorigin"
alt=""
/>
<div

View File

@@ -253,6 +253,12 @@ export const uploadProps = buildProps({
type: definePropType<UploadHooks['onExceed']>(Function),
default: NOOP,
},
/**
* @description set HTML attribute: crossorigin.
*/
crossorigin: {
type: definePropType<'anonymous' | 'use-credentials' | ''>(String),
},
} as const)
export type UploadProps = ExtractPropTypes<typeof uploadProps>

View File

@@ -5,6 +5,7 @@
:disabled="disabled"
:list-type="listType"
:files="uploadFiles"
:crossorigin="crossorigin"
:handle-preview="onPreview"
@remove="handleRemove"
>
@@ -35,6 +36,7 @@
:disabled="disabled"
:list-type="listType"
:files="uploadFiles"
:crossorigin="crossorigin"
:handle-preview="onPreview"
@remove="handleRemove"
>

View File

@@ -5,7 +5,8 @@ import type { ComputedRef, Ref } from 'vue'
export const useDraggable = (
targetRef: Ref<HTMLElement | undefined>,
dragRef: Ref<HTMLElement | undefined>,
draggable: ComputedRef<boolean>
draggable: ComputedRef<boolean>,
overflow?: ComputedRef<boolean>
) => {
let transform = {
offsetX: 0,
@@ -32,14 +33,13 @@ export const useDraggable = (
const maxTop = clientHeight - targetTop - targetHeight + offsetY
const onMousemove = (e: MouseEvent) => {
const moveX = Math.min(
Math.max(offsetX + e.clientX - downX, minLeft),
maxLeft
)
const moveY = Math.min(
Math.max(offsetY + e.clientY - downY, minTop),
maxTop
)
let moveX = offsetX + e.clientX - downX
let moveY = offsetY + e.clientY - downY
if (!overflow?.value) {
moveX = Math.min(Math.max(moveX, minLeft), maxLeft)
moveY = Math.min(Math.max(moveY, minTop), maxTop)
}
transform = {
offsetX: moveX,

View File

@@ -18,10 +18,15 @@
}
@include when(checked) {
background-color: getCssVar('color', 'primary', 'light-8');
color: getCssVar('color', 'primary');
&:hover {
background-color: getCssVar('color', 'primary', 'light-7');
@each $type in $types {
&.#{bem('check-tag', '', $type)} {
background-color: getCssVar('color', $type, 'light-8');
color: getCssVar('color', $type);
&:hover {
background-color: getCssVar('color', $type, 'light-7');
}
}
}
}
}

View File

@@ -516,11 +516,13 @@ $messagebox: map.merge(
'title-color': getCssVar('text-color-primary'),
'width': 420px,
'border-radius': 4px,
'box-shadow': getCssVar('box-shadow'),
'font-size': getCssVar('font-size-large'),
'content-font-size': getCssVar('font-size-base'),
'content-color': getCssVar('text-color-regular'),
'error-font-size': 12px,
'padding-primary': 15px,
'padding-primary': 12px,
'font-line-height': getCssVar('font-line-height-primary'),
),
$messagebox
);
@@ -786,7 +788,7 @@ $dialog: map.merge(
'title-font-size': getCssVar('font-size-large'),
'content-font-size': 14px,
'font-line-height': getCssVar('font-line-height-primary'),
'padding-primary': 20px,
'padding-primary': 16px,
'border-radius': getCssVar('border-radius-small'),
),
$dialog
@@ -798,16 +800,16 @@ $tour: () !default;
$tour: map.merge(
(
'width': 520px,
'padding-primary': 16px,
'padding-primary': 12px,
'font-line-height': getCssVar('font-line-height-primary'),
'title-font-size': 16px,
'title-text-color': getCssVar('text-color-primary'),
'title-font-weight': 400,
'close-color': getCssVar('color-info'),
'font-size': 14px,
'color': getCssVar('text-color-primary'),
'bg-color': getCssVar('bg-color'),
'border-radius': 4px,
'border-color': getCssVar('border-color-lighter'),
),
$tour
);

View File

@@ -15,7 +15,9 @@
border-radius: getCssVar('dialog', 'border-radius');
box-shadow: getCssVar('dialog', 'box-shadow');
box-sizing: border-box;
padding: getCssVar('dialog', 'padding-primary');
width: var(#{getCssVarName('dialog-width')}, 50%);
overflow-wrap: break-word;
&:focus {
outline: none !important;
@@ -52,18 +54,23 @@
}
@include e(header) {
padding: getCssVar('dialog', 'padding-primary');
padding-bottom: 10px;
margin-right: 16px;
padding-bottom: getCssVar('dialog', 'padding-primary');
&.show-close {
padding-right: calc(getCssVar('dialog', 'padding-primary') + var(
#{getCssVarName('message-close-size')},
map.get($message, 'close-size')
));
}
}
@include e(headerbtn) {
position: absolute;
top: 6px;
top: 0;
right: 0;
padding: 0;
width: 54px;
height: 54px;
width: 48px;
height: 48px;
background: transparent;
border: none;
outline: none;
@@ -93,15 +100,12 @@
}
@include e(body) {
padding: calc(#{getCssVar('dialog-padding-primary')} + 10px)
getCssVar('dialog-padding-primary');
color: getCssVar('text-color', 'regular');
font-size: getCssVar('dialog-content-font-size');
}
@include e(footer) {
padding: getCssVar('dialog-padding-primary');
padding-top: 10px;
padding-top: getCssVar('dialog', 'padding-primary');
text-align: right;
box-sizing: border-box;
}
@@ -112,7 +116,6 @@
@include e(body) {
text-align: initial;
padding: 25px calc(#{getCssVar('dialog-padding-primary')} + 5px) 30px;
}
@include e(footer) {

View File

@@ -11,20 +11,21 @@
@include b(message-box) {
display: inline-block;
position: relative;
max-width: getCssVar('messagebox-width');
width: 100%;
padding-bottom: 10px;
padding: getCssVar('messagebox-padding-primary');
vertical-align: middle;
background-color: getCssVar('bg-color');
border-radius: getCssVar('messagebox-border-radius');
border: 1px solid getCssVar('border-color-lighter');
font-size: getCssVar('messagebox-font-size');
box-shadow: getCssVar('box-shadow-light');
box-shadow: getCssVar('messagebox-box-shadow');
text-align: left;
overflow: hidden;
backface-visibility: hidden;
// To avoid small screen overflowing, see #11919
box-sizing: border-box;
overflow-wrap: break-word;
&:focus {
outline: none !important;
@@ -59,24 +60,29 @@
}
@include e(header) {
position: relative;
padding: getCssVar('messagebox-padding-primary');
padding-bottom: 10px;
padding-bottom: getCssVar('messagebox-padding-primary');
&.show-close {
padding-right: calc(getCssVar('messagebox-padding-primary') + var(
#{getCssVarName('message-close-size')},
map.get($message, 'close-size')
));
}
}
@include e(title) {
padding-left: 0;
margin-bottom: 0;
font-size: getCssVar('messagebox-font-size');
line-height: 1;
line-height: getCssVar('messagebox-font-line-height');
color: getCssVar('messagebox-title-color');
}
@include e(headerbtn) {
position: absolute;
top: getCssVar('messagebox-padding-primary');
right: getCssVar('messagebox-padding-primary');
top: 0;
right: 0;
padding: 0;
width: 40px;
height: 40px;
border: none;
outline: none;
background: transparent;
@@ -100,17 +106,18 @@
}
@include e(content) {
padding: 10px getCssVar('messagebox-padding-primary');
color: getCssVar('messagebox-content-color');
font-size: getCssVar('messagebox-content-font-size');
}
@include e(container) {
position: relative;
display: flex;
align-items: center;
gap: 12px;
}
@include e(input) {
padding-top: 15px;
padding-top: 12px;
& div.invalid > input {
border-color: getCssVar('color-error');
@@ -122,26 +129,7 @@
}
@include e(status) {
position: absolute;
top: 50%;
transform: translateY(-50%);
font-size: 24px !important;
&::before {
// 防止图标切割
padding-left: 1px;
}
&.#{$namespace}-icon {
// 防止 el-icon 的position样式覆盖
position: absolute;
}
+ .#{$namespace}-message-box__message {
padding-left: 36px;
padding-right: 12px;
overflow-wrap: break-word;
}
font-size: 24px;
@each $type in (success, info, warning, error) {
&.#{$namespace}-message-box-icon--#{$type} {
@@ -156,67 +144,43 @@
& p {
margin: 0;
line-height: 24px;
line-height: getCssVar('messagebox-font-line-height');
}
}
@include e(errormsg) {
color: getCssVar('color-error');
font-size: getCssVar('messagebox-error-font-size');
min-height: 18px;
margin-top: 2px;
line-height: getCssVar('messagebox-font-line-height');
}
@include e(btns) {
padding: 5px 15px 0;
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
align-items: center;
& button:nth-child(2) {
margin-left: 10px;
}
}
@include e(btns-reverse) {
flex-direction: row-reverse;
padding-top: getCssVar('messagebox-padding-primary');
}
// centerAlign 布局
@include m(center) {
@include e(title) {
position: relative;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
}
@include e(status) {
position: relative;
top: auto;
padding-right: 5px;
text-align: center;
transform: translateY(-1px);
}
@include e(message) {
margin-left: 0;
font-size: inherit;
}
@include e(btns) {
justify-content: center;
}
@include e(content) {
$padding-horizontal: calc(
#{getCssVar('messagebox', 'padding-primary')} + 12px
);
padding-left: $padding-horizontal;
padding-right: $padding-horizontal;
text-align: center;
@include e(container) {
justify-content: center;
}
}
}

View File

@@ -63,7 +63,6 @@ $tag-icon-span-gap: map.merge(
@include b(tag) {
@include genTheme('light-9', 'light-8', '');
@include css-var-from-global(('tag', 'text-color'), ('color', 'primary'));
@each $type in $types {
&.#{bem('tag', '', $type)} {
@include css-var-from-global(('tag', 'text-color'), ('color', $type));

View File

@@ -12,13 +12,13 @@
}
@include e(content) {
padding: 0;
border-radius: getCssVar('tour-border-radius');
border: 1px solid getCssVar('tour-border-color');
width: var(#{getCssVarName('tour-width')});
padding: getCssVar('tour-padding-primary');
background: getCssVar('tour-bg-color');
box-shadow: getCssVar('box-shadow-light');
box-sizing: border-box;
overflow-wrap: break-word;
$content-selector: &;
@@ -32,7 +32,6 @@
@include e(arrow) {
position: absolute;
background: getCssVar('tour-bg-color');
border: 1px solid getCssVar('tour-border-color');
width: 10px;
height: 10px;
pointer-events: none;
@@ -63,11 +62,11 @@
@include e(closebtn) {
position: absolute;
top: 6px;
top: 0;
right: 0;
padding: 0;
width: 44px;
height: 44px;
width: 40px;
height: 40px;
background: transparent;
border: none;
outline: none;
@@ -78,7 +77,7 @@
);
.#{$namespace}-tour__close {
color: getCssVar('tour-title-text-color');
color: getCssVar('tour-close-color');
font-size: inherit;
}
@@ -91,9 +90,14 @@
}
@include e(header) {
padding: getCssVar('tour', 'padding-primary');
padding-bottom: 10px;
margin-right: 16px;
padding-bottom: getCssVar('tour-padding-primary');
&.show-close {
padding-right: calc(getCssVar('tour-padding-primary') + var(
#{getCssVarName('message-close-size')},
map.get($message, 'close-size')
));
}
}
@include e(title) {
@@ -104,7 +108,6 @@
}
@include e(body) {
padding: 0 getCssVar('tour-padding-primary');
color: getCssVar('tour-text-color');
font-size: getCssVar('tour-font-size');
img,
@@ -114,8 +117,7 @@
}
@include e(footer) {
padding: getCssVar('tour-padding-primary');
padding-top: 10px;
padding-top: getCssVar('tour-padding-primary');
box-sizing: border-box;
display: flex;
justify-content: space-between;
@@ -144,6 +146,7 @@
@include set-css-var-value('tour-title-text-color', #fff);
@include set-css-var-value('tour-text-color', #fff);
@include set-css-var-value('tour-bg-color', getCssVar('color', 'primary'));
@include set-css-var-value('tour-close-color', #fff);
.#{$namespace}-tour__closebtn {
&:focus,

View File

@@ -1,4 +1,6 @@
<template>
<!-- If you want to apply a custom namespace, uncomment the following and apply the css configuration in `vite.config.ts`. -->
<!-- <el-config-provider namespace="ep"> -->
<div class="play-container">
<el-icon color="#409EFC">
<el-icon-check />
@@ -9,6 +11,7 @@
<el-button>test</el-button>
<div v-loading="true" />
</div>
<!-- </el-config-provider> -->
</template>
<script setup lang="ts">
@@ -21,9 +24,11 @@ body {
width: 100vw;
height: 100vh;
margin: 0;
#play {
height: 100%;
width: 100%;
.play-container {
height: 100%;
width: 100%;

3
play/styles/custom.scss Normal file
View File

@@ -0,0 +1,3 @@
@forward '@element-plus/theme-chalk/src/mixins/config.scss' with (
$namespace: 'ep'
);

View File

@@ -40,6 +40,13 @@ export default defineConfig(async ({ mode }) => {
).map((dep) => dep.replace(/\.js$/, ''))
return {
// css: {
// preprocessorOptions: {
// scss: {
// additionalData: `@use "/styles/custom.scss" as *;`,
// },
// },
// },
resolve: {
alias: [
{