Files
element-plus/packages/components/tree-select/src/tree.ts
虞金攀 904aa0e21b feat(components): add tree select component (#6843)
* feat(ElTreeSelect): add tree select base component

* refactor(ElTreeSelect): use render function and move select/tree props to them self module

* fix(ElTreeSelect): init value not checked

* fix(ElTreeSelect): `toArray` ignores valid values

* fix(ElTreeSelect): expose not working when defined on mounted

* fix(ElTreeSelect): watch `modelValue` deep

* test(ElTreeSelect): add base unit test

* perf(ElTreeSelect): default slot should be a function

* fix(ElTreeSelect): `onNodeClick` can not call,

* test(ElTreeSelect): update unit test

* fix(ElTreeSelect): `onNodeClick` can not call,

* fix(ElTreeSelect): remove folder node when `checkStrictly` is false

* feat(ElTreeSelect): export `ElTreeSelect`

* fix(ElTreeSelect): `filterMethod` conflicts with `filterNodeMethod`

* docs(ElTreeSelect): add component docs

* fix(ElTreeSelect): fix lint

* docs(ElTreeSelect): fix lazy loading requires non-leaf nodes, and change mock labels

* docs(ElTreeSelect): the link address of the attributes is incorrect

* docs(ElTreeSelect): `dropdown` doesn't need the `-` symbol

* refactor(ElTreeSelect): use alias path and make sure vue is above to components

* refactor(ElTreeSelect): use a unified namespace for styles

* docs(ElTreeSelect): change option labels in default slots

* refactor(ElTreeSelect): import `ElOption` using unified entry and change the way to override the select click event

* style(ElTreeSelect): sort imports

* docs(ElTreeSelect): update the documentation for special codes

* refactor(ElTreeSelect): keep it consistent with the select style

* refactor(ElTreeSelect): use `isFunction` from `@element-plus/utils`

* refactor(ElTreeSelect): use single closing tag when no subset

* docs(ElTreeSelect): set `TreeSelect` promotion as `2.1.8`
2022-04-02 15:15:33 +08:00

137 lines
3.7 KiB
TypeScript

import { computed, nextTick, toRefs, watch } from 'vue'
import { isEqual, pick } from 'lodash-unified'
import { UPDATE_MODEL_EVENT } from '@element-plus/constants'
import { isFunction } from '@element-plus/utils'
import ElTree from '@element-plus/components/tree'
import TreeSelectOption from './tree-select-option'
import type { Ref } from 'vue'
import type ElSelect from '@element-plus/components/select'
import type Node from '@element-plus/components/tree/src/model/node'
import type { TreeNodeData } from '@element-plus/components/tree/src/tree.type'
export const useTree = (
props,
{ attrs, slots, emit },
{
select,
tree,
key,
}: {
select: Ref<InstanceType<typeof ElSelect> | undefined>
tree: Ref<InstanceType<typeof ElTree> | undefined>
key: Ref<string>
}
) => {
watch(
() => props.modelValue,
() => {
if (props.showCheckbox) {
nextTick(() => {
const treeInstance = tree.value
if (
treeInstance &&
!isEqual(
treeInstance.getCheckedKeys(),
toValidArray(props.modelValue)
)
) {
treeInstance.setCheckedKeys(toValidArray(props.modelValue))
}
})
}
},
{
immediate: true,
deep: true,
}
)
const propsMap = computed(() => ({
value: key.value,
...props.props,
}))
const getNodeValByProp = (
prop: 'value' | 'label' | 'children' | 'disabled' | 'isLeaf',
data: TreeNodeData
) => {
const propVal = propsMap.value[prop]
if (isFunction(propVal)) {
return propVal(
data,
tree.value?.getNode(getNodeValByProp('value', data)) as Node
)
} else {
return data[propVal as string]
}
}
return {
...pick(toRefs(props), Object.keys(ElTree.props)),
...attrs,
nodeKey: key,
defaultExpandedKeys: computed(() =>
props.defaultExpandedKeys
? props.defaultExpandedKeys.concat(props.modelValue)
: toValidArray(props.modelValue)
),
renderContent: (h, { node, data, store }) => {
return h(
TreeSelectOption,
{
value: getNodeValByProp('value', data),
label: getNodeValByProp('label', data),
disabled: getNodeValByProp('disabled', data),
},
props.renderContent
? () => props.renderContent(h, { node, data, store })
: slots.default
? () => slots.default({ node, data, store })
: undefined
)
},
filterNodeMethod: (value, data, node) => {
if (props.filterNodeMethod)
return props.filterNodeMethod(value, data, node)
if (!value) return true
return getNodeValByProp('label', data)?.includes(value)
},
onNodeClick: (data, node, e) => {
attrs.onNodeClick?.(data, node, e)
if (props.checkStrictly || node.isLeaf) {
if (!getNodeValByProp('disabled', data)) {
const option = select.value?.options.get(
getNodeValByProp('value', data)
)
select.value?.handleOptionSelect(option, true)
}
} else {
e.ctx.handleExpandIconClick()
}
},
onCheck: (data, params) => {
attrs.onCheck?.(data, params)
// remove folder node when `checkStrictly` is false
const checkedKeys = !props.checkStrictly
? tree.value?.getCheckedKeys(true)
: params.checkedKeys
const value = getNodeValByProp('value', data)
emit(
UPDATE_MODEL_EVENT,
props.multiple
? checkedKeys
: checkedKeys.includes(value)
? value
: undefined
)
},
}
}
function toValidArray(val: any) {
return Array.isArray(val) ? val : val || val === 0 ? [val] : []
}