mirror of
https://github.com/element-plus/element-plus.git
synced 2026-03-13 07:51:17 +08:00
@@ -1,4 +1,27 @@
|
||||
## Changelog
|
||||
|
||||
### 2.2.30
|
||||
|
||||
_2023-02-10_
|
||||
|
||||
#### Features
|
||||
|
||||
- Components [statistic] (#11420 by @chenxch)
|
||||
|
||||
#### Bug fixes
|
||||
|
||||
- Components [select] recalculate input height when display change (#11374 by @makedopamine)
|
||||
- Components [input] use v-if throw exception (#11187 by @acyza)
|
||||
- Components [tree] update refNode type (#11421 by @btea)
|
||||
- Components [dropdown] (#11431 by @jw-foss)
|
||||
- Theme-chalk [table] the cell missing padding style (#11456 by @tolking)
|
||||
- Components fix virtual list scroll&wheel handler (#11472 by @Rapliangle)
|
||||
- Components [virtual-list] scroll issue (#11475 by @jw-foss)
|
||||
|
||||
#### Refactors
|
||||
|
||||
- Tokens use Symbol as injection key (#11251 by @Naeemo)
|
||||
- Locale change short months naming (#11326 by @ADISAKBOONMARK)
|
||||
|
||||
### 2.2.29
|
||||
|
||||
|
||||
@@ -222,6 +222,11 @@
|
||||
{
|
||||
"link": "/tree-v2",
|
||||
"text": "Virtualized Tree"
|
||||
},
|
||||
{
|
||||
"link": "/statistic",
|
||||
"text": "Statistic",
|
||||
"promotion": "2.2.30"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -154,18 +154,18 @@ cascader/panel
|
||||
| disabled | whether Cascader is disabled | boolean | — | false |
|
||||
| clearable | whether selected value can be cleared | boolean | — | false |
|
||||
| show-all-levels | whether to display all levels of the selected value in the input | boolean | — | true |
|
||||
| collapse-tags | whether to collapse tags in multiple selection mode | boolean | - | false |
|
||||
| collapse-tags-tooltip | whether show all selected tags when mouse hover text of collapse-tags. To use this, `collapse-tags` must be true | boolean | - | false |
|
||||
| collapse-tags | whether to collapse tags in multiple selection mode | boolean | — | false |
|
||||
| collapse-tags-tooltip | whether show all selected tags when mouse hover text of collapse-tags. To use this, `collapse-tags` must be true | boolean | — | false |
|
||||
| separator | option label separator | string | — | ' / ' |
|
||||
| filterable | whether the options can be searched | boolean | — | — |
|
||||
| filter-method | customize search logic, the first parameter is `node`, the second is `keyword`, and need return a boolean value indicating whether it hits. | function(node, keyword) | - | - |
|
||||
| filter-method | customize search logic, the first parameter is `node`, the second is `keyword`, and need return a boolean value indicating whether it hits. | function(node, keyword) | — | — |
|
||||
| debounce | debounce delay when typing filter keyword, in milliseconds | number | — | 300 |
|
||||
| before-filter | hook function before filtering with the value to be filtered as its parameter. If `false` is returned or a `Promise` is returned and then is rejected, filtering will be aborted | function(value) | — | — |
|
||||
| popper-class | custom class name for Cascader's dropdown | string | — | — |
|
||||
| teleported | whether cascader popup is teleported | boolean | true / false | true |
|
||||
| popper-append-to-body(deprecated) | whether to append the popper menu to body. If the positioning of the popper is wrong, you can try to set this prop to false | boolean | - | true |
|
||||
| teleported | whether cascader popup is teleported | boolean | — | true |
|
||||
| popper-append-to-body(deprecated) | whether to append the popper menu to body. If the positioning of the popper is wrong, you can try to set this prop to false | boolean | — | true |
|
||||
| tag-type | tag type | string | success/info/warning/danger | info |
|
||||
| validate-event | whether to trigger form validation | boolean | - | true |
|
||||
| validate-event | whether to trigger form validation | boolean | — | true |
|
||||
|
||||
## Cascader Events
|
||||
|
||||
@@ -224,11 +224,11 @@ cascader/panel
|
||||
| Attribute | Description | Type | Accepted Values | Default |
|
||||
| ------------- | ---------------------------------------------------------------------------------------------------------- | ----------------------- | --------------- | ---------- |
|
||||
| expandTrigger | trigger mode of expanding options | string | click / hover | 'click' |
|
||||
| multiple | whether multiple selection is enabled | boolean | - | false |
|
||||
| checkStrictly | whether checked state of a node not affects its parent and child nodes | boolean | - | false |
|
||||
| emitPath | when checked nodes change, whether to emit an array of node's path, if false, only emit the value of node. | boolean | - | true |
|
||||
| lazy | whether to dynamic load child nodes, use with `lazyload` attribute | boolean | - | false |
|
||||
| lazyLoad | method for loading child nodes data, only works when `lazy` is true | function(node, resolve) | - | - |
|
||||
| multiple | whether multiple selection is enabled | boolean | — | false |
|
||||
| checkStrictly | whether checked state of a node not affects its parent and child nodes | boolean | — | false |
|
||||
| emitPath | when checked nodes change, whether to emit an array of node's path, if false, only emit the value of node. | boolean | — | true |
|
||||
| lazy | whether to dynamic load child nodes, use with `lazyload` attribute | boolean | — | false |
|
||||
| lazyLoad | method for loading child nodes data, only works when `lazy` is true | function(node, resolve) | — | — |
|
||||
| value | specify which key of node object is used as the node's value | string | — | 'value' |
|
||||
| label | specify which key of node object is used as the node's label | string | — | 'label' |
|
||||
| children | specify which key of node object is used as the node's children | string | — | 'children' |
|
||||
|
||||
@@ -147,7 +147,7 @@ form/accessibility
|
||||
|
||||
| Method | Description | Type |
|
||||
| --------------- | ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `validate` | Validate the whole form. Receives a callback or returns `Promise`. | `(callback?: (isValid: boolean, invalidFields?: ValidateFieldsError) => void) => Promise<void>` |
|
||||
| `validate` | Validate the whole form. Receives a callback or returns `Promise`. | `(callback?: (isValid: boolean, invalidFields?: ValidateFieldsError) => void) => Promise<boolean>` |
|
||||
| `validateField` | Validate specified fields. | `(props?: Arrayable<FormItemProp>, callback?: (isValid: boolean, invalidFields?: ValidateFieldsError) => void) => Promise<void>` |
|
||||
| `resetFields` | Reset specified fields and remove validation result. | `(props?: Arrayable<FormItemProp>) => void` |
|
||||
| `scrollToField` | Scroll to the specified fields. | `(prop: FormItemProp) => void` |
|
||||
|
||||
@@ -238,7 +238,7 @@ table/table-layout
|
||||
| max-height | Table's max-height. The legal value is a number or the height in px. | string / number | — | — |
|
||||
| stripe | whether Table is striped | boolean | — | false |
|
||||
| border | whether Table has vertical border | boolean | — | false |
|
||||
| size | size of Table | string | large / default /small | default |
|
||||
| size | size of Table | string | large / default /small | — |
|
||||
| fit | whether width of column automatically fits its container | boolean | — | true |
|
||||
| show-header | whether Table header is visible | boolean | — | true |
|
||||
| highlight-current-row | whether current row is highlighted | boolean | — | false |
|
||||
|
||||
@@ -228,4 +228,4 @@ We can also use [Nuxt.js](https://v3.nuxtjs.org/):
|
||||
## Let's Get Started
|
||||
|
||||
You can bootstrap your project from now on. For each components usage, please
|
||||
refer to the individual component documentation.
|
||||
refer to [the individual component documentation](https://element-plus.org/en-US/component/button.html).
|
||||
|
||||
@@ -28,15 +28,6 @@
|
||||
</el-icon>
|
||||
</span>
|
||||
</div>
|
||||
<div class="footer-item">
|
||||
<span>two days ago</span>
|
||||
<span class="red">
|
||||
17%
|
||||
<el-icon>
|
||||
<CaretBottom />
|
||||
</el-icon>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
@@ -59,15 +50,6 @@
|
||||
</template>
|
||||
</el-statistic>
|
||||
<div class="statistic-footer">
|
||||
<div class="footer-item">
|
||||
<span>year on year</span>
|
||||
<span class="green">
|
||||
24%
|
||||
<el-icon>
|
||||
<CaretTop />
|
||||
</el-icon>
|
||||
</span>
|
||||
</div>
|
||||
<div class="footer-item">
|
||||
<span>month on month</span>
|
||||
<span class="red">
|
||||
|
||||
@@ -14,17 +14,17 @@
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-countdown format="DD [days] HH [hours] mm:ss" :value="value2">
|
||||
<el-countdown format="DD [days] HH:mm:ss" :value="value2">
|
||||
<template #title>
|
||||
<div style="display: inline-flex; align-items: center">
|
||||
<el-icon style="margin-right: 4px" :size="12">
|
||||
<Calendar />
|
||||
</el-icon>
|
||||
New Year's Day is still to come
|
||||
Still to go until next month
|
||||
</div>
|
||||
</template>
|
||||
</el-countdown>
|
||||
<div class="countdown-footer">2023-01-01 00:00:00</div>
|
||||
<div class="countdown-footer">{{ value2.format('YYYY-MM-DD') }}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
@@ -36,7 +36,7 @@ import { Calendar } from '@element-plus/icons-vue'
|
||||
|
||||
const value = ref(Date.now() + 1000 * 60 * 60 * 7)
|
||||
const value1 = ref(Date.now() + 1000 * 60 * 60 * 24 * 2)
|
||||
const value2 = ref(dayjs('2023-01-01 00:00:00'))
|
||||
const value2 = ref(dayjs().add(1, 'month').startOf('month'))
|
||||
|
||||
function reset() {
|
||||
value1.value = Date.now() + 1000 * 60 * 60 * 24 * 2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { InjectionKey } from 'vue'
|
||||
import type { IDescriptionsInject } from './descriptions.type'
|
||||
|
||||
export const descriptionsKey =
|
||||
'elDescriptions' as any as InjectionKey<IDescriptionsInject>
|
||||
export const descriptionsKey: InjectionKey<IDescriptionsInject> =
|
||||
Symbol('elDescriptions')
|
||||
|
||||
@@ -14,12 +14,14 @@
|
||||
:role="role"
|
||||
@click="(e) => $emit('clickimpl', e)"
|
||||
@focus="handleFocus"
|
||||
@keydown="handleKeydown"
|
||||
@keydown.self="handleKeydown"
|
||||
@mousedown="handleMousedown"
|
||||
@pointermove="(e) => $emit('pointermove', e)"
|
||||
@pointerleave="(e) => $emit('pointerleave', e)"
|
||||
>
|
||||
<el-icon v-if="icon"><component :is="icon" /></el-icon>
|
||||
<el-icon v-if="icon">
|
||||
<component :is="icon" />
|
||||
</el-icon>
|
||||
<slot />
|
||||
</li>
|
||||
</template>
|
||||
|
||||
@@ -62,11 +62,27 @@ export default defineComponent({
|
||||
whenMouse((e) => {
|
||||
if (props.disabled) {
|
||||
onItemLeave(e)
|
||||
} else {
|
||||
onItemEnter(e)
|
||||
if (!e.defaultPrevented) {
|
||||
;(e.currentTarget as HTMLElement)?.focus()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const target = e.currentTarget as HTMLElement
|
||||
/**
|
||||
* This handles the following scenario:
|
||||
* when the item contains a form element such as input element
|
||||
* when the mouse is moving over the element itself which is contained by
|
||||
* the item, the default focusing logic should be prevented so that
|
||||
* it won't cause weird action.
|
||||
*/
|
||||
if (
|
||||
target === document.activeElement ||
|
||||
target.contains(document.activeElement)
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
onItemEnter(e)
|
||||
if (!e.defaultPrevented) {
|
||||
target?.focus()
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
:aria-labelledby="triggerId"
|
||||
@blur="onBlur"
|
||||
@focus="onFocus"
|
||||
@keydown="handleKeydown"
|
||||
@mousedown="onMousedown"
|
||||
@keydown.self="handleKeydown"
|
||||
@mousedown.self="onMousedown"
|
||||
>
|
||||
<slot />
|
||||
</ul>
|
||||
|
||||
@@ -327,17 +327,17 @@ useResizeObserver(textarea, (entries) => {
|
||||
const resizeTextarea = () => {
|
||||
const { type, autosize } = props
|
||||
|
||||
if (!isClient || type !== 'textarea') return
|
||||
if (!isClient || type !== 'textarea' || !textarea.value) return
|
||||
|
||||
if (autosize) {
|
||||
const minRows = isObject(autosize) ? autosize.minRows : undefined
|
||||
const maxRows = isObject(autosize) ? autosize.maxRows : undefined
|
||||
textareaCalcStyle.value = {
|
||||
...calcTextareaHeight(textarea.value!, minRows, maxRows),
|
||||
...calcTextareaHeight(textarea.value, minRows, maxRows),
|
||||
}
|
||||
} else {
|
||||
textareaCalcStyle.value = {
|
||||
minHeight: calcTextareaHeight(textarea.value!).minHeight,
|
||||
minHeight: calcTextareaHeight(textarea.value).minHeight,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,8 @@ export interface SelectV2Context {
|
||||
onKeyboardSelect: () => void
|
||||
}
|
||||
|
||||
export const selectV2InjectionKey =
|
||||
'ElSelectV2Injection' as any as InjectionKey<SelectV2Context>
|
||||
export const selectV2InjectionKey: InjectionKey<SelectV2Context> = Symbol(
|
||||
'ElSelectV2Injection'
|
||||
)
|
||||
export type IOptionProps = ExtractPropTypes<typeof OptionProps>
|
||||
export type ISelectProps = ExtractPropTypes<typeof SelectProps>
|
||||
|
||||
@@ -35,10 +35,10 @@ export interface SelectContext {
|
||||
}
|
||||
|
||||
// For individual build sharing injection key, we had to make `Symbol` to string
|
||||
export const selectGroupKey =
|
||||
'ElSelectGroup' as unknown as InjectionKey<SelectGroupContext>
|
||||
export const selectGroupKey: InjectionKey<SelectGroupContext> =
|
||||
Symbol('ElSelectGroup')
|
||||
|
||||
export const selectKey = 'ElSelect' as unknown as InjectionKey<SelectContext>
|
||||
export const selectKey: InjectionKey<SelectContext> = Symbol('ElSelect')
|
||||
|
||||
export interface SelectOptionProxy {
|
||||
value: string | number | Record<string, string>
|
||||
|
||||
@@ -557,7 +557,7 @@ export const useSelect = (props, states: States, ctx) => {
|
||||
const handleResize = () => {
|
||||
resetInputWidth()
|
||||
tooltipRef.value?.updatePopper?.()
|
||||
if (props.multiple && !props.filterable) resetInputHeight()
|
||||
if (props.multiple) resetInputHeight()
|
||||
}
|
||||
|
||||
const resetInputWidth = () => {
|
||||
|
||||
@@ -358,7 +358,7 @@ describe('Table.vue', () => {
|
||||
const filter = document.body.querySelector('.el-table-filter')
|
||||
|
||||
triggerEvent(filter.querySelector('.el-checkbox'), 'click', true, false)
|
||||
// confrim button
|
||||
// confirm button
|
||||
await doubleWait()
|
||||
triggerEvent(
|
||||
filter.querySelector('.el-table-filter__bottom button'),
|
||||
@@ -386,7 +386,7 @@ describe('Table.vue', () => {
|
||||
const filter = document.body.querySelector('.el-table-filter')
|
||||
|
||||
triggerEvent(filter.querySelector('.el-checkbox'), 'click', true, false)
|
||||
// confrim button
|
||||
// confirm button
|
||||
await doubleWait()
|
||||
triggerEvent(
|
||||
filter.querySelector('.el-table-filter__bottom button'),
|
||||
@@ -1173,7 +1173,7 @@ describe('Table.vue', () => {
|
||||
describe('tree', () => {
|
||||
let wrapper: VueWrapper<ComponentPublicInstance>
|
||||
afterEach(() => wrapper?.unmount())
|
||||
it('render tree structual data', async () => {
|
||||
it('render tree structural data', async () => {
|
||||
wrapper = mount({
|
||||
components: {
|
||||
ElTableColumn,
|
||||
|
||||
@@ -107,18 +107,24 @@ export default class TreeStore {
|
||||
}
|
||||
}
|
||||
|
||||
getNode(data: TreeKey | TreeNodeData): Node {
|
||||
getNode(data: TreeKey | TreeNodeData | Node): Node {
|
||||
if (data instanceof Node) return data
|
||||
const key = isObject(data) ? getNodeKey(this.key, data) : data
|
||||
return this.nodesMap[key] || null
|
||||
}
|
||||
|
||||
insertBefore(data: TreeNodeData, refData: TreeKey | TreeNodeData): void {
|
||||
insertBefore(
|
||||
data: TreeNodeData,
|
||||
refData: TreeKey | TreeNodeData | Node
|
||||
): void {
|
||||
const refNode = this.getNode(refData)
|
||||
refNode.parent.insertBefore({ data }, refNode)
|
||||
}
|
||||
|
||||
insertAfter(data: TreeNodeData, refData: TreeKey | TreeNodeData): void {
|
||||
insertAfter(
|
||||
data: TreeNodeData,
|
||||
refData: TreeKey | TreeNodeData | Node
|
||||
): void {
|
||||
const refNode = this.getNode(refData)
|
||||
refNode.parent.insertAfter({ data }, refNode)
|
||||
}
|
||||
|
||||
@@ -349,14 +349,14 @@ export default defineComponent({
|
||||
|
||||
const insertBefore = (
|
||||
data: TreeNodeData,
|
||||
refNode: TreeKey | TreeNodeData
|
||||
refNode: TreeKey | TreeNodeData | Node
|
||||
) => {
|
||||
store.value.insertBefore(data, refNode)
|
||||
}
|
||||
|
||||
const insertAfter = (
|
||||
data: TreeNodeData,
|
||||
refNode: TreeKey | TreeNodeData
|
||||
refNode: TreeKey | TreeNodeData | Node
|
||||
) => {
|
||||
store.value.insertAfter(data, refNode)
|
||||
}
|
||||
|
||||
@@ -326,11 +326,15 @@ const createGrid = ({
|
||||
{
|
||||
atXStartEdge: computed(() => states.value.scrollLeft <= 0),
|
||||
atXEndEdge: computed(
|
||||
() => states.value.scrollLeft >= estimatedTotalWidth.value
|
||||
() =>
|
||||
states.value.scrollLeft >=
|
||||
estimatedTotalWidth.value - unref(parsedWidth)
|
||||
),
|
||||
atYStartEdge: computed(() => states.value.scrollTop <= 0),
|
||||
atYEndEdge: computed(
|
||||
() => states.value.scrollTop >= estimatedTotalHeight.value
|
||||
() =>
|
||||
states.value.scrollTop >=
|
||||
estimatedTotalHeight.value - unref(parsedHeight)
|
||||
),
|
||||
},
|
||||
(x: number, y: number) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { cAF, isFirefox, rAF } from '@element-plus/utils'
|
||||
import { cAF, rAF } from '@element-plus/utils'
|
||||
|
||||
import type { ComputedRef } from 'vue'
|
||||
|
||||
@@ -21,9 +21,9 @@ export const useGridWheel = (
|
||||
|
||||
const hasReachedEdge = (x: number, y: number) => {
|
||||
const xEdgeReached =
|
||||
(x < 0 && atXStartEdge.value) || (x > 0 && atXEndEdge.value)
|
||||
(x <= 0 && atXStartEdge.value) || (x >= 0 && atXEndEdge.value)
|
||||
const yEdgeReached =
|
||||
(y < 0 && atYStartEdge.value) || (y > 0 && atYEndEdge.value)
|
||||
(y <= 0 && atYStartEdge.value) || (y >= 0 && atYEndEdge.value)
|
||||
return xEdgeReached && yEdgeReached
|
||||
}
|
||||
|
||||
@@ -54,9 +54,7 @@ export const useGridWheel = (
|
||||
xOffset += x
|
||||
yOffset += y
|
||||
|
||||
if (!isFirefox()) {
|
||||
e.preventDefault()
|
||||
}
|
||||
e.preventDefault()
|
||||
|
||||
frameHandle = rAF(() => {
|
||||
onWheelDelta(xOffset, yOffset)
|
||||
|
||||
@@ -45,18 +45,18 @@ export default {
|
||||
sat: 'ส',
|
||||
},
|
||||
months: {
|
||||
jan: 'มกรา',
|
||||
feb: 'กุมภา',
|
||||
mar: 'มีนา',
|
||||
apr: 'เมษา',
|
||||
may: 'พฤษภา',
|
||||
jun: 'มิถุนา',
|
||||
jul: 'กรกฎา',
|
||||
aug: 'สิงหา',
|
||||
sep: 'กันยา',
|
||||
oct: 'ตุลา',
|
||||
nov: 'พฤศจิกา',
|
||||
dec: 'ธันวา',
|
||||
jan: 'ม.ค.',
|
||||
feb: 'ก.พ.',
|
||||
mar: 'มี.ค.',
|
||||
apr: 'เม.ย.',
|
||||
may: 'พ.ค.',
|
||||
jun: 'มิ.ย.',
|
||||
jul: 'ก.ค.',
|
||||
aug: 'ส.ค.',
|
||||
sep: 'ก.ย.',
|
||||
oct: 'ต.ค.',
|
||||
nov: 'พ.ย.',
|
||||
dec: 'ธ.ค.',
|
||||
},
|
||||
},
|
||||
select: {
|
||||
|
||||
@@ -170,6 +170,7 @@
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
line-height: 23px;
|
||||
padding: 0 12px;
|
||||
|
||||
&.#{$namespace}-tooltip {
|
||||
white-space: nowrap;
|
||||
|
||||
Reference in New Issue
Block a user