ant-design-vue/components/tree/DirectoryTree.tsx

273 lines
8.2 KiB
Vue
Raw Normal View History

2021-08-19 02:56:21 +00:00
import { ExtractPropTypes, nextTick, onUpdated, PropType, ref, watch } from 'vue';
import { defineComponent } from 'vue';
import debounce from 'lodash-es/debounce';
2020-03-28 07:50:01 +00:00
import FolderOpenOutlined from '@ant-design/icons-vue/FolderOpenOutlined';
2020-04-06 03:18:26 +00:00
import FolderOutlined from '@ant-design/icons-vue/FolderOutlined';
2020-03-28 07:50:01 +00:00
import FileOutlined from '@ant-design/icons-vue/FileOutlined';
2020-08-31 08:53:19 +00:00
import classNames from '../_util/classNames';
2021-08-19 02:56:21 +00:00
import { AntdTreeNodeAttribute, treeProps } from './Tree';
2021-06-26 01:35:40 +00:00
import Tree, { TreeProps } from './Tree';
2020-10-22 06:25:02 +00:00
import initDefaultProps from '../_util/props-util/initDefaultProps';
2021-08-19 02:56:21 +00:00
import { convertDataToEntities, convertTreeToData } from '../vc-tree/utils/treeUtil';
import { DataNode, EventDataNode, Key } from '../vc-tree/interface';
import { conductExpandParent } from '../vc-tree/util';
import { calcRangeKeys, convertDirectoryKeysToNodes } from './utils/dictUtil';
import useConfigInject from '../_util/hooks/useConfigInject';
import { filterEmpty } from '../_util/props-util';
2018-09-26 14:57:01 +00:00
2021-08-19 02:56:21 +00:00
export type ExpandAction = false | 'click' | 'doubleclick' | 'dblclick';
2018-09-26 14:57:01 +00:00
2021-08-17 14:03:49 +00:00
const directoryTreeProps = {
...treeProps(),
expandAction: { type: [Boolean, String] as PropType<ExpandAction> },
};
export type DirectoryTreeProps = Partial<ExtractPropTypes<typeof directoryTreeProps>>;
2021-08-19 02:56:21 +00:00
function getIcon(props: AntdTreeNodeAttribute) {
const { isLeaf, expanded } = props;
if (isLeaf) {
return <FileOutlined />;
}
return expanded ? <FolderOpenOutlined /> : <FolderOutlined />;
}
2020-10-22 06:25:02 +00:00
export default defineComponent({
2018-09-26 14:57:01 +00:00
name: 'ADirectoryTree',
2020-07-16 10:31:20 +00:00
inheritAttrs: false,
2021-08-17 14:03:49 +00:00
props: initDefaultProps(directoryTreeProps, {
showIcon: true,
expandAction: 'click',
}),
2021-08-19 02:56:21 +00:00
slots: ['icon', 'title', 'switcherIcon'],
emits: [
'update:selectedKeys',
'update:checkedKeys',
'update:expandedKeys',
'expand',
'select',
'check',
'doubleclick',
'dblclick',
'click',
],
setup(props, { attrs, slots, emit }) {
// convertTreeToData 兼容 a-tree-node 历史写法未来a-tree-node移除后删除相关代码不要再render中调用 treeData否则死循环
const treeData = ref<DataNode[]>(
props.treeData || convertTreeToData(filterEmpty(slots.default?.())),
);
watch(
() => props.treeData,
() => {
treeData.value = props.treeData;
},
);
onUpdated(() => {
nextTick(() => {
if (props.treeData === undefined && slots.default) {
treeData.value = convertTreeToData(filterEmpty(slots.default?.()));
}
});
});
// Shift click usage
const lastSelectedKey = ref<Key>();
const cachedSelectedKeys = ref<Key[]>();
const treeRef = ref();
const getInitExpandedKeys = () => {
const { keyEntities } = convertDataToEntities(treeData.value);
let initExpandedKeys: any;
// Expanded keys
if (props.defaultExpandAll) {
initExpandedKeys = Object.keys(keyEntities);
} else if (props.defaultExpandParent) {
initExpandedKeys = conductExpandParent(
props.expandedKeys || props.defaultExpandedKeys,
keyEntities,
);
} else {
initExpandedKeys = props.expandedKeys || props.defaultExpandedKeys;
}
return initExpandedKeys;
};
const selectedKeys = ref(props.selectedKeys || props.defaultSelectedKeys || []);
const expandedKeys = ref<Key[]>(getInitExpandedKeys());
watch(
() => props.selectedKeys,
() => {
if (props.selectedKeys !== undefined) {
selectedKeys.value = props.selectedKeys;
}
},
{ immediate: true },
);
watch(
() => props.expandedKeys,
() => {
if (props.expandedKeys !== undefined) {
expandedKeys.value = props.expandedKeys;
}
},
{ immediate: true },
);
const expandFolderNode = (event: MouseEvent, node: any) => {
const { isLeaf } = node;
if (isLeaf || event.shiftKey || event.metaKey || event.ctrlKey) {
return;
}
// Call internal rc-tree expand function
// https://github.com/ant-design/ant-design/issues/12567
treeRef.value!.onNodeExpand(event as any, node);
};
const onDebounceExpand = debounce(expandFolderNode, 200, {
leading: true,
});
const onExpand = (
keys: Key[],
info: {
node: EventDataNode;
expanded: boolean;
nativeEvent: MouseEvent;
},
) => {
if (props.expandedKeys === undefined) {
expandedKeys.value = keys;
}
// Call origin function
emit('update:expandedKeys', keys);
emit('expand', keys, info);
};
const onClick = (event: MouseEvent, node: EventDataNode) => {
const { expandAction } = props;
// Expand the tree
if (expandAction === 'click') {
onDebounceExpand(event, node);
}
emit('click', event, node);
};
const onDoubleClick = (event: MouseEvent, node: EventDataNode) => {
const { expandAction } = props;
// Expand the tree
if (expandAction === 'dblclick' || expandAction === 'doubleclick') {
onDebounceExpand(event, node);
}
emit('doubleclick', event, node);
emit('dblclick', event, node);
};
const onSelect = (
keys: Key[],
event: {
event: 'select';
selected: boolean;
node: any;
selectedNodes: DataNode[];
nativeEvent: MouseEvent;
},
) => {
const { multiple } = props;
const { node, nativeEvent } = event;
const { key = '' } = node;
// const newState: DirectoryTreeState = {};
// We need wrap this event since some value is not same
const newEvent: any = {
...event,
selected: true, // Directory selected always true
};
// Windows / Mac single pick
const ctrlPick: boolean = nativeEvent.ctrlKey || nativeEvent.metaKey;
const shiftPick: boolean = nativeEvent.shiftKey;
// Generate new selected keys
let newSelectedKeys: Key[];
if (multiple && ctrlPick) {
// Control click
newSelectedKeys = keys;
lastSelectedKey.value = key;
cachedSelectedKeys.value = newSelectedKeys;
newEvent.selectedNodes = convertDirectoryKeysToNodes(treeData.value, newSelectedKeys);
} else if (multiple && shiftPick) {
// Shift click
newSelectedKeys = Array.from(
new Set([
...(cachedSelectedKeys.value || []),
...calcRangeKeys({
treeData: treeData.value,
expandedKeys: expandedKeys.value,
startKey: key,
endKey: lastSelectedKey.value,
}),
]),
);
newEvent.selectedNodes = convertDirectoryKeysToNodes(treeData.value, newSelectedKeys);
} else {
// Single click
newSelectedKeys = [key];
lastSelectedKey.value = key;
cachedSelectedKeys.value = newSelectedKeys;
newEvent.selectedNodes = convertDirectoryKeysToNodes(treeData.value, newSelectedKeys);
}
emit('update:selectedKeys', newSelectedKeys);
emit('select', newSelectedKeys, newEvent);
if (props.selectedKeys === undefined) {
selectedKeys.value = newSelectedKeys;
}
};
const onCheck: TreeProps['onCheck'] = (checkedObjOrKeys, eventObj) => {
emit('update:checkedKeys', checkedObjOrKeys);
emit('check', checkedObjOrKeys, eventObj);
};
const { prefixCls, direction } = useConfigInject('tree', props);
2021-08-17 14:03:49 +00:00
return () => {
2021-08-19 02:56:21 +00:00
const connectClassName = classNames(
`${prefixCls.value}-directory`,
{
[`${prefixCls.value}-directory-rtl`]: direction.value === 'rtl',
},
attrs.class,
);
2021-08-19 15:29:07 +00:00
const { icon = slots.icon, blockNode = true, ...otherProps } = props;
2021-08-19 02:56:21 +00:00
return (
<Tree
{...attrs}
icon={icon || getIcon}
ref={treeRef}
2021-08-19 15:29:07 +00:00
blockNode={blockNode}
2021-08-19 02:56:21 +00:00
{...otherProps}
prefixCls={prefixCls.value}
class={connectClassName}
expandedKeys={expandedKeys.value}
selectedKeys={selectedKeys.value}
onSelect={onSelect}
onClick={onClick}
onDblclick={onDoubleClick}
onExpand={onExpand}
onCheck={onCheck}
v-slots={slots}
/>
);
2019-01-12 03:33:27 +00:00
};
2018-09-26 14:57:01 +00:00
},
2020-10-22 06:25:02 +00:00
});