Skip to content

Commit

Permalink
feat(tree): add show-irrelevant-nodes prop, closes #2764
Browse files Browse the repository at this point in the history
  • Loading branch information
07akioni committed Apr 18, 2022
1 parent 5487d3a commit e0ef701
Show file tree
Hide file tree
Showing 13 changed files with 379 additions and 278 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
- `n-dynamic-input` adds `RTL` support.
- `n-table` adds `RTL` support.
- `n-collapse-transition` adds `RTL` support.
- `n-tree` adds `show-irrelevant-nodes` prop, closes [#2764](https://github.com/TuSimple/naive-ui/issues/2764).

### i18n

Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
- `n-dynamic-input` 添加 `RTL` 支持
- `n-table` 添加 `RTL` 支持
- `n-collapse-transition` 添加 `RTL` 支持
- `n-tree-select` 新增 `show-irrelevant-nodes` 属性,关闭 [#2764](https://github.com/TuSimple/naive-ui/issues/2764)

### i18n

Expand Down
182 changes: 61 additions & 121 deletions src/tree-select/src/TreeSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ import type {
import { treeSelectInjectionKey } from './interface'
import {
treeOption2SelectOption,
filterTree,
treeOption2SelectOptionWithPath
} from './utils'
import style from './styles/index.cssr'
Expand Down Expand Up @@ -209,47 +208,13 @@ export default defineComponent({
.includes(pattern.toLowerCase())
}
})
const filteredTreeInfoRef = computed<{
filteredTree: TreeSelectOption[]
highlightKeySet: Set<Key> | null
expandedKeys: Key[] | undefined
}>(() => {
if (!props.filterable) {
return {
filteredTree: props.options,
highlightKeySet: null,
expandedKeys: undefined
}
}
const { value: pattern } = patternRef
if (!pattern.length || !mergedFilterRef.value) {
return {
filteredTree: props.options,
highlightKeySet: null,
expandedKeys: undefined
}
}
return filterTree(
props.options,
mergedFilterRef.value,
pattern,
props.keyField,
props.childrenField
)
})
// used to resolve selected options
const dataTreeMateRef = computed(() =>
createTreeMate<TreeSelectOption>(
props.options,
createTreeMateOptions(props.keyField, props.childrenField)
)
)
const displayTreeMateRef = computed(() =>
createTreeMate<TreeSelectOption>(
filteredTreeInfoRef.value.filteredTree,
createTreeMateOptions(props.keyField, props.childrenField)
)
)
const { value: initMergedValue } = mergedValueRef
const pendingNodeKeyRef = ref(
props.checkable
Expand All @@ -265,7 +230,7 @@ export default defineComponent({
// function to reuse it.
const uncontrolledExpandedKeysRef = ref(
props.defaultExpandAll
? displayTreeMateRef.value.getNonLeafKeys()
? [] // leave it, n-tree will handle it
: props.defaultExpandedKeys || props.expandedKeys
)
const controlledExpandedKeysRef = toRef(props, 'expandedKeys')
Expand Down Expand Up @@ -614,25 +579,6 @@ export default defineComponent({
if (!mergedShowRef.value) return
void nextTick(syncPosition)
})
let memorizedExpandedKeys: Key[] | undefined
watch(patternRef, (value, oldValue) => {
if (!value.length) {
if (memorizedExpandedKeys !== undefined) {
doUpdateExpandedKeys(
memorizedExpandedKeys,
getOptionsByKeys(memorizedExpandedKeys)
)
}
} else {
if (!oldValue.length) {
memorizedExpandedKeys = mergedExpandedKeysRef.value
}
const { expandedKeys } = filteredTreeInfoRef.value
if (expandedKeys !== undefined) {
doUpdateExpandedKeys(expandedKeys, getOptionsByKeys(expandedKeys))
}
}
})
const themeRef = useTheme(
'TreeSelect',
'-tree-select',
Expand Down Expand Up @@ -683,9 +629,7 @@ export default defineComponent({
adjustedTo: useAdjustedTo(props),
isMounted: useIsMounted(),
focused: focusedRef,
filteredTreeInfo: filteredTreeInfoRef,
dataTreeMate: dataTreeMateRef,
displayTreeMate: displayTreeMateRef,
menuPadding: menuPaddingRef,
mergedPlaceholder: mergedPlaceholderRef,
mergedExpandedKeys: mergedExpandedKeysRef,
Expand All @@ -698,6 +642,7 @@ export default defineComponent({
pattern: patternRef,
pendingNodeKey: pendingNodeKeyRef,
mergedCascade: mergedCascadeRef,
mergedFilter: mergedFilterRef,
doUpdateExpandedKeys,
handleMenuLeave,
handleTriggerClick,
Expand Down Expand Up @@ -787,10 +732,10 @@ export default defineComponent({
if (!this.mergedShow) return null
const {
mergedClsPrefix,
filteredTreeInfo,
checkable,
multiple,
menuProps
menuProps,
options
} = this
this.onRender?.()
return withDirectives(
Expand All @@ -813,68 +758,63 @@ export default defineComponent({
onFocusin={this.handleMenuFocusin}
onFocusout={this.handleMenuFocusout}
>
{filteredTreeInfo.filteredTree.length ? (
<NTree
ref="treeInstRef"
blockLine
animated={false}
data={filteredTreeInfo.filteredTree}
cancelable={multiple}
labelField={this.labelField}
theme={mergedTheme.peers.Tree}
themeOverrides={
mergedTheme.peerOverrides.Tree
}
defaultExpandAll={this.defaultExpandAll}
defaultExpandedKeys={this.defaultExpandedKeys}
expandedKeys={this.mergedExpandedKeys}
checkedKeys={this.treeCheckedKeys}
selectedKeys={this.treeSelectedKeys}
checkable={checkable}
checkStrategy={this.checkStrategy}
cascade={this.mergedCascade}
leafOnly={this.leafOnly}
multiple={this.multiple}
virtualScroll={
this.consistentMenuWidth &&
this.virtualScroll
}
internalTreeSelect
internalDataTreeMate={this.dataTreeMate}
internalDisplayTreeMate={this.displayTreeMate}
internalHighlightKeySet={
filteredTreeInfo.highlightKeySet
}
internalUnifySelectCheck
internalScrollable
internalScrollablePadding={this.menuPadding}
internalFocusable={false}
internalCheckboxFocusable={false}
onLoad={this.onLoad}
onUpdateCheckedKeys={
this.handleUpdateCheckedKeys
}
onUpdateIndeterminateKeys={
this.handleUpdateIndeterminateKeys
}
onUpdateExpandedKeys={
this.doUpdateExpandedKeys
}
/>
) : (
<div
class={`${mergedClsPrefix}-tree-select-menu__empty`}
>
{resolveSlot($slots.empty, () => [
<NEmpty
theme={mergedTheme.peers.Empty}
themeOverrides={
mergedTheme.peerOverrides.Empty
}
/>
])}
</div>
)}
<NTree
ref="treeInstRef"
blockLine
showIrrelevantNodes={false}
animated={false}
pattern={this.pattern}
filter={this.mergedFilter}
data={options}
cancelable={multiple}
labelField={this.labelField}
keyField={this.keyField}
childrenField={this.childrenField}
theme={mergedTheme.peers.Tree}
themeOverrides={mergedTheme.peerOverrides.Tree}
defaultExpandAll={this.defaultExpandAll}
defaultExpandedKeys={this.defaultExpandedKeys}
expandedKeys={this.mergedExpandedKeys}
checkedKeys={this.treeCheckedKeys}
selectedKeys={this.treeSelectedKeys}
checkable={checkable}
checkStrategy={this.checkStrategy}
cascade={this.mergedCascade}
leafOnly={this.leafOnly}
multiple={this.multiple}
virtualScroll={
this.consistentMenuWidth && this.virtualScroll
}
internalTreeSelect
internalDataTreeMate={this.dataTreeMate}
internalUnifySelectCheck
internalScrollable
internalScrollablePadding={this.menuPadding}
internalFocusable={false}
internalCheckboxFocusable={false}
internalRenderEmpty={() => (
<div
class={`${mergedClsPrefix}-tree-select-menu__empty`}
>
{resolveSlot($slots.empty, () => [
<NEmpty
theme={mergedTheme.peers.Empty}
themeOverrides={
mergedTheme.peerOverrides.Empty
}
/>
])}
</div>
)}
onLoad={this.onLoad}
onUpdateCheckedKeys={
this.handleUpdateCheckedKeys
}
onUpdateIndeterminateKeys={
this.handleUpdateIndeterminateKeys
}
onUpdateExpandedKeys={this.doUpdateExpandedKeys}
/>
{resolveWrappedSlot($slots.action, (children) => {
return children ? (
<div
Expand Down
77 changes: 1 addition & 76 deletions src/tree-select/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { SelectBaseOption } from '../../select/src/interface'
import { Key } from '../../tree/src/interface'
import { TreeSelectTmNode, TreeSelectOption } from './interface'
import { TreeSelectTmNode } from './interface'

export function treeOption2SelectOption (
tmNode: TreeSelectTmNode,
Expand All @@ -27,77 +26,3 @@ export function treeOption2SelectOptionWithPath (
label: path.map((v) => (v.rawNode as any)[labelField]).join(separator)
}
}

export function filterTree (
tree: TreeSelectOption[],
filter: (pattern: string, v: TreeSelectOption) => boolean,
pattern: string,
keyField: string,
childrenField: string
): {
filteredTree: TreeSelectOption[]
expandedKeys: Key[]
highlightKeySet: Set<Key>
} {
const visitedTailKeys = new Set<Key>()
const visitedNonTailKeys = new Set<Key>()
const highlightKeySet = new Set<Key>()
const expandedKeys: Key[] = []
const filteredTree: TreeSelectOption[] = []
const path: TreeSelectOption[] = []
function visit (t: TreeSelectOption[]): void {
t.forEach((n) => {
path.push(n)
if (filter(pattern, n)) {
visitedTailKeys.add((n as any)[keyField] as Key)
highlightKeySet.add((n as any)[keyField] as Key)
for (let i = path.length - 2; i >= 0; --i) {
const key: Key = (path[i] as any)[keyField]
if (!visitedNonTailKeys.has(key)) {
visitedNonTailKeys.add(key)
if (visitedTailKeys.has(key)) {
visitedTailKeys.delete(key)
}
} else {
break
}
}
}
const children = n[childrenField] as TreeSelectOption[] | undefined
if (children) {
visit(children)
}
path.pop()
})
}
visit(tree)
function build (t: TreeSelectOption[], sibs: TreeSelectOption[]): void {
t.forEach((n) => {
const key = (n as any)[keyField] as Key
const isVisitedTail = visitedTailKeys.has(key)
const isVisitedNonTail = visitedNonTailKeys.has(key)
if (!isVisitedTail && !isVisitedNonTail) return
const children = n[childrenField] as TreeSelectOption[] | undefined
if (children) {
if (isVisitedTail) {
// If it is visited path tail, use origin node
sibs.push(n)
} else {
// It it is not visited path tail, use cloned node
expandedKeys.push(key)
const clonedNode = { ...n, [childrenField]: [] }
sibs.push(clonedNode)
build(children, clonedNode[childrenField] as TreeSelectOption[])
}
} else {
sibs.push(n)
}
})
}
build(tree, filteredTree)
return {
filteredTree,
highlightKeySet,
expandedKeys
}
}
Loading

0 comments on commit e0ef701

Please sign in to comment.