diff --git a/src/_common b/src/_common index 94e21e27e2..c88163b349 160000 --- a/src/_common +++ b/src/_common @@ -1 +1 @@ -Subproject commit 94e21e27e2aba45cc0da263d424537f453055180 +Subproject commit c88163b349999008a768614816c49355185a8711 diff --git a/src/cascader/core/effect.ts b/src/cascader/core/effect.ts index 7f30f6ef05..c17b1d4394 100644 --- a/src/cascader/core/effect.ts +++ b/src/cascader/core/effect.ts @@ -45,7 +45,7 @@ export function expendClickEffect( // 非受控状态下更新状态 setValue(valueType === 'single' ? value : node.getPath().map((item) => item.value), 'check', node.getModel()); - // 当 trigger 为 hover 时 ,点击节点一定是关闭 panel 的操作 + // 当 trigger 为 hover 时 ,点击节点一定是关闭 panel 的操作 if (!checkStrictly || propsTrigger === 'hover') { setVisible(false, {}); } diff --git a/src/dialog/Dialog.tsx b/src/dialog/Dialog.tsx index 0a10d28bcb..abe521b9f3 100644 --- a/src/dialog/Dialog.tsx +++ b/src/dialog/Dialog.tsx @@ -157,7 +157,7 @@ const Dialog = forwardRef((originalProps, ref) => { const onAnimateStart = () => { if (!wrapRef.current) return; - onBeforeOpen?.() + onBeforeOpen?.(); wrapRef.current.style.display = 'block'; }; diff --git a/src/select/base/Select.tsx b/src/select/base/Select.tsx index 8da32842c7..7bebf676ee 100644 --- a/src/select/base/Select.tsx +++ b/src/select/base/Select.tsx @@ -192,7 +192,6 @@ const Select = forwardRefWithStatics( return; } - const values = currentOptions .filter((option) => !option.checkAll && !option.disabled) .map((option) => option[keys?.value || 'value']); diff --git a/src/tree/Tree.tsx b/src/tree/Tree.tsx index 7b322d6fb9..a641397af7 100644 --- a/src/tree/Tree.tsx +++ b/src/tree/Tree.tsx @@ -67,7 +67,8 @@ const Tree = forwardRef, TreeProps>((origi allowDrop, } = props; - const { value, onChange, expanded, onExpand, onActive, actived } = useControllable(props); + const { value, onChange, expanded, onExpand, onActive, actived, setTreeIndeterminate, indeterminate } = + useControllable(props); // 国际化文本初始化 const emptyText = locale('empty'); @@ -81,6 +82,8 @@ const Tree = forwardRef, TreeProps>((origi onExpand, onActive, actived, + indeterminate, + setTreeIndeterminate, }, initial, ); @@ -104,6 +107,21 @@ const Tree = forwardRef, TreeProps>((origi return expanded; }, ); + + // 因为是被 useImperativeHandle 依赖的方法,使用 usePersistFn 变成持久化的。或者也可以使用 useCallback + const setIndeterminate = usePersistFn( + ( + node: TreeNode, + isIndeterminate: boolean, + ctx: { e?: MouseEvent; trigger: 'node-click' | 'icon-click' | 'setItem' }, + ) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { e, trigger } = ctx; + const indeterminate = node.setIndeterminate(isIndeterminate); + return indeterminate; + }, + ); + const treeRef = useRef(null); const { @@ -254,11 +272,17 @@ const Tree = forwardRef, TreeProps>((origi setChecked(node, spec.checked, { trigger: 'setItem' }); delete spec.checked; } + if ('indeterminate' in options) { + // @ts-ignore + setTreeIndeterminate((prevIndeterminate: TreeNodeValue[]) => [...prevIndeterminate, value]); + setIndeterminate(node, spec.indeterminate, { trigger: 'setItem' }); + delete spec.indeterminate; + } node.set(spec); } }, }), - [store, setExpanded, setActived, setChecked, handleScrollToElement], + [store, setExpanded, setActived, setTreeIndeterminate, setChecked, setIndeterminate, handleScrollToElement], ); /* ======== render ======= */ diff --git a/src/tree/hooks/useControllable.ts b/src/tree/hooks/useControllable.ts index 2286e775fa..a4185ccef0 100644 --- a/src/tree/hooks/useControllable.ts +++ b/src/tree/hooks/useControllable.ts @@ -1,15 +1,24 @@ +import { useState } from 'react'; import useControlled from '../../hooks/useControlled'; import { TdTreeProps } from '../type'; +import type { TreeNodeValue } from '../../_common/js/tree-v1/types'; -export default function useControllable( - props: TdTreeProps, -): Pick { +export default function useControllable(props: TdTreeProps): Pick< + TdTreeProps, + 'value' | 'onChange' | 'expanded' | 'onExpand' | 'actived' | 'onActive' +> & { + setTreeIndeterminate: (value: Array) => void; + indeterminate: Array; +} { const [value, onChange] = useControlled(props, 'value', props.onChange); const [expanded, onExpand] = useControlled(props, 'expanded', props.onExpand); const [actived, onActive] = useControlled(props, 'actived', props.onActive); + // Indeterminate state + const [indeterminate, setTreeIndeterminate] = useState([]); + return { value, onChange, @@ -17,5 +26,7 @@ export default function useControllable( onExpand, actived, onActive, + setTreeIndeterminate, + indeterminate, }; } diff --git a/src/tree/hooks/useStore.ts b/src/tree/hooks/useStore.ts index 613809aab5..20b38f72f0 100644 --- a/src/tree/hooks/useStore.ts +++ b/src/tree/hooks/useStore.ts @@ -7,9 +7,13 @@ import { usePersistFn } from '../../hooks/usePersistFn'; import type { TdTreeProps } from '../type'; import type { TypeEventState } from '../interface'; -import type { TypeTreeNodeData } from '../../_common/js/tree-v1/types'; +import type { TreeNodeValue, TypeTreeNodeData } from '../../_common/js/tree-v1/types'; +import { TreeNode } from '../../cascader/interface'; -export function useStore(props: TdTreeProps, refresh: () => void): TreeStore { +export function useStore( + props: TdTreeProps & { indeterminate: any; setTreeIndeterminate: any }, + refresh: () => void, +): TreeStore { const storeRef = useRef(); const [filterChanged, toggleFilterChanged] = useState(false); const [prevExpanded, changePrevExpanded] = useState(null); @@ -34,6 +38,8 @@ export function useStore(props: TdTreeProps, refresh: () => void): TreeStore { valueMode, filter, onLoad, + indeterminate, + setTreeIndeterminate, allowFoldNodeOnFilter = false, } = props; @@ -129,7 +135,6 @@ export function useStore(props: TdTreeProps, refresh: () => void): TreeStore { // 刷新节点,必须在配置选中之前执行 // 这样选中态联动判断才能找到父节点 store.refreshNodes(); - // 初始化选中状态 if (Array.isArray(value)) { store.setChecked(value); @@ -223,6 +228,11 @@ export function useStore(props: TdTreeProps, refresh: () => void): TreeStore { useUpdateLayoutEffect(() => { if (Array.isArray(value)) { store.replaceChecked(value); + const checkedValue = store.getCheckedNodes().map((v: TreeNode) => v.data[keys.value || 'value']); + const indeterminateConflict = checkedValue.filter((v) => indeterminate.includes(v)); + if (indeterminateConflict.length) { + setTreeIndeterminate(indeterminate.filter((v: TreeNodeValue) => !indeterminateConflict.includes(v))); + } } }, [store, value, data]); @@ -239,6 +249,12 @@ export function useStore(props: TdTreeProps, refresh: () => void): TreeStore { } }, [actived, store]); + useUpdateLayoutEffect(() => { + if (Array.isArray(indeterminate)) { + store.replaceIndeterminate(indeterminate); + } + }, [indeterminate, store, data]); + useUpdateLayoutEffect(() => { store.setConfig({ filter,