mirror of
https://github.com/element-plus/element-plus.git
synced 2026-03-13 07:51:17 +08:00
chore: wip
This commit is contained in:
@@ -54,26 +54,16 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
getCurrentInstance,
|
||||
inject,
|
||||
reactive,
|
||||
toRefs,
|
||||
watch,
|
||||
} from 'vue'
|
||||
import { computed, defineComponent, inject, reactive, toRefs } from 'vue'
|
||||
import ElScrollbar from '@element-plus/components/scrollbar'
|
||||
import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from '@element-plus/constants'
|
||||
import { flattedChildren, isArray, isObject } from '@element-plus/utils'
|
||||
import { isArray } from '@element-plus/utils'
|
||||
import ElOption from './option.vue'
|
||||
import ElSelectMenu from './select-dropdown.vue'
|
||||
import ElOptions from './options'
|
||||
import { selectProps } from './select'
|
||||
import { useFlatSelect } from './useFlatSelect'
|
||||
|
||||
import type { VNode } from 'vue'
|
||||
|
||||
const COMPONENT_NAME = 'ElSelect'
|
||||
export default defineComponent({
|
||||
name: COMPONENT_NAME,
|
||||
@@ -87,22 +77,7 @@ export default defineComponent({
|
||||
props: selectProps,
|
||||
emits: [UPDATE_MODEL_EVENT, CHANGE_EVENT, 'popup-scroll'],
|
||||
|
||||
setup(props, { emit, slots }) {
|
||||
const instance = getCurrentInstance()!
|
||||
instance.appContext.config.warnHandler = (...args) => {
|
||||
// Overrides warnings about slots not being executable outside of a render function.
|
||||
// We call slot below just to simulate data when persist is false, this warning message should be ignored
|
||||
if (
|
||||
!args[0] ||
|
||||
args[0].includes(
|
||||
'Slot "default" invoked outside of the render function'
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(...args)
|
||||
}
|
||||
setup(props, { emit }) {
|
||||
const modelValue = computed(() => {
|
||||
const { modelValue: rawModelValue, multiple } = props
|
||||
const fallback = multiple ? [] : undefined
|
||||
@@ -121,67 +96,6 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const API = inject('flat-select', () => useFlatSelect(_props, emit), true)
|
||||
|
||||
const flatTreeSelectData = (data: any[]) => {
|
||||
return data.reduce((acc, item) => {
|
||||
acc.push(item)
|
||||
if (item.children && item.children.length > 0) {
|
||||
acc.push(...flatTreeSelectData(item.children))
|
||||
}
|
||||
return acc
|
||||
}, [])
|
||||
}
|
||||
|
||||
const manuallyRenderSlots = (vnodes: VNode[] | undefined) => {
|
||||
// After option rendering is completed, the useSelect internal state can collect the value of each option.
|
||||
// If the persistent value is false, option will not be rendered by default, so in this case,
|
||||
// manually render and load option data here.
|
||||
const children = flattedChildren(vnodes || []) as VNode[]
|
||||
children.forEach((item) => {
|
||||
if (
|
||||
isObject(item) &&
|
||||
// @ts-expect-error
|
||||
(item.type.name === 'ElOption' || item.type.name === 'ElTree')
|
||||
) {
|
||||
// @ts-expect-error
|
||||
const _name = item.type.name
|
||||
if (_name === 'ElTree') {
|
||||
// tree-select component is a special case.
|
||||
// So we need to handle it separately.
|
||||
const treeData = item.props?.data || []
|
||||
const flatData = flatTreeSelectData(treeData)
|
||||
flatData.forEach((treeItem: any) => {
|
||||
treeItem.currentLabel =
|
||||
treeItem.label ||
|
||||
(isObject(treeItem.value) ? '' : treeItem.value)
|
||||
API.onOptionCreate(treeItem)
|
||||
})
|
||||
} else if (_name === 'ElOption') {
|
||||
const obj = { ...item.props } as any
|
||||
obj.currentLabel =
|
||||
obj.label || (isObject(obj.value) ? '' : obj.value)
|
||||
API.onOptionCreate(obj)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
watch(
|
||||
() => {
|
||||
const slotsContent = slots.default?.()
|
||||
return slotsContent
|
||||
},
|
||||
(newSlot) => {
|
||||
if (props.persistent) {
|
||||
// If persistent is true, we don't need to manually render slots.
|
||||
return
|
||||
}
|
||||
manuallyRenderSlots(newSlot)
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
...API,
|
||||
modelValue,
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
:effect="effect"
|
||||
pure
|
||||
trigger="click"
|
||||
:transition="`${nsSelect.namespace.value}-zoom-in-top`"
|
||||
:transition="`${nsSelect.namespace}-zoom-in-top`"
|
||||
:stop-popper-mouse-event="false"
|
||||
:gpu-acceleration="false"
|
||||
:persistent="persistent"
|
||||
@@ -250,7 +250,21 @@
|
||||
</template>
|
||||
<template #content>
|
||||
<el-flat-select v-bind="$props">
|
||||
<template v-if="$slots.header">
|
||||
<slot name="header" />
|
||||
</template>
|
||||
<slot />
|
||||
<template v-if="$slots.footer">
|
||||
<slot name="footer" />
|
||||
</template>
|
||||
|
||||
<template v-if="$slots.loading">
|
||||
<slot name="loading" />
|
||||
</template>
|
||||
|
||||
<template v-if="$slots.empty">
|
||||
<slot name="empty" />
|
||||
</template>
|
||||
</el-flat-select>
|
||||
</template>
|
||||
</el-tooltip>
|
||||
@@ -258,7 +272,15 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, reactive, toRefs } from 'vue'
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
getCurrentInstance,
|
||||
reactive,
|
||||
toRefs,
|
||||
useSlots,
|
||||
watch,
|
||||
} from 'vue'
|
||||
import { ClickOutside } from '@element-plus/directives'
|
||||
import ElTooltip from '@element-plus/components/tooltip'
|
||||
import ElScrollbar from '@element-plus/components/scrollbar'
|
||||
@@ -270,6 +292,10 @@ import ElFlatSelect from './flat-select.vue'
|
||||
import { useSelect } from './useSelect'
|
||||
import ElOptions from './options'
|
||||
import { selectProps } from './select'
|
||||
import { flattedChildren, isArray, isObject } from '@element-plus/utils'
|
||||
import { useFlatSelect } from './useFlatSelect'
|
||||
|
||||
import type { VNode } from 'vue'
|
||||
|
||||
const COMPONENT_NAME = 'ElSelect'
|
||||
export default defineComponent({
|
||||
@@ -301,9 +327,86 @@ export default defineComponent({
|
||||
...toRefs(props),
|
||||
})
|
||||
|
||||
const API = useSelect(_props, emit)
|
||||
const FLAT_SELECT_API = reactive(useFlatSelect(props, emit))
|
||||
const API = useSelect(_props, emit, FLAT_SELECT_API)
|
||||
const { calculatorRef, inputStyle } = useCalcInputWidth()
|
||||
|
||||
const instance = getCurrentInstance()!
|
||||
instance.appContext.config.warnHandler = (...args) => {
|
||||
// Overrides warnings about slots not being executable outside of a render function.
|
||||
// We call slot below just to simulate data when persist is false, this warning message should be ignored
|
||||
if (
|
||||
!args[0] ||
|
||||
args[0].includes(
|
||||
'Slot "default" invoked outside of the render function'
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(...args)
|
||||
}
|
||||
const flatTreeSelectData = (data: any[]) => {
|
||||
return data.reduce((acc, item) => {
|
||||
acc.push(item)
|
||||
if (item.children && item.children.length > 0) {
|
||||
acc.push(...flatTreeSelectData(item.children))
|
||||
}
|
||||
return acc
|
||||
}, [])
|
||||
}
|
||||
|
||||
const manuallyRenderSlots = (vnodes: VNode[] | undefined) => {
|
||||
// After option rendering is completed, the useSelect internal state can collect the value of each option.
|
||||
// If the persistent value is false, option will not be rendered by default, so in this case,
|
||||
// manually render and load option data here.
|
||||
const children = flattedChildren(vnodes || []) as VNode[]
|
||||
children.forEach((item) => {
|
||||
if (
|
||||
isObject(item) &&
|
||||
// @ts-expect-error
|
||||
(item.type.name === 'ElOption' || item.type.name === 'ElTree')
|
||||
) {
|
||||
// @ts-expect-error
|
||||
const _name = item.type.name
|
||||
if (_name === 'ElTree') {
|
||||
// tree-select component is a special case.
|
||||
// So we need to handle it separately.
|
||||
const treeData = item.props?.data || []
|
||||
const flatData = flatTreeSelectData(treeData)
|
||||
flatData.forEach((treeItem: any) => {
|
||||
treeItem.currentLabel =
|
||||
treeItem.label ||
|
||||
(isObject(treeItem.value) ? '' : treeItem.value)
|
||||
FLAT_SELECT_API.onOptionCreate(treeItem)
|
||||
})
|
||||
} else if (_name === 'ElOption') {
|
||||
const obj = { ...item.props } as any
|
||||
obj.currentLabel =
|
||||
obj.label || (isObject(obj.value) ? '' : obj.value)
|
||||
FLAT_SELECT_API.onOptionCreate(obj)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
const slots = useSlots()
|
||||
watch(
|
||||
() => {
|
||||
const slotsContent = slots.default?.()
|
||||
return slotsContent
|
||||
},
|
||||
(newSlot) => {
|
||||
if (props.persistent) {
|
||||
// If persistent is true, we don't need to manually render slots.
|
||||
return
|
||||
}
|
||||
manuallyRenderSlots(newSlot)
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
)
|
||||
|
||||
const selectedLabel = computed(() => {
|
||||
if (!props.multiple) {
|
||||
return API.states.selectedLabel
|
||||
@@ -311,8 +414,21 @@ export default defineComponent({
|
||||
return API.states.selected.map((i) => i.currentLabel as string)
|
||||
})
|
||||
|
||||
const modelValue = computed(() => {
|
||||
const { modelValue: rawModelValue, multiple } = props
|
||||
const fallback = multiple ? [] : undefined
|
||||
// When it is array, we check if this is multi-select.
|
||||
// Based on the result we get
|
||||
if (isArray(rawModelValue)) {
|
||||
return multiple ? rawModelValue : fallback
|
||||
}
|
||||
|
||||
return multiple ? fallback : rawModelValue
|
||||
})
|
||||
|
||||
return {
|
||||
...API,
|
||||
modelValue,
|
||||
calculatorRef,
|
||||
inputStyle,
|
||||
selectedLabel,
|
||||
|
||||
@@ -4,7 +4,11 @@ import type {
|
||||
ComputedRef,
|
||||
ExtractPropTypes,
|
||||
Ref,
|
||||
UnwrapNestedRefs,
|
||||
} from 'vue'
|
||||
import type { ScrollbarInstance } from '@element-plus/components/scrollbar'
|
||||
import type { Translator } from '@element-plus/hooks/use-locale'
|
||||
import type { UseNamespaceReturn } from '@element-plus/hooks/use-namespace'
|
||||
import type { SelectProps } from './select'
|
||||
import type { optionProps } from './option'
|
||||
|
||||
@@ -19,6 +23,28 @@ export interface SelectContext extends FlatSelectContext {
|
||||
export interface FlatSelectContext {
|
||||
props: SelectProps
|
||||
states: SelectStates
|
||||
t: Translator
|
||||
contentId: string
|
||||
nsSelect: UnwrapNestedRefs<UseNamespaceReturn>
|
||||
filteredOptionsCount: number
|
||||
scrollToOption: (
|
||||
option:
|
||||
| OptionPublicInstance
|
||||
| OptionPublicInstance[]
|
||||
| SelectStates['selected']
|
||||
) => void
|
||||
hasModelValue: boolean
|
||||
updateOptions: () => void
|
||||
showNewOption: boolean
|
||||
emptyText: string | null
|
||||
selectOption: () => void
|
||||
getValueKey: (
|
||||
item: OptionPublicInstance | SelectStates['selected'][number]
|
||||
) => any
|
||||
popupScroll: (data: { scrollTop: number; scrollLeft: number }) => void
|
||||
scrollbarRef: ScrollbarInstance | undefined
|
||||
menuRef: HTMLElement | undefined
|
||||
handleQueryChange: (val: string) => void
|
||||
optionsArray: OptionPublicInstance[]
|
||||
setSelected(): void
|
||||
onOptionCreate(vm: OptionPublicInstance): void
|
||||
|
||||
@@ -380,20 +380,10 @@ export const useFlatSelect = (props: SelectProps, emit: FlatSelectEmits) => {
|
||||
// DOM ref
|
||||
scrollbarRef,
|
||||
menuRef,
|
||||
props,
|
||||
}
|
||||
|
||||
provide(
|
||||
flatSelectKey,
|
||||
reactive({
|
||||
props,
|
||||
states,
|
||||
optionsArray,
|
||||
setSelected,
|
||||
handleOptionSelect,
|
||||
onOptionCreate,
|
||||
onOptionDestroy,
|
||||
})
|
||||
)
|
||||
provide(flatSelectKey, reactive(FLAT_SELECT_API))
|
||||
|
||||
provide('flat-select', FLAT_SELECT_API)
|
||||
|
||||
|
||||
@@ -44,19 +44,22 @@ import {
|
||||
useFormSize,
|
||||
} from '@element-plus/components/form'
|
||||
import { selectKey } from './token'
|
||||
import { useFlatSelect } from './useFlatSelect'
|
||||
|
||||
import type { TooltipInstance } from '@element-plus/components/tooltip'
|
||||
import type { SelectEmits, SelectProps } from './select'
|
||||
import type {
|
||||
FlatSelectContext,
|
||||
OptionBasic,
|
||||
OptionPublicInstance,
|
||||
OptionValue,
|
||||
SelectStates,
|
||||
} from './type'
|
||||
|
||||
export const useSelect = (props: SelectProps, emit: SelectEmits) => {
|
||||
const FLAT_SELECT_API = useFlatSelect(props, emit)
|
||||
export const useSelect = (
|
||||
props: SelectProps,
|
||||
emit: SelectEmits,
|
||||
FLAT_SELECT_API: FlatSelectContext
|
||||
) => {
|
||||
const {
|
||||
t,
|
||||
contentId,
|
||||
@@ -471,7 +474,7 @@ export const useSelect = (props: SelectProps, emit: SelectEmits) => {
|
||||
scrollIntoView(menu as HTMLElement, target)
|
||||
}
|
||||
}
|
||||
scrollbarRef.value?.handleScroll()
|
||||
FLAT_SELECT_API.scrollbarRef?.handleScroll()
|
||||
}
|
||||
|
||||
const popperRef = computed(() => {
|
||||
@@ -481,7 +484,7 @@ export const useSelect = (props: SelectProps, emit: SelectEmits) => {
|
||||
const handleMenuEnter = () => {
|
||||
states.isBeforeHide = false
|
||||
nextTick(() => {
|
||||
scrollbarRef.value?.update()
|
||||
FLAT_SELECT_API.scrollbarRef?.update()
|
||||
scrollToOption(states.selected)
|
||||
})
|
||||
}
|
||||
@@ -654,14 +657,15 @@ export const useSelect = (props: SelectProps, emit: SelectEmits) => {
|
||||
provide(
|
||||
selectKey,
|
||||
reactive({
|
||||
props,
|
||||
states: FLAT_SELECT_API.states,
|
||||
//props,
|
||||
//states: FLAT_SELECT_API.states,
|
||||
selectRef,
|
||||
optionsArray: FLAT_SELECT_API.optionsArray,
|
||||
setSelected: FLAT_SELECT_API.setSelected,
|
||||
handleOptionSelect: FLAT_SELECT_API.handleOptionSelect,
|
||||
onOptionCreate: FLAT_SELECT_API.onOptionCreate,
|
||||
onOptionDestroy: FLAT_SELECT_API.onOptionDestroy,
|
||||
//optionsArray: FLAT_SELECT_API.optionsArray,
|
||||
//setSelected: FLAT_SELECT_API.setSelected,
|
||||
//handleOptionSelect: FLAT_SELECT_API.handleOptionSelect,
|
||||
//onOptionCreate: FLAT_SELECT_API.onOptionCreate,
|
||||
//onOptionDestroy: FLAT_SELECT_API.onOptionDestroy,
|
||||
...FLAT_SELECT_API,
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user