styles(cascader): [cascader] add cascader types (#1324)

This commit is contained in:
jxhhdx 2024-01-24 12:20:14 +08:00 committed by GitHub
parent 4eeac6a614
commit 076e8369d1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 400 additions and 120 deletions

View File

@ -16,6 +16,14 @@ import debounce from '../common/deps/debounce'
import { isEqual } from '../common/object'
import { addResizeListener } from '../common/deps/resize-event'
import { KEY_CODE, CASCADER } from '../common'
import type {
ICascaderState,
ICascaderProps,
ICascaderApi,
ICascaderConstants,
ICascadeRenderlessParamUtils
} from '@/types'
import type CascaderPanelNode from '../cascader-panel/node'
const initMigratingProps = () => ({
expandTrigger: { newProp: CASCADER.PropsExpandTri, type: String },
@ -30,7 +38,7 @@ const kebabCase = (str) => {
}
export const getConfig =
({ parent, props }) =>
({ parent, props }: { parent: ICascadeRenderlessParamUtils['parent']; props: ICascaderProps }) =>
() => {
const config = props.props || {}
const { $attrs } = parent
@ -53,7 +61,7 @@ export const getConfig =
}
export const computClearVisible =
({ props, state }) =>
({ props, state }: { props: ICascaderProps; state: ICascaderState }) =>
() => {
if (!props.clearable || state.isDisabled || state.filtering || !state.inputHover) {
return false
@ -63,7 +71,7 @@ export const computClearVisible =
}
export const computePresentContent =
({ api, state }) =>
({ api, state }: { api: ICascaderApi; state: ICascaderState }) =>
() => {
if (state.config.multiple) {
api.computePresentTags()
@ -74,16 +82,16 @@ export const computePresentContent =
}
export const watchValue =
({ api, state }) =>
({ api, state }: { api: ICascaderApi; state: ICascaderState }) =>
(value) => {
if (!isEqual(value, state.checkedValue)) {
if (!isEqual(value, state.checkedValue as any)) {
state.checkedValue = value
setTimeout(api.computePresentContent)
}
}
export const watchCheckedValue =
({ api, emit, state }) =>
({ api, emit, state }: { api: ICascaderApi; emit: ICascadeRenderlessParamUtils['emit']; state: ICascaderState }) =>
(value) => {
const { checkStrictly, multiple } = state.config
@ -131,7 +139,19 @@ export const isEmpty = (val) => {
}
export const selfMounted =
({ api, parent, props, refs, state }) =>
({
api,
parent,
props,
refs,
state
}: {
api: ICascaderApi
parent: ICascadeRenderlessParamUtils['parent']
props: ICascaderProps
refs: ICascadeRenderlessParamUtils['refs']
state: ICascaderState
}) =>
() => {
const { input } = refs
const inputSizeMap = { medium: 36, small: 32, mini: 28 }
@ -167,7 +187,19 @@ export const selfMounted =
}
export const toggleDropDownVisible =
({ emit, nextTick, refs, state, updatePopper }) =>
({
emit,
nextTick: _,
refs,
state,
updatePopper
}: {
emit: ICascadeRenderlessParamUtils['emit']
nextTick: ICascadeRenderlessParamUtils['nextTick']
refs: ICascadeRenderlessParamUtils['refs']
state: ICascaderState
updatePopper: (popperElm?: HTMLElement | undefined) => void
}) =>
(visible) => {
if (state.isDisabled) {
return
@ -264,7 +296,7 @@ export const handleInput =
}
}
export const handleClear = (state) => () => {
export const handleClear = (state: ICascaderState) => (_event) => {
state.presentText = ''
state.panel.clearCheckedNodes()
}
@ -285,10 +317,10 @@ export const handleExpandChange =
}
export const focusFirstNode =
({ refs, state }) =>
({ refs, state }: { refs: ICascadeRenderlessParamUtils['refs']; state: ICascaderState }) =>
() => {
const { popper, suggestionPanel } = refs
let firstNode = null
let firstNode: HTMLLIElement | null = null
if (state.filtering && suggestionPanel) {
firstNode = suggestionPanel.$el.querySelector(CASCADER.SugItem)
@ -304,7 +336,7 @@ export const focusFirstNode =
}
export const computePresentText =
({ props, state }) =>
({ props, state }: { props: ICascaderProps; state: ICascaderState }) =>
() => {
if (!isEmpty(state.checkedValue)) {
const node = state.panel.getNodeByValue(state.checkedValue)
@ -320,16 +352,16 @@ export const computePresentText =
}
export const computePresentTags =
({ api, props, state }) =>
({ api, props, state }: { api: ICascaderApi; props: ICascaderProps; state: ICascaderState }) =>
() => {
const checkedNodes = api.getCheckedNodes(state.leafOnly)
const tags = []
const tags: Partial<CascaderPanelNode & { key: string | number; text: string; closable: boolean }>[] = []
const genTag = (node) => {
const text = node.getText(props.showAllLevels, props.separator)
const closable = !state.isDisabled && !node.isDisabled
return { node, key: node.uid, text, hitState: false, closable }
return { node, key: node.uid, text, hitState: false, closable } as Partial<CascaderPanelNode>
}
if (checkedNodes.length) {
@ -355,7 +387,15 @@ export const computePresentTags =
}
export const calcCollapseTags =
({ state, refs, nextTick }) =>
({
state,
refs,
nextTick
}: {
state: ICascaderState
refs: ICascadeRenderlessParamUtils['refs']
nextTick: ICascadeRenderlessParamUtils['nextTick']
}) =>
() => {
nextTick(() => {
const content = refs['tags-content']
@ -378,8 +418,10 @@ export const calcCollapseTags =
const collapseTagWidth =
tagsLength && parseFloat(collapseTagContentWidth) + parseFloat(marginRight) + parseFloat(marginLeft)
const tags = Array.from(content.querySelectorAll('.tiny-tag'))
let { total, dom, idx } = { total: collapseTagWidth, dom: null, idx: 0 }
const tags: HTMLDivElement[] = Array.from(content.querySelectorAll('.tiny-tag'))
let { total, dom, idx } = { total: collapseTagWidth as number, dom: null as HTMLDivElement | null, idx: 0 }
// eslint-disable-next-line array-callback-return
tags.some((tag, index) => {
if (tag !== tagsLength) {
const { width: tagContentWidth, marginRight, marginLeft } = tag && window.getComputedStyle(tag)
@ -410,7 +452,7 @@ export const calcCollapseTags =
}
export const watchInputHover =
({ refs }) =>
({ refs }: { refs: ICascadeRenderlessParamUtils['refs'] }) =>
(newVal) => {
const [inputHover, dropDownVisible] = newVal
if (!inputHover && !dropDownVisible) {
@ -420,7 +462,7 @@ export const watchInputHover =
}
export const handleMouseenter =
({ refs, state }) =>
({ refs, state }: { refs: ICascadeRenderlessParamUtils['refs']; state: ICascaderState }) =>
($event) => {
const dom = $event.target
const textNode = dom && dom.querySelector('span')
@ -439,19 +481,30 @@ export const handleMouseenter =
}
export const handleMouseleave =
({ state }) =>
({ state }: { state: ICascaderState }) =>
() =>
(state.tooltipVisible = false)
export const getSuggestions =
({ nextTick, props, state, updatePopper }) =>
({
nextTick,
props,
state,
updatePopper
}: {
nextTick: ICascadeRenderlessParamUtils['nextTick']
props: ICascaderProps
state: ICascaderState
updatePopper
}) =>
() => {
let filterMethod = null
type IFilterMethod = (node: any, keyword: any) => number
let filterMethod: IFilterMethod | null = null
if (!(props.filterMethod && typeof props.filterMethod === 'function')) {
filterMethod = (node, keyword) => ~node.text.indexOf(keyword)
} else {
filterMethod = props.filterMethod
filterMethod = props.filterMethod as IFilterMethod
}
const suggestions = state.panel.getFlattedNodes(state.leafOnly).filter((node) => {
@ -459,9 +512,9 @@ export const getSuggestions =
return false
}
node.text = node.getText(props.showAllLevels, props.separator) || ''
;(node as typeof node & { text: string }).text = node.getText(props.showAllLevels, props.separator) || ''
return filterMethod(node, state.inputValue)
return filterMethod?.(node, state.inputValue)
})
if (state.multiple) {
@ -470,7 +523,7 @@ export const getSuggestions =
})
} else {
suggestions.forEach((node) => {
node.checked = isEqual(state.checkedValue, node.getValueByOption())
node.checked = isEqual(state.checkedValue as object, node.getValueByOption() as object)
})
}
@ -481,7 +534,7 @@ export const getSuggestions =
}
export const handleSuggestionKeyDown =
({ api }) =>
({ api }: { api: ICascaderApi }) =>
(event) => {
event = event || window.event
@ -505,7 +558,7 @@ export const handleSuggestionKeyDown =
}
export const handleDelete =
({ api, state }) =>
({ api, state }: { api: ICascaderApi; state: ICascaderState }) =>
() => {
const lastIndex = state.presentTags.length - 1
const lastTag = state.presentTags[lastIndex]
@ -526,8 +579,8 @@ export const handleDelete =
}
export const handleSuggestionClick =
({ api, state }) =>
(index) => {
({ api, state }: { api: ICascaderApi; state: ICascaderState }) =>
(index: number) => {
const targetNode = state.suggestions[index]
if (state.multiple) {
@ -542,10 +595,10 @@ export const handleSuggestionClick =
}
export const deleteTag =
({ emit, state }) =>
({ emit, state }: { emit: ICascadeRenderlessParamUtils['emit']; state: ICascaderState }) =>
(index) => {
const val = state.checkedValue[index]
state.checkedValue = state.checkedValue.filter((n, i) => i !== index)
state.checkedValue = (state.checkedValue as Array<any>).filter((n, i) => i !== index)
emit('remove-tag', val)
}
@ -562,16 +615,17 @@ export const updateStyle =
}
const tags = $el.querySelector(CASCADER.TagClass)
let suggestionPanelEl = null
let suggestionPanelEl: HTMLDivElement | null = null
// eslint-disable-next-line no-cond-assign
if (suggestionPanel && (suggestionPanelEl = suggestionPanel.$el)) {
const suggestionList = suggestionPanelEl.querySelector(CASCADER.ListClass)
suggestionList.style.minWidth = inputInner.offsetWidth + 'px'
const suggestionList: HTMLDivElement | null = suggestionPanelEl?.querySelector(CASCADER.ListClass)
suggestionList && (suggestionList.style.minWidth = inputInner.offsetWidth + 'px')
}
if (tags) {
const offsetHeight = tags.offsetHeight
let height = 0
let height: number | string = 0
if (props.hoverExpand && !state.inputHover && !state.dropDownVisible) {
height = state.inputInitialHeight + 'px'
} else {
@ -590,24 +644,35 @@ export const updateStyle =
}
}
export const getCheckedNodes = (state) => (leafOnly) => state.panel.getCheckedNodes(leafOnly, state.checkedValue)
export const getCheckedNodes = (state: ICascaderState) => (leafOnly: boolean) =>
state.panel.getCheckedNodes(leafOnly, state.checkedValue)
export const setInputHeightToTag =
({ nextTick, parent, state }) =>
({
nextTick,
parent,
state
}: {
nextTick: ICascadeRenderlessParamUtils['nextTick']
parent: ICascadeRenderlessParamUtils['parent']
state: ICascaderState
}) =>
() => {
nextTick(() => {
const parentEl = parent.$el
const height = parentEl.querySelector(CASCADER.TagClass).offsetHeight + 6 + 'px'
const parentEl = parent.$el as HTMLDivElement
const height = (parentEl?.querySelector(CASCADER.TagClass) as HTMLDivElement)?.offsetHeight + 6 + 'px'
if (!state.isDisplayOnly) {
parentEl.querySelector(CASCADER.InputClass).style.height = height
const inputDom = parentEl?.querySelector(CASCADER.InputClass) as HTMLDivElement | null
inputDom && (inputDom.style.height = height)
} else {
parentEl.querySelector(CASCADER.InputClass).style.height = 'auto'
const inputDom = parentEl?.querySelector(CASCADER.InputClass) as HTMLDivElement | null
inputDom && (inputDom.style.height = 'auto')
}
})
}
export const presentTextHandler = ({ state, value }) => {
export const presentTextHandler = ({ state, value }: { state: ICascaderState; value: string | null }) => {
if (state.inputValue) {
if (state.inputValue !== value) {
state.inputValue = value
@ -620,7 +685,7 @@ export const presentTextHandler = ({ state, value }) => {
}
export const handleBeforeUpdate =
({ props, api, state }) =>
({ props, api, state }: { props: ICascaderProps; api: ICascaderApi; state: ICascaderState }) =>
() => {
if ((!isEmpty(props.modelValue) && !props.filterable) || (props.hoverExpand && state.multiple)) {
api.computePresentContent()
@ -628,9 +693,19 @@ export const handleBeforeUpdate =
}
export const showPlaceholder =
({ props, state, t, constants }) =>
({
props,
state,
t,
constants
}: {
props: ICascaderProps
state: ICascaderState
t: ICascadeRenderlessParamUtils['t']
constants: ICascaderConstants
}) =>
() => {
let placeholder = null
let placeholder: string | null = null
placeholder =
(state.multiple && state.presentTags.length) || state.present ? '' : props.placeholder || t(constants.placeholder)

View File

@ -47,6 +47,14 @@ import {
import { removeResizeListener } from '../common/deps/resize-event'
import userPopper from '../common/deps/vue-popper'
import { DATEPICKER } from '../common'
import type {
ICascaderProps,
ICascaderApi,
ICascaderState,
ICascaderConstants,
ISharedRenderlessFunctionParams,
ICascadeRenderlessParamUtils
} from '@/types'
export const api = [
'state',
@ -68,7 +76,27 @@ export const api = [
'handleMouseleave'
]
const initState = ({ reactive, props, computed, parent, api, t, constants, refs, inject }) => {
const initState = ({
reactive,
props,
computed,
parent,
api,
t,
constants,
refs,
inject
}: {
reactive: ISharedRenderlessFunctionParams<null>['reactive']
props: ICascaderProps
computed: ISharedRenderlessFunctionParams<null>['computed']
parent: ICascadeRenderlessParamUtils['parent']
api: ICascaderApi
t: ICascadeRenderlessParamUtils['t']
constants: ICascaderConstants
refs: ICascadeRenderlessParamUtils['refs']
inject: ISharedRenderlessFunctionParams<null>['inject']
}) => {
const state = reactive({
showAutoWidth: inject('showAutoWidth', null),
/** popper 元素是否显示。 它是通过v-show 绑定到页面上造成隐藏时popperJs并没有destory,有一定的性能影响 */
@ -104,10 +132,34 @@ const initState = ({ reactive, props, computed, parent, api, t, constants, refs,
tooltipContent: ''
})
return state
return state as ICascaderState
}
const initApi = ({ api, state, constants, dispatch, emit, refs, props, updatePopper, nextTick, parent, t }) => {
const initApi = ({
api,
state,
constants,
dispatch,
emit,
refs,
props,
updatePopper,
nextTick,
parent,
t
}: {
api: ICascaderApi
state: ICascaderState
constants: ICascaderConstants
dispatch: ICascadeRenderlessParamUtils['dispatch']
emit: ICascadeRenderlessParamUtils['emit']
refs: ICascadeRenderlessParamUtils['refs']
props: ICascaderProps
updatePopper: (popperElm?: HTMLElement | undefined) => void
nextTick: ICascadeRenderlessParamUtils['nextTick']
parent: ICascadeRenderlessParamUtils['parent']
t: ICascadeRenderlessParamUtils['t']
}) => {
Object.assign(api, {
state,
handleFocus: handleFocus(emit),
@ -144,7 +196,21 @@ const initApi = ({ api, state, constants, dispatch, emit, refs, props, updatePop
})
}
const initWatch = ({ watch, state, api, props, nextTick, updatePopper }) => {
const initWatch = ({
watch,
state,
api,
props,
nextTick,
updatePopper
}: {
watch: ISharedRenderlessFunctionParams<null>['watch']
state: ICascaderState
api: ICascaderApi
props: ICascaderProps
nextTick: ICascadeRenderlessParamUtils['nextTick']
updatePopper: (popperElm?: HTMLElement | undefined) => void
}) => {
watch(() => state.disabled, api.computePresentContent)
watch(() => props.modelValue, api.watchValue)
@ -190,9 +256,20 @@ const initWatch = ({ watch, state, api, props, nextTick, updatePopper }) => {
}
export const renderless = (
props,
{ computed, onMounted, onBeforeUnmount, onDeactivated, onUpdated, onBeforeUpdate, reactive, toRefs, watch, inject },
{ t, refs, emit, nextTick, constants, parent, slots, dispatch }
props: ICascaderProps,
{
computed,
onMounted,
onBeforeUnmount,
onDeactivated,
onUpdated,
onBeforeUpdate,
reactive,
toRefs,
watch,
inject
}: ISharedRenderlessFunctionParams<null>,
{ t, refs, emit, nextTick, constants, parent, slots, dispatch }: ICascadeRenderlessParamUtils
) => {
parent.tinyForm = parent.tinyForm || inject('form', null)
@ -215,21 +292,33 @@ export const renderless = (
nextTick,
onBeforeUnmount,
onDeactivated
} as any)
const api: Partial<ICascaderApi> = {}
const state = initState({ reactive, props, computed, parent, api: api as ICascaderApi, t, constants, refs, inject })
initApi({
api: api as ICascaderApi,
state,
constants,
dispatch,
emit,
refs,
props,
updatePopper,
nextTick,
parent,
t
})
const api = {}
const state = initState({ reactive, props, computed, parent, api, t, constants, refs, inject })
initWatch({ watch, state, api: api as ICascaderApi, props, nextTick, updatePopper })
initApi({ api, state, constants, dispatch, emit, refs, props, updatePopper, nextTick, parent, t })
onBeforeUpdate((api as ICascaderApi).handleBeforeUpdate)
onUpdated((api as ICascaderApi).updateStyle)
onMounted((api as ICascaderApi).selfMounted)
initWatch({ watch, state, api, props, nextTick, updatePopper })
onBeforeUpdate(api.handleBeforeUpdate)
onUpdated(api.updateStyle)
onMounted(api.selfMounted)
parent.$on('handle-clear', (event) => {
api.handleClear(event)
parent.$on('handle-clear', (event: ICascaderState) => {
;(api as ICascaderApi).handleClear(event)
})
onBeforeUnmount(() => removeResizeListener(parent.$el, api.updateStyle))

View File

@ -0,0 +1,114 @@
import type { ISharedRenderlessParamUtils } from './shared.type'
import type { ExtractPropTypes } from 'vue'
import type { cascaderProps } from '@/cascader/src'
import type { ICascaderPanelApi, ICascaderPanelNodePropValue } from '@/types'
import type {
getConfig,
deleteTag,
getCheckedNodes,
updateStyle,
handleSuggestionClick,
handleDelete,
handleSuggestionKeyDown,
getSuggestions,
handleExpandChange,
computePresentText,
focusFirstNode,
handleClear,
handleInput,
handleBlur,
handleFocus,
handleKeyDown,
handleDropdownLeave,
toggleDropDownVisible,
computePresentTags,
selfMounted,
watchCheckedValue,
computClearVisible,
computePresentContent,
watchValue,
setInputHeightToTag,
handleBeforeUpdate,
showPlaceholder,
calcCollapseTags,
watchInputHover,
handleMouseenter,
handleMouseleave
} from '../src/cascader'
import type CascaderPanelNode from '../src/cascader-panel/node'
export interface ICascaderState {
showAutoWidth: boolean
/** popper 元素是否显示。 它是通过v-show 绑定到页面上造成隐藏时popperJs并没有destory,有一定的性能影响 */
dropDownVisible: boolean
checkedValue: ICascaderPanelNodePropValue
inputHover: boolean
inputValue: string | null
presentText: string | null
present: string | null
presentTags: Partial<
CascaderPanelNode & { key: string | number; text: string; closable: boolean; hitState: boolean }
>[]
checkedNodes: (CascaderPanelNode & { text?: string })[]
filtering: boolean
suggestions: CascaderPanelNode[]
inputInitialHeight: number
pressDeleteCount: number
realSize: 'medium' | 'small' | 'mini'
formDisabled: boolean
displayOnly: boolean
disabled: boolean
tagSize: 'mini' | 'small'
isDisabled: boolean
isDisplayOnly: boolean
config: { multiple?: boolean; checkStrictly?: boolean }
multiple: boolean
leafOnly: boolean
readonly: boolean
clearBtnVisible: boolean
panel: Element & ICascaderPanelApi
placeholder: boolean
inputValues: boolean
collapseTagsLength: number
isHidden: boolean
tooltipVisible: boolean
tooltipContent: string
}
export type ICascaderProps = ExtractPropTypes<typeof cascaderProps>
export interface ICascaderApi {
state: ICascaderState
handleFocus: ReturnType<typeof handleFocus>
handleClear: ReturnType<typeof handleClear>
getCheckedNodes: ReturnType<typeof getCheckedNodes>
deleteTag: ReturnType<typeof deleteTag>
handleDropdownLeave: ReturnType<typeof handleDropdownLeave>
focusFirstNode: ReturnType<typeof focusFirstNode>
computClearVisible: ReturnType<typeof computClearVisible>
showPlaceholder: ReturnType<typeof showPlaceholder>
updateStyle: ReturnType<typeof updateStyle>
getSuggestions: ReturnType<typeof getSuggestions>
handleExpandChange: ReturnType<typeof handleExpandChange>
getConfig: ReturnType<typeof getConfig>
setInputHeightToTag: ReturnType<typeof setInputHeightToTag>
handleSuggestionClick: ReturnType<typeof handleSuggestionClick>
handleDelete: ReturnType<typeof handleDelete>
computePresentText: ReturnType<typeof computePresentText>
handleSuggestionKeyDown: ReturnType<typeof handleSuggestionKeyDown>
handleInput: ReturnType<typeof handleInput>
handleKeyDown: ReturnType<typeof handleKeyDown>
watchValue: ReturnType<typeof watchValue>
computePresentTags: ReturnType<typeof computePresentTags>
computePresentContent: ReturnType<typeof computePresentContent>
watchCheckedValue: ReturnType<typeof watchCheckedValue>
toggleDropDownVisible: ReturnType<typeof toggleDropDownVisible>
selfMounted: ReturnType<typeof selfMounted>
handleBeforeUpdate: ReturnType<typeof handleBeforeUpdate>
handleBlur: ReturnType<typeof handleBlur>
calcCollapseTags: ReturnType<typeof calcCollapseTags>
watchInputHover: ReturnType<typeof watchInputHover>
handleMouseenter: ReturnType<typeof handleMouseenter>
handleMouseleave: ReturnType<typeof handleMouseleave>
filterHandler: Function
}
export type ICascaderConstants = ReturnType<typeof cascaderProps._constants.default>
export type ICascadeRenderlessParamUtils = ISharedRenderlessParamUtils<ICascaderConstants>

View File

@ -18,63 +18,65 @@ const $constants = {
EVENT_NAME: { FormBlur: 'form.blur' }
}
export default defineComponent({
name: $prefix + 'Cascader',
props: {
...$props,
_constants: {
type: Object,
default: () => $constants
},
autoSize: Boolean,
beforeFilter: {
type: Function,
default: () => () => {
// do nothing
}
},
clearable: Boolean,
collapseTags: Boolean,
debounce: {
type: Number,
default: 300
},
disabled: Boolean,
filterMethod: Function,
filterable: Boolean,
modelValue: {},
options: Array,
placeholder: {
type: String,
default: ''
},
popperAppendToBody: {
type: Boolean,
default: true
},
popperClass: String,
props: Object,
separator: {
type: String,
default: '/'
},
showAllLevels: {
type: Boolean,
default: true
},
size: String,
shape: String,
label: String,
tip: String,
displayOnly: {
type: Boolean,
default: false
},
hoverExpand: {
type: Boolean,
default: false
export const cascaderProps = {
...$props,
_constants: {
type: Object,
default: () => $constants
},
autoSize: Boolean,
beforeFilter: {
type: Function,
default: () => () => {
// do nothing
}
},
clearable: Boolean,
collapseTags: Boolean,
debounce: {
type: Number,
default: 300
},
disabled: Boolean,
filterMethod: Function,
filterable: Boolean,
modelValue: {},
options: Array,
placeholder: {
type: String,
default: ''
},
popperAppendToBody: {
type: Boolean,
default: true
},
popperClass: String,
props: Object,
separator: {
type: String,
default: '/'
},
showAllLevels: {
type: Boolean,
default: true
},
size: String,
shape: String,
label: String,
tip: String,
displayOnly: {
type: Boolean,
default: false
},
hoverExpand: {
type: Boolean,
default: false
}
}
export default defineComponent({
name: $prefix + 'Cascader',
props: cascaderProps,
setup(props, context) {
return $setup({ props, context, template })
}