refactor: tree
parent
3117c2748b
commit
a0f7e8d21f
|
@ -20,6 +20,7 @@ export default (
|
||||||
}>;
|
}>;
|
||||||
autoInsertSpaceInButton: ComputedRef<Boolean>;
|
autoInsertSpaceInButton: ComputedRef<Boolean>;
|
||||||
renderEmpty?: ComputedRef<(componentName?: string) => VNodeChild | JSX.Element>;
|
renderEmpty?: ComputedRef<(componentName?: string) => VNodeChild | JSX.Element>;
|
||||||
|
virtual: ComputedRef<Boolean>;
|
||||||
} => {
|
} => {
|
||||||
const configProvider = inject<UnwrapRef<ConfigProviderProps>>(
|
const configProvider = inject<UnwrapRef<ConfigProviderProps>>(
|
||||||
'configProvider',
|
'configProvider',
|
||||||
|
@ -34,6 +35,7 @@ export default (
|
||||||
const form = computed(() => configProvider.form);
|
const form = computed(() => configProvider.form);
|
||||||
const size = computed(() => props.size || configProvider.componentSize);
|
const size = computed(() => props.size || configProvider.componentSize);
|
||||||
const getTargetContainer = computed(() => props.getTargetContainer);
|
const getTargetContainer = computed(() => props.getTargetContainer);
|
||||||
|
const virtual = computed(() => props.virtual);
|
||||||
return {
|
return {
|
||||||
configProvider,
|
configProvider,
|
||||||
prefixCls,
|
prefixCls,
|
||||||
|
@ -45,5 +47,6 @@ export default (
|
||||||
form,
|
form,
|
||||||
autoInsertSpaceInButton,
|
autoInsertSpaceInButton,
|
||||||
renderEmpty,
|
renderEmpty,
|
||||||
|
virtual,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -747,6 +747,7 @@
|
||||||
|
|
||||||
// Tree
|
// Tree
|
||||||
// ---
|
// ---
|
||||||
|
@tree-bg: @component-background;
|
||||||
@tree-title-height: 24px;
|
@tree-title-height: 24px;
|
||||||
@tree-child-padding: 18px;
|
@tree-child-padding: 18px;
|
||||||
@tree-directory-selected-color: #fff;
|
@tree-directory-selected-color: #fff;
|
||||||
|
|
|
@ -0,0 +1,251 @@
|
||||||
|
import type { VNode } from 'vue';
|
||||||
|
import { defineComponent, inject } from 'vue';
|
||||||
|
import omit from 'omit.js';
|
||||||
|
import debounce from 'lodash-es/debounce';
|
||||||
|
import FolderOpenOutlined from '@ant-design/icons-vue/FolderOpenOutlined';
|
||||||
|
import FolderOutlined from '@ant-design/icons-vue/FolderOutlined';
|
||||||
|
import FileOutlined from '@ant-design/icons-vue/FileOutlined';
|
||||||
|
import PropTypes from '../_util/vue-types';
|
||||||
|
import classNames from '../_util/classNames';
|
||||||
|
import { conductExpandParent, convertTreeToEntities } from '../vc-tree/src/util';
|
||||||
|
import type { CheckEvent, ExpendEvent, SelectEvent } from './Tree';
|
||||||
|
import Tree, { TreeProps } from './Tree';
|
||||||
|
import {
|
||||||
|
calcRangeKeys,
|
||||||
|
getFullKeyList,
|
||||||
|
convertDirectoryKeysToNodes,
|
||||||
|
getFullKeyListByTreeData,
|
||||||
|
} from './util';
|
||||||
|
import BaseMixin from '../_util/BaseMixin';
|
||||||
|
import { getOptionProps, getComponent, getSlot } from '../_util/props-util';
|
||||||
|
import initDefaultProps from '../_util/props-util/initDefaultProps';
|
||||||
|
import { defaultConfigProvider } from '../config-provider';
|
||||||
|
|
||||||
|
// export type ExpandAction = false | 'click' | 'dblclick'; export interface
|
||||||
|
// DirectoryTreeProps extends TreeProps { expandAction?: ExpandAction; }
|
||||||
|
// export interface DirectoryTreeState { expandedKeys?: string[];
|
||||||
|
// selectedKeys?: string[]; }
|
||||||
|
|
||||||
|
export interface DirectoryTreeState {
|
||||||
|
_expandedKeys?: (string | number)[];
|
||||||
|
_selectedKeys?: (string | number)[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIcon(props: { isLeaf: boolean; expanded: boolean } & VNode) {
|
||||||
|
const { isLeaf, expanded } = props;
|
||||||
|
if (isLeaf) {
|
||||||
|
return <FileOutlined />;
|
||||||
|
}
|
||||||
|
return expanded ? <FolderOpenOutlined /> : <FolderOutlined />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'ADirectoryTree',
|
||||||
|
mixins: [BaseMixin],
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: initDefaultProps(
|
||||||
|
{
|
||||||
|
...TreeProps(),
|
||||||
|
expandAction: PropTypes.oneOf([false, 'click', 'doubleclick', 'dblclick']),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
showIcon: true,
|
||||||
|
expandAction: 'click',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
setup() {
|
||||||
|
return {
|
||||||
|
children: null,
|
||||||
|
onDebounceExpand: null,
|
||||||
|
tree: null,
|
||||||
|
lastSelectedKey: '',
|
||||||
|
cachedSelectedKeys: [],
|
||||||
|
configProvider: inject('configProvider', defaultConfigProvider),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
const props = getOptionProps(this);
|
||||||
|
const { defaultExpandAll, defaultExpandParent, expandedKeys, defaultExpandedKeys } = props;
|
||||||
|
const children = getSlot(this);
|
||||||
|
const { keyEntities } = convertTreeToEntities(children);
|
||||||
|
const state: DirectoryTreeState = {};
|
||||||
|
// Selected keys
|
||||||
|
state._selectedKeys = props.selectedKeys || props.defaultSelectedKeys || [];
|
||||||
|
|
||||||
|
// Expanded keys
|
||||||
|
if (defaultExpandAll) {
|
||||||
|
if (props.treeData) {
|
||||||
|
state._expandedKeys = getFullKeyListByTreeData(props.treeData, props.replaceFields);
|
||||||
|
} else {
|
||||||
|
state._expandedKeys = getFullKeyList(children);
|
||||||
|
}
|
||||||
|
} else if (defaultExpandParent) {
|
||||||
|
state._expandedKeys = conductExpandParent(expandedKeys || defaultExpandedKeys, keyEntities);
|
||||||
|
} else {
|
||||||
|
state._expandedKeys = expandedKeys || defaultExpandedKeys;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
_selectedKeys: [],
|
||||||
|
_expandedKeys: [],
|
||||||
|
...state,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
expandedKeys(val) {
|
||||||
|
this.setState({ _expandedKeys: val });
|
||||||
|
},
|
||||||
|
selectedKeys(val) {
|
||||||
|
this.setState({ _selectedKeys: val });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.onDebounceExpand = debounce(this.expandFolderNode, 200, { leading: true });
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleExpand(expandedKeys: (string | number)[], info: ExpendEvent) {
|
||||||
|
this.setUncontrolledState({ _expandedKeys: expandedKeys });
|
||||||
|
this.$emit('update:expandedKeys', expandedKeys);
|
||||||
|
this.$emit('expand', expandedKeys, info);
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
handleClick(event: MouseEvent, node: VNode) {
|
||||||
|
const { expandAction } = this.$props;
|
||||||
|
|
||||||
|
// Expand the tree
|
||||||
|
if (expandAction === 'click') {
|
||||||
|
this.onDebounceExpand(event, node);
|
||||||
|
}
|
||||||
|
this.$emit('click', event, node);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDoubleClick(event: MouseEvent, node: VNode) {
|
||||||
|
const { expandAction } = this.$props;
|
||||||
|
|
||||||
|
// Expand the tree
|
||||||
|
if (expandAction === 'dblclick' || expandAction === 'doubleclick') {
|
||||||
|
this.onDebounceExpand(event, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('doubleclick', event, node);
|
||||||
|
this.$emit('dblclick', event, node);
|
||||||
|
},
|
||||||
|
|
||||||
|
hanldeSelect(keys: (string | number)[], event: SelectEvent) {
|
||||||
|
const { multiple } = this.$props;
|
||||||
|
const children = this.children || [];
|
||||||
|
const { _expandedKeys: expandedKeys = [] } = this.$data;
|
||||||
|
const { node, nativeEvent } = event;
|
||||||
|
const { eventKey = '' } = node;
|
||||||
|
|
||||||
|
const newState: DirectoryTreeState = {};
|
||||||
|
|
||||||
|
// We need wrap this event since some value is not same
|
||||||
|
const newEvent = {
|
||||||
|
...event,
|
||||||
|
selected: true, // Directory selected always true
|
||||||
|
};
|
||||||
|
|
||||||
|
// Windows / Mac single pick
|
||||||
|
const ctrlPick = nativeEvent.ctrlKey || nativeEvent.metaKey;
|
||||||
|
const shiftPick = nativeEvent.shiftKey;
|
||||||
|
|
||||||
|
// Generate new selected keys
|
||||||
|
let newSelectedKeys: (string | number)[];
|
||||||
|
if (multiple && ctrlPick) {
|
||||||
|
// Control click
|
||||||
|
newSelectedKeys = keys;
|
||||||
|
this.lastSelectedKey = eventKey;
|
||||||
|
this.cachedSelectedKeys = newSelectedKeys;
|
||||||
|
newEvent.selectedNodes = convertDirectoryKeysToNodes(children, newSelectedKeys);
|
||||||
|
} else if (multiple && shiftPick) {
|
||||||
|
// Shift click
|
||||||
|
newSelectedKeys = Array.from(
|
||||||
|
new Set([
|
||||||
|
...(this.cachedSelectedKeys || []),
|
||||||
|
...calcRangeKeys(children, expandedKeys, eventKey, this.lastSelectedKey),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
newEvent.selectedNodes = convertDirectoryKeysToNodes(children, newSelectedKeys);
|
||||||
|
} else {
|
||||||
|
// Single click
|
||||||
|
newSelectedKeys = [eventKey];
|
||||||
|
this.lastSelectedKey = eventKey;
|
||||||
|
this.cachedSelectedKeys = newSelectedKeys;
|
||||||
|
newEvent.selectedNodes = [event.node];
|
||||||
|
}
|
||||||
|
newState._selectedKeys = newSelectedKeys;
|
||||||
|
|
||||||
|
this.$emit('update:selectedKeys', newSelectedKeys);
|
||||||
|
this.$emit('select', newSelectedKeys, newEvent);
|
||||||
|
|
||||||
|
this.setUncontrolledState(newState);
|
||||||
|
},
|
||||||
|
setTreeRef(node: VNode) {
|
||||||
|
this.tree = node;
|
||||||
|
},
|
||||||
|
|
||||||
|
expandFolderNode(event: MouseEvent, node: { isLeaf: boolean } & VNode) {
|
||||||
|
const { isLeaf } = node;
|
||||||
|
|
||||||
|
if (isLeaf || event.shiftKey || event.metaKey || event.ctrlKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.tree.tree) {
|
||||||
|
// Get internal vc-tree
|
||||||
|
const internalTree = this.tree.tree;
|
||||||
|
|
||||||
|
// Call internal rc-tree expand function
|
||||||
|
// https://github.com/ant-design/ant-design/issues/12567
|
||||||
|
internalTree.onNodeExpand(event, node);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setUncontrolledState(state: unknown) {
|
||||||
|
const newState = omit(
|
||||||
|
state,
|
||||||
|
Object.keys(getOptionProps(this)).map(p => `_${p}`),
|
||||||
|
);
|
||||||
|
if (Object.keys(newState).length) {
|
||||||
|
this.setState(newState);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleCheck(checkedObj: (string | number)[], eventObj: CheckEvent) {
|
||||||
|
this.$emit('update:checkedKeys', checkedObj);
|
||||||
|
this.$emit('check', checkedObj, eventObj);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
this.children = getSlot(this);
|
||||||
|
const { prefixCls: customizePrefixCls, ...props } = getOptionProps(this);
|
||||||
|
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||||
|
const prefixCls = getPrefixCls('tree', customizePrefixCls);
|
||||||
|
const { _expandedKeys: expandedKeys, _selectedKeys: selectedKeys } = this.$data;
|
||||||
|
const { class: className, ...restAttrs } = this.$attrs;
|
||||||
|
const connectClassName = classNames(`${prefixCls}-directory`, className);
|
||||||
|
const treeProps = {
|
||||||
|
icon: getIcon,
|
||||||
|
...restAttrs,
|
||||||
|
...omit(props, ['onUpdate:selectedKeys', 'onUpdate:checkedKeys', 'onUpdate:expandedKeys']),
|
||||||
|
prefixCls,
|
||||||
|
expandedKeys,
|
||||||
|
selectedKeys,
|
||||||
|
switcherIcon: getComponent(this, 'switcherIcon'),
|
||||||
|
ref: this.setTreeRef,
|
||||||
|
class: connectClassName,
|
||||||
|
onSelect: this.hanldeSelect,
|
||||||
|
onClick: this.handleClick,
|
||||||
|
onDblclick: this.handleDoubleClick,
|
||||||
|
onExpand: this.handleExpand,
|
||||||
|
onCheck: this.handleCheck,
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Tree {...treeProps} v-slots={omit(this.$slots, ['default'])}>
|
||||||
|
{this.children}
|
||||||
|
</Tree>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,4 +1,4 @@
|
||||||
import type { VNode } from 'vue';
|
import type { ExtractPropTypes, PropType, VNode } from 'vue';
|
||||||
import { defineComponent, inject } from 'vue';
|
import { defineComponent, inject } from 'vue';
|
||||||
import omit from 'omit.js';
|
import omit from 'omit.js';
|
||||||
import debounce from 'lodash-es/debounce';
|
import debounce from 'lodash-es/debounce';
|
||||||
|
@ -7,8 +7,7 @@ import FolderOutlined from '@ant-design/icons-vue/FolderOutlined';
|
||||||
import FileOutlined from '@ant-design/icons-vue/FileOutlined';
|
import FileOutlined from '@ant-design/icons-vue/FileOutlined';
|
||||||
import PropTypes from '../_util/vue-types';
|
import PropTypes from '../_util/vue-types';
|
||||||
import classNames from '../_util/classNames';
|
import classNames from '../_util/classNames';
|
||||||
import { conductExpandParent, convertTreeToEntities } from '../vc-tree/src/util';
|
import { treeProps } from './Tree';
|
||||||
import type { CheckEvent, ExpendEvent, SelectEvent } from './Tree';
|
|
||||||
import Tree, { TreeProps } from './Tree';
|
import Tree, { TreeProps } from './Tree';
|
||||||
import {
|
import {
|
||||||
calcRangeKeys,
|
calcRangeKeys,
|
||||||
|
@ -16,20 +15,11 @@ import {
|
||||||
convertDirectoryKeysToNodes,
|
convertDirectoryKeysToNodes,
|
||||||
getFullKeyListByTreeData,
|
getFullKeyListByTreeData,
|
||||||
} from './util';
|
} from './util';
|
||||||
import BaseMixin from '../_util/BaseMixin';
|
|
||||||
import { getOptionProps, getComponent, getSlot } from '../_util/props-util';
|
import { getOptionProps, getComponent, getSlot } from '../_util/props-util';
|
||||||
import initDefaultProps from '../_util/props-util/initDefaultProps';
|
import initDefaultProps from '../_util/props-util/initDefaultProps';
|
||||||
import { defaultConfigProvider } from '../config-provider';
|
import { defaultConfigProvider } from '../config-provider';
|
||||||
|
|
||||||
// export type ExpandAction = false | 'click' | 'dblclick'; export interface
|
export type ExpandAction = false | 'click' | 'doubleClick' | 'dblclick';
|
||||||
// DirectoryTreeProps extends TreeProps { expandAction?: ExpandAction; }
|
|
||||||
// export interface DirectoryTreeState { expandedKeys?: string[];
|
|
||||||
// selectedKeys?: string[]; }
|
|
||||||
|
|
||||||
export interface DirectoryTreeState {
|
|
||||||
_expandedKeys?: (string | number)[];
|
|
||||||
_selectedKeys?: (string | number)[];
|
|
||||||
}
|
|
||||||
|
|
||||||
function getIcon(props: { isLeaf: boolean; expanded: boolean } & VNode) {
|
function getIcon(props: { isLeaf: boolean; expanded: boolean } & VNode) {
|
||||||
const { isLeaf, expanded } = props;
|
const { isLeaf, expanded } = props;
|
||||||
|
@ -39,213 +29,23 @@ function getIcon(props: { isLeaf: boolean; expanded: boolean } & VNode) {
|
||||||
return expanded ? <FolderOpenOutlined /> : <FolderOutlined />;
|
return expanded ? <FolderOpenOutlined /> : <FolderOutlined />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const directoryTreeProps = {
|
||||||
|
...treeProps(),
|
||||||
|
expandAction: { type: [Boolean, String] as PropType<ExpandAction> },
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DirectoryTreeProps = Partial<ExtractPropTypes<typeof directoryTreeProps>>;
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'ADirectoryTree',
|
name: 'ADirectoryTree',
|
||||||
mixins: [BaseMixin],
|
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: initDefaultProps(
|
props: initDefaultProps(directoryTreeProps, {
|
||||||
{
|
|
||||||
...TreeProps(),
|
|
||||||
expandAction: PropTypes.oneOf([false, 'click', 'doubleclick', 'dblclick']),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
showIcon: true,
|
showIcon: true,
|
||||||
expandAction: 'click',
|
expandAction: 'click',
|
||||||
},
|
}),
|
||||||
),
|
|
||||||
setup() {
|
setup() {
|
||||||
return {
|
return () => {
|
||||||
children: null,
|
return null;
|
||||||
onDebounceExpand: null,
|
|
||||||
tree: null,
|
|
||||||
lastSelectedKey: '',
|
|
||||||
cachedSelectedKeys: [],
|
|
||||||
configProvider: inject('configProvider', defaultConfigProvider),
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
data() {
|
|
||||||
const props = getOptionProps(this);
|
|
||||||
const { defaultExpandAll, defaultExpandParent, expandedKeys, defaultExpandedKeys } = props;
|
|
||||||
const children = getSlot(this);
|
|
||||||
const { keyEntities } = convertTreeToEntities(children);
|
|
||||||
const state: DirectoryTreeState = {};
|
|
||||||
// Selected keys
|
|
||||||
state._selectedKeys = props.selectedKeys || props.defaultSelectedKeys || [];
|
|
||||||
|
|
||||||
// Expanded keys
|
|
||||||
if (defaultExpandAll) {
|
|
||||||
if (props.treeData) {
|
|
||||||
state._expandedKeys = getFullKeyListByTreeData(props.treeData, props.replaceFields);
|
|
||||||
} else {
|
|
||||||
state._expandedKeys = getFullKeyList(children);
|
|
||||||
}
|
|
||||||
} else if (defaultExpandParent) {
|
|
||||||
state._expandedKeys = conductExpandParent(expandedKeys || defaultExpandedKeys, keyEntities);
|
|
||||||
} else {
|
|
||||||
state._expandedKeys = expandedKeys || defaultExpandedKeys;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
_selectedKeys: [],
|
|
||||||
_expandedKeys: [],
|
|
||||||
...state,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
expandedKeys(val) {
|
|
||||||
this.setState({ _expandedKeys: val });
|
|
||||||
},
|
|
||||||
selectedKeys(val) {
|
|
||||||
this.setState({ _selectedKeys: val });
|
|
||||||
},
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.onDebounceExpand = debounce(this.expandFolderNode, 200, { leading: true });
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleExpand(expandedKeys: (string | number)[], info: ExpendEvent) {
|
|
||||||
this.setUncontrolledState({ _expandedKeys: expandedKeys });
|
|
||||||
this.$emit('update:expandedKeys', expandedKeys);
|
|
||||||
this.$emit('expand', expandedKeys, info);
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
},
|
|
||||||
|
|
||||||
handleClick(event: MouseEvent, node: VNode) {
|
|
||||||
const { expandAction } = this.$props;
|
|
||||||
|
|
||||||
// Expand the tree
|
|
||||||
if (expandAction === 'click') {
|
|
||||||
this.onDebounceExpand(event, node);
|
|
||||||
}
|
|
||||||
this.$emit('click', event, node);
|
|
||||||
},
|
|
||||||
|
|
||||||
handleDoubleClick(event: MouseEvent, node: VNode) {
|
|
||||||
const { expandAction } = this.$props;
|
|
||||||
|
|
||||||
// Expand the tree
|
|
||||||
if (expandAction === 'dblclick' || expandAction === 'doubleclick') {
|
|
||||||
this.onDebounceExpand(event, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$emit('doubleclick', event, node);
|
|
||||||
this.$emit('dblclick', event, node);
|
|
||||||
},
|
|
||||||
|
|
||||||
hanldeSelect(keys: (string | number)[], event: SelectEvent) {
|
|
||||||
const { multiple } = this.$props;
|
|
||||||
const children = this.children || [];
|
|
||||||
const { _expandedKeys: expandedKeys = [] } = this.$data;
|
|
||||||
const { node, nativeEvent } = event;
|
|
||||||
const { eventKey = '' } = node;
|
|
||||||
|
|
||||||
const newState: DirectoryTreeState = {};
|
|
||||||
|
|
||||||
// We need wrap this event since some value is not same
|
|
||||||
const newEvent = {
|
|
||||||
...event,
|
|
||||||
selected: true, // Directory selected always true
|
|
||||||
};
|
|
||||||
|
|
||||||
// Windows / Mac single pick
|
|
||||||
const ctrlPick = nativeEvent.ctrlKey || nativeEvent.metaKey;
|
|
||||||
const shiftPick = nativeEvent.shiftKey;
|
|
||||||
|
|
||||||
// Generate new selected keys
|
|
||||||
let newSelectedKeys: (string | number)[];
|
|
||||||
if (multiple && ctrlPick) {
|
|
||||||
// Control click
|
|
||||||
newSelectedKeys = keys;
|
|
||||||
this.lastSelectedKey = eventKey;
|
|
||||||
this.cachedSelectedKeys = newSelectedKeys;
|
|
||||||
newEvent.selectedNodes = convertDirectoryKeysToNodes(children, newSelectedKeys);
|
|
||||||
} else if (multiple && shiftPick) {
|
|
||||||
// Shift click
|
|
||||||
newSelectedKeys = Array.from(
|
|
||||||
new Set([
|
|
||||||
...(this.cachedSelectedKeys || []),
|
|
||||||
...calcRangeKeys(children, expandedKeys, eventKey, this.lastSelectedKey),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
newEvent.selectedNodes = convertDirectoryKeysToNodes(children, newSelectedKeys);
|
|
||||||
} else {
|
|
||||||
// Single click
|
|
||||||
newSelectedKeys = [eventKey];
|
|
||||||
this.lastSelectedKey = eventKey;
|
|
||||||
this.cachedSelectedKeys = newSelectedKeys;
|
|
||||||
newEvent.selectedNodes = [event.node];
|
|
||||||
}
|
|
||||||
newState._selectedKeys = newSelectedKeys;
|
|
||||||
|
|
||||||
this.$emit('update:selectedKeys', newSelectedKeys);
|
|
||||||
this.$emit('select', newSelectedKeys, newEvent);
|
|
||||||
|
|
||||||
this.setUncontrolledState(newState);
|
|
||||||
},
|
|
||||||
setTreeRef(node: VNode) {
|
|
||||||
this.tree = node;
|
|
||||||
},
|
|
||||||
|
|
||||||
expandFolderNode(event: MouseEvent, node: { isLeaf: boolean } & VNode) {
|
|
||||||
const { isLeaf } = node;
|
|
||||||
|
|
||||||
if (isLeaf || event.shiftKey || event.metaKey || event.ctrlKey) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.tree.tree) {
|
|
||||||
// Get internal vc-tree
|
|
||||||
const internalTree = this.tree.tree;
|
|
||||||
|
|
||||||
// Call internal rc-tree expand function
|
|
||||||
// https://github.com/ant-design/ant-design/issues/12567
|
|
||||||
internalTree.onNodeExpand(event, node);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setUncontrolledState(state: unknown) {
|
|
||||||
const newState = omit(
|
|
||||||
state,
|
|
||||||
Object.keys(getOptionProps(this)).map(p => `_${p}`),
|
|
||||||
);
|
|
||||||
if (Object.keys(newState).length) {
|
|
||||||
this.setState(newState);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleCheck(checkedObj: (string | number)[], eventObj: CheckEvent) {
|
|
||||||
this.$emit('update:checkedKeys', checkedObj);
|
|
||||||
this.$emit('check', checkedObj, eventObj);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
this.children = getSlot(this);
|
|
||||||
const { prefixCls: customizePrefixCls, ...props } = getOptionProps(this);
|
|
||||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
|
||||||
const prefixCls = getPrefixCls('tree', customizePrefixCls);
|
|
||||||
const { _expandedKeys: expandedKeys, _selectedKeys: selectedKeys } = this.$data;
|
|
||||||
const { class: className, ...restAttrs } = this.$attrs;
|
|
||||||
const connectClassName = classNames(`${prefixCls}-directory`, className);
|
|
||||||
const treeProps = {
|
|
||||||
icon: getIcon,
|
|
||||||
...restAttrs,
|
|
||||||
...omit(props, ['onUpdate:selectedKeys', 'onUpdate:checkedKeys', 'onUpdate:expandedKeys']),
|
|
||||||
prefixCls,
|
|
||||||
expandedKeys,
|
|
||||||
selectedKeys,
|
|
||||||
switcherIcon: getComponent(this, 'switcherIcon'),
|
|
||||||
ref: this.setTreeRef,
|
|
||||||
class: connectClassName,
|
|
||||||
onSelect: this.hanldeSelect,
|
|
||||||
onClick: this.handleClick,
|
|
||||||
onDblclick: this.handleDoubleClick,
|
|
||||||
onExpand: this.handleExpand,
|
|
||||||
onCheck: this.handleCheck,
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<Tree {...treeProps} v-slots={omit(this.$slots, ['default'])}>
|
|
||||||
{this.children}
|
|
||||||
</Tree>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,289 @@
|
||||||
|
import type { VNode, PropType, CSSProperties } from 'vue';
|
||||||
|
import { defineComponent, inject } from 'vue';
|
||||||
|
import classNames from '../_util/classNames';
|
||||||
|
import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined';
|
||||||
|
import FileOutlined from '@ant-design/icons-vue/FileOutlined';
|
||||||
|
import CaretDownFilled from '@ant-design/icons-vue/CaretDownFilled';
|
||||||
|
import MinusSquareOutlined from '@ant-design/icons-vue/MinusSquareOutlined';
|
||||||
|
import PlusSquareOutlined from '@ant-design/icons-vue/PlusSquareOutlined';
|
||||||
|
import VcTree, { TreeNode } from '../vc-tree';
|
||||||
|
import animation from '../_util/openAnimation';
|
||||||
|
import PropTypes from '../_util/vue-types';
|
||||||
|
import { getOptionProps, getComponent, getSlot } from '../_util/props-util';
|
||||||
|
import initDefaultProps from '../_util/props-util/initDefaultProps';
|
||||||
|
import { cloneElement } from '../_util/vnode';
|
||||||
|
import { defaultConfigProvider } from '../config-provider';
|
||||||
|
|
||||||
|
export interface TreeDataItem {
|
||||||
|
key?: string | number;
|
||||||
|
title?: string;
|
||||||
|
isLeaf?: boolean;
|
||||||
|
selectable?: boolean;
|
||||||
|
children?: TreeDataItem[];
|
||||||
|
disableCheckbox?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
class?: string;
|
||||||
|
style?: CSSProperties;
|
||||||
|
checkable?: boolean;
|
||||||
|
icon?: VNode;
|
||||||
|
|
||||||
|
slots?: Record<string, string>;
|
||||||
|
switcherIcon?: VNode;
|
||||||
|
// support custom field
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DefaultEvent {
|
||||||
|
nativeEvent: MouseEvent;
|
||||||
|
node: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CheckEvent extends DefaultEvent {
|
||||||
|
checked: boolean;
|
||||||
|
checkedNodes: Array<Record<string, any>>;
|
||||||
|
checkedNodesPositions: { node: Record<string, any>; pos: string | number }[];
|
||||||
|
event: string;
|
||||||
|
halfCheckedKeys: (string | number)[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExpendEvent extends DefaultEvent {
|
||||||
|
expanded: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SelectEvent extends DefaultEvent {
|
||||||
|
event: string;
|
||||||
|
selected: boolean;
|
||||||
|
selectedNodes: Array<Record<string, any>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TreeDragEvent {
|
||||||
|
event: DragEvent;
|
||||||
|
expandedKeys: (string | number)[];
|
||||||
|
node: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DropEvent {
|
||||||
|
dragNode: Record<string, any>;
|
||||||
|
dragNodesKeys: (string | number)[];
|
||||||
|
dropPosition: number;
|
||||||
|
dropToGap: boolean;
|
||||||
|
event: DragEvent;
|
||||||
|
node: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function TreeProps() {
|
||||||
|
return {
|
||||||
|
showLine: PropTypes.looseBool,
|
||||||
|
/** 是否支持多选 */
|
||||||
|
multiple: PropTypes.looseBool,
|
||||||
|
/** 是否自动展开父节点 */
|
||||||
|
autoExpandParent: PropTypes.looseBool,
|
||||||
|
/** checkable状态下节点选择完全受控(父子节点选中状态不再关联)*/
|
||||||
|
checkStrictly: PropTypes.looseBool,
|
||||||
|
/** 是否支持选中 */
|
||||||
|
checkable: PropTypes.looseBool,
|
||||||
|
/** 是否禁用树 */
|
||||||
|
disabled: PropTypes.looseBool,
|
||||||
|
/** 默认展开所有树节点 */
|
||||||
|
defaultExpandAll: PropTypes.looseBool,
|
||||||
|
/** 默认展开对应树节点 */
|
||||||
|
defaultExpandParent: PropTypes.looseBool,
|
||||||
|
/** 默认展开指定的树节点 */
|
||||||
|
defaultExpandedKeys: PropTypes.arrayOf(
|
||||||
|
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||||
|
),
|
||||||
|
/** (受控)展开指定的树节点 */
|
||||||
|
expandedKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||||
|
/** (受控)选中复选框的树节点 */
|
||||||
|
checkedKeys: PropTypes.oneOfType([
|
||||||
|
PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||||
|
PropTypes.shape({
|
||||||
|
checked: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||||
|
halfChecked: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||||
|
}).loose,
|
||||||
|
]),
|
||||||
|
/** 默认选中复选框的树节点 */
|
||||||
|
defaultCheckedKeys: PropTypes.arrayOf(
|
||||||
|
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||||
|
),
|
||||||
|
/** (受控)设置选中的树节点 */
|
||||||
|
selectedKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||||
|
/** 默认选中的树节点 */
|
||||||
|
defaultSelectedKeys: PropTypes.arrayOf(
|
||||||
|
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||||
|
),
|
||||||
|
selectable: PropTypes.looseBool,
|
||||||
|
|
||||||
|
/** filter some AntTreeNodes as you need. it should return true */
|
||||||
|
filterAntTreeNode: PropTypes.func,
|
||||||
|
/** 异步加载数据 */
|
||||||
|
loadData: PropTypes.func,
|
||||||
|
loadedKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||||
|
// onLoaded: (loadedKeys: string[], info: { event: 'load', node: AntTreeNode; }) => void,
|
||||||
|
/** 响应右键点击 */
|
||||||
|
// onRightClick: (options: AntTreeNodeMouseEvent) => void,
|
||||||
|
/** 设置节点可拖拽(IE>8)*/
|
||||||
|
draggable: PropTypes.looseBool,
|
||||||
|
// /** 开始拖拽时调用 */
|
||||||
|
// onDragStart: (options: AntTreeNodeMouseEvent) => void,
|
||||||
|
// /** dragenter 触发时调用 */
|
||||||
|
// onDragEnter: (options: AntTreeNodeMouseEvent) => void,
|
||||||
|
// /** dragover 触发时调用 */
|
||||||
|
// onDragOver: (options: AntTreeNodeMouseEvent) => void,
|
||||||
|
// /** dragleave 触发时调用 */
|
||||||
|
// onDragLeave: (options: AntTreeNodeMouseEvent) => void,
|
||||||
|
// /** drop 触发时调用 */
|
||||||
|
// onDrop: (options: AntTreeNodeMouseEvent) => void,
|
||||||
|
showIcon: PropTypes.looseBool,
|
||||||
|
icon: PropTypes.func,
|
||||||
|
switcherIcon: PropTypes.any,
|
||||||
|
prefixCls: PropTypes.string,
|
||||||
|
filterTreeNode: PropTypes.func,
|
||||||
|
openAnimation: PropTypes.any,
|
||||||
|
treeData: {
|
||||||
|
type: Array as PropType<TreeDataItem[]>,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @default{title,key,children}
|
||||||
|
* 替换treeNode中 title,key,children字段为treeData中对应的字段
|
||||||
|
*/
|
||||||
|
replaceFields: PropTypes.object,
|
||||||
|
blockNode: PropTypes.looseBool,
|
||||||
|
/** 展开/收起节点时触发 */
|
||||||
|
onExpand: PropTypes.func,
|
||||||
|
/** 点击复选框触发 */
|
||||||
|
onCheck: PropTypes.func,
|
||||||
|
/** 点击树节点触发 */
|
||||||
|
onSelect: PropTypes.func,
|
||||||
|
/** 单击树节点触发 */
|
||||||
|
onClick: PropTypes.func,
|
||||||
|
/** 双击树节点触发 */
|
||||||
|
onDoubleclick: PropTypes.func,
|
||||||
|
onDblclick: PropTypes.func,
|
||||||
|
'onUpdate:selectedKeys': PropTypes.func,
|
||||||
|
'onUpdate:checkedKeys': PropTypes.func,
|
||||||
|
'onUpdate:expandedKeys': PropTypes.func,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export { TreeProps };
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'ATree',
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: initDefaultProps(TreeProps(), {
|
||||||
|
checkable: false,
|
||||||
|
showIcon: false,
|
||||||
|
openAnimation: {
|
||||||
|
...animation,
|
||||||
|
appear: null,
|
||||||
|
},
|
||||||
|
blockNode: false,
|
||||||
|
}),
|
||||||
|
setup() {
|
||||||
|
return {
|
||||||
|
tree: null,
|
||||||
|
configProvider: inject('configProvider', defaultConfigProvider),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
TreeNode,
|
||||||
|
methods: {
|
||||||
|
renderSwitcherIcon(prefixCls: string, switcherIcon: VNode, { isLeaf, loading, expanded }) {
|
||||||
|
const { showLine } = this.$props;
|
||||||
|
if (loading) {
|
||||||
|
return <LoadingOutlined class={`${prefixCls}-switcher-loading-icon`} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLeaf) {
|
||||||
|
return showLine ? <FileOutlined class={`${prefixCls}-switcher-line-icon`} /> : null;
|
||||||
|
}
|
||||||
|
const switcherCls = `${prefixCls}-switcher-icon`;
|
||||||
|
if (switcherIcon) {
|
||||||
|
return cloneElement(switcherIcon, {
|
||||||
|
class: switcherCls,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return showLine ? (
|
||||||
|
expanded ? (
|
||||||
|
<MinusSquareOutlined class={`${prefixCls}-switcher-line-icon`} />
|
||||||
|
) : (
|
||||||
|
<PlusSquareOutlined class={`${prefixCls}-switcher-line-icon`} />
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<CaretDownFilled class={switcherCls} />
|
||||||
|
);
|
||||||
|
},
|
||||||
|
updateTreeData(treeData: TreeDataItem[]) {
|
||||||
|
const { $slots } = this;
|
||||||
|
const defaultFields = { children: 'children', title: 'title', key: 'key' };
|
||||||
|
const replaceFields = { ...defaultFields, ...this.$props.replaceFields };
|
||||||
|
return treeData.map(item => {
|
||||||
|
const key = item[replaceFields.key];
|
||||||
|
const children = item[replaceFields.children];
|
||||||
|
const { slots = {}, class: cls, style, ...restProps } = item;
|
||||||
|
const treeNodeProps = {
|
||||||
|
...restProps,
|
||||||
|
icon: $slots[slots.icon] || restProps.icon,
|
||||||
|
switcherIcon: $slots[slots.switcherIcon] || restProps.switcherIcon,
|
||||||
|
title: $slots[slots.title] || $slots.title || restProps[replaceFields.title],
|
||||||
|
dataRef: item,
|
||||||
|
key,
|
||||||
|
class: cls,
|
||||||
|
style,
|
||||||
|
};
|
||||||
|
if (children) {
|
||||||
|
return { ...treeNodeProps, children: this.updateTreeData(children) };
|
||||||
|
}
|
||||||
|
return treeNodeProps;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setTreeRef(node: VNode) {
|
||||||
|
this.tree = node;
|
||||||
|
},
|
||||||
|
handleCheck(checkedObj: (number | string)[], eventObj: CheckEvent) {
|
||||||
|
this.$emit('update:checkedKeys', checkedObj);
|
||||||
|
this.$emit('check', checkedObj, eventObj);
|
||||||
|
},
|
||||||
|
handleExpand(expandedKeys: (number | string)[], eventObj: ExpendEvent) {
|
||||||
|
this.$emit('update:expandedKeys', expandedKeys);
|
||||||
|
this.$emit('expand', expandedKeys, eventObj);
|
||||||
|
},
|
||||||
|
handleSelect(selectedKeys: (number | string)[], eventObj: SelectEvent) {
|
||||||
|
this.$emit('update:selectedKeys', selectedKeys);
|
||||||
|
this.$emit('select', selectedKeys, eventObj);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
const props = getOptionProps(this);
|
||||||
|
const { prefixCls: customizePrefixCls, showIcon, treeNodes, blockNode } = props;
|
||||||
|
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||||
|
const prefixCls = getPrefixCls('tree', customizePrefixCls);
|
||||||
|
const switcherIcon = getComponent(this, 'switcherIcon');
|
||||||
|
const checkable = props.checkable;
|
||||||
|
let treeData = props.treeData || treeNodes;
|
||||||
|
if (treeData) {
|
||||||
|
treeData = this.updateTreeData(treeData);
|
||||||
|
}
|
||||||
|
const { class: className, ...restAttrs } = this.$attrs;
|
||||||
|
const vcTreeProps = {
|
||||||
|
...props,
|
||||||
|
prefixCls,
|
||||||
|
checkable: checkable ? <span class={`${prefixCls}-checkbox-inner`} /> : checkable,
|
||||||
|
children: getSlot(this),
|
||||||
|
switcherIcon: nodeProps => this.renderSwitcherIcon(prefixCls, switcherIcon, nodeProps),
|
||||||
|
ref: this.setTreeRef,
|
||||||
|
...restAttrs,
|
||||||
|
class: classNames(className, {
|
||||||
|
[`${prefixCls}-icon-hide`]: !showIcon,
|
||||||
|
[`${prefixCls}-block-node`]: blockNode,
|
||||||
|
}),
|
||||||
|
onCheck: this.handleCheck,
|
||||||
|
onExpand: this.handleExpand,
|
||||||
|
onSelect: this.handleSelect,
|
||||||
|
} as Record<string, any>;
|
||||||
|
if (treeData) {
|
||||||
|
vcTreeProps.treeData = treeData;
|
||||||
|
}
|
||||||
|
return <VcTree {...vcTreeProps} />;
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,290 +1,347 @@
|
||||||
import type { VNode, PropType, CSSProperties } from 'vue';
|
import { VNode, PropType, DefineComponent, ExtractPropTypes, ref } from 'vue';
|
||||||
import { defineComponent, inject } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import classNames from '../_util/classNames';
|
import classNames from '../_util/classNames';
|
||||||
import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined';
|
import VcTree, { TreeNode } from '../vc-tree';
|
||||||
import FileOutlined from '@ant-design/icons-vue/FileOutlined';
|
|
||||||
import CaretDownFilled from '@ant-design/icons-vue/CaretDownFilled';
|
|
||||||
import MinusSquareOutlined from '@ant-design/icons-vue/MinusSquareOutlined';
|
|
||||||
import PlusSquareOutlined from '@ant-design/icons-vue/PlusSquareOutlined';
|
|
||||||
import VcTree from '../vc-tree';
|
|
||||||
import animation from '../_util/openAnimation';
|
import animation from '../_util/openAnimation';
|
||||||
import PropTypes from '../_util/vue-types';
|
import PropTypes from '../_util/vue-types';
|
||||||
import { getOptionProps, getComponent, getSlot } from '../_util/props-util';
|
import { filterEmpty } from '../_util/props-util';
|
||||||
import initDefaultProps from '../_util/props-util/initDefaultProps';
|
import initDefaultProps from '../_util/props-util/initDefaultProps';
|
||||||
import { cloneElement } from '../_util/vnode';
|
import { DataNode, FieldNames, Key } from '../vc-tree/interface';
|
||||||
import { defaultConfigProvider } from '../config-provider';
|
import { treeProps as vcTreeProps } from '../vc-tree/props';
|
||||||
|
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||||
|
import renderSwitcherIcon from './utils/iconUtil';
|
||||||
|
import dropIndicatorRender from './utils/dropIndicator';
|
||||||
|
|
||||||
const TreeNode = VcTree.TreeNode;
|
export interface AntdTreeNodeAttribute {
|
||||||
|
eventKey: string;
|
||||||
export interface TreeDataItem {
|
prefixCls: string;
|
||||||
key?: string | number;
|
className: string;
|
||||||
title?: string;
|
|
||||||
isLeaf?: boolean;
|
|
||||||
selectable?: boolean;
|
|
||||||
children?: TreeDataItem[];
|
|
||||||
disableCheckbox?: boolean;
|
|
||||||
disabled?: boolean;
|
|
||||||
class?: string;
|
|
||||||
style?: CSSProperties;
|
|
||||||
checkable?: boolean;
|
|
||||||
icon?: VNode;
|
|
||||||
slots?: Record<string, string>;
|
|
||||||
switcherIcon?: VNode;
|
|
||||||
// support custom field
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DefaultEvent {
|
|
||||||
nativeEvent: MouseEvent;
|
|
||||||
node: Record<string, any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CheckEvent extends DefaultEvent {
|
|
||||||
checked: boolean;
|
|
||||||
checkedNodes: Array<Record<string, any>>;
|
|
||||||
checkedNodesPositions: { node: Record<string, any>; pos: string | number }[];
|
|
||||||
event: string;
|
|
||||||
halfCheckedKeys: (string | number)[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ExpendEvent extends DefaultEvent {
|
|
||||||
expanded: boolean;
|
expanded: boolean;
|
||||||
}
|
|
||||||
|
|
||||||
export interface SelectEvent extends DefaultEvent {
|
|
||||||
event: string;
|
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
selectedNodes: Array<Record<string, any>>;
|
checked: boolean;
|
||||||
|
halfChecked: boolean;
|
||||||
|
children: any;
|
||||||
|
title: any;
|
||||||
|
pos: string;
|
||||||
|
dragOver: boolean;
|
||||||
|
dragOverGapTop: boolean;
|
||||||
|
dragOverGapBottom: boolean;
|
||||||
|
isLeaf: boolean;
|
||||||
|
selectable: boolean;
|
||||||
|
disabled: boolean;
|
||||||
|
disableCheckbox: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TreeDragEvent {
|
export interface AntTreeNodeProps {
|
||||||
|
className?: string;
|
||||||
|
checkable?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
disableCheckbox?: boolean;
|
||||||
|
title?: string | any;
|
||||||
|
key?: Key;
|
||||||
|
eventKey?: string;
|
||||||
|
isLeaf?: boolean;
|
||||||
|
checked?: boolean;
|
||||||
|
expanded?: boolean;
|
||||||
|
loading?: boolean;
|
||||||
|
selected?: boolean;
|
||||||
|
selectable?: boolean;
|
||||||
|
icon?: ((treeNode: AntdTreeNodeAttribute) => any) | VNode;
|
||||||
|
children?: any;
|
||||||
|
[customProp: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AntTreeNode extends DefineComponent<AntTreeNodeProps, {}> {}
|
||||||
|
|
||||||
|
export interface AntTreeNodeBaseEvent {
|
||||||
|
node: AntTreeNode;
|
||||||
|
nativeEvent: MouseEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AntTreeNodeCheckedEvent extends AntTreeNodeBaseEvent {
|
||||||
|
event: 'check';
|
||||||
|
checked?: boolean;
|
||||||
|
checkedNodes?: AntTreeNode[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AntTreeNodeSelectedEvent extends AntTreeNodeBaseEvent {
|
||||||
|
event: 'select';
|
||||||
|
selected?: boolean;
|
||||||
|
selectedNodes?: DataNode[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AntTreeNodeExpandedEvent extends AntTreeNodeBaseEvent {
|
||||||
|
expanded?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AntTreeNodeMouseEvent {
|
||||||
|
node: AntTreeNode;
|
||||||
event: DragEvent;
|
event: DragEvent;
|
||||||
expandedKeys: (string | number)[];
|
|
||||||
node: Record<string, any>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DropEvent {
|
export interface AntTreeNodeDragEnterEvent extends AntTreeNodeMouseEvent {
|
||||||
dragNode: Record<string, any>;
|
expandedKeys: Key[];
|
||||||
dragNodesKeys: (string | number)[];
|
}
|
||||||
|
|
||||||
|
export interface AntTreeNodeDropEvent {
|
||||||
|
node: AntTreeNode;
|
||||||
|
dragNode: AntTreeNode;
|
||||||
|
dragNodesKeys: Key[];
|
||||||
dropPosition: number;
|
dropPosition: number;
|
||||||
dropToGap: boolean;
|
dropToGap?: boolean;
|
||||||
event: DragEvent;
|
event: MouseEvent;
|
||||||
node: Record<string, any>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function TreeProps() {
|
// [Legacy] Compatible for v2
|
||||||
|
export type TreeDataItem = DataNode;
|
||||||
|
|
||||||
|
export const treeProps = () => {
|
||||||
return {
|
return {
|
||||||
showLine: PropTypes.looseBool,
|
...vcTreeProps(),
|
||||||
|
showLine: { type: Boolean, default: undefined },
|
||||||
/** 是否支持多选 */
|
/** 是否支持多选 */
|
||||||
multiple: PropTypes.looseBool,
|
multiple: { type: Boolean, default: undefined },
|
||||||
/** 是否自动展开父节点 */
|
/** 是否自动展开父节点 */
|
||||||
autoExpandParent: PropTypes.looseBool,
|
autoExpandParent: { type: Boolean, default: undefined },
|
||||||
/** checkable状态下节点选择完全受控(父子节点选中状态不再关联)*/
|
/** checkable状态下节点选择完全受控(父子节点选中状态不再关联)*/
|
||||||
checkStrictly: PropTypes.looseBool,
|
checkStrictly: { type: Boolean, default: undefined },
|
||||||
/** 是否支持选中 */
|
/** 是否支持选中 */
|
||||||
checkable: PropTypes.looseBool,
|
checkable: { type: Boolean, default: undefined },
|
||||||
/** 是否禁用树 */
|
/** 是否禁用树 */
|
||||||
disabled: PropTypes.looseBool,
|
disabled: { type: Boolean, default: undefined },
|
||||||
/** 默认展开所有树节点 */
|
/** 默认展开所有树节点 */
|
||||||
defaultExpandAll: PropTypes.looseBool,
|
defaultExpandAll: { type: Boolean, default: undefined },
|
||||||
/** 默认展开对应树节点 */
|
/** 默认展开对应树节点 */
|
||||||
defaultExpandParent: PropTypes.looseBool,
|
defaultExpandParent: { type: Boolean, default: undefined },
|
||||||
/** 默认展开指定的树节点 */
|
/** 默认展开指定的树节点 */
|
||||||
defaultExpandedKeys: PropTypes.arrayOf(
|
defaultExpandedKeys: { type: Array as PropType<Key[]> },
|
||||||
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
||||||
),
|
|
||||||
/** (受控)展开指定的树节点 */
|
/** (受控)展开指定的树节点 */
|
||||||
expandedKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
expandedKeys: { type: Array as PropType<Key[]> },
|
||||||
/** (受控)选中复选框的树节点 */
|
/** (受控)选中复选框的树节点 */
|
||||||
checkedKeys: PropTypes.oneOfType([
|
checkedKeys: {
|
||||||
PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
type: [Array, Object] as PropType<Key[] | { checked: Key[]; halfChecked: Key[] }>,
|
||||||
PropTypes.shape({
|
},
|
||||||
checked: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
|
||||||
halfChecked: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
|
||||||
}).loose,
|
|
||||||
]),
|
|
||||||
/** 默认选中复选框的树节点 */
|
/** 默认选中复选框的树节点 */
|
||||||
defaultCheckedKeys: PropTypes.arrayOf(
|
defaultCheckedKeys: { type: Array as PropType<Key[]> },
|
||||||
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
||||||
),
|
|
||||||
/** (受控)设置选中的树节点 */
|
/** (受控)设置选中的树节点 */
|
||||||
selectedKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
selectedKeys: { type: Array as PropType<Key[]> },
|
||||||
/** 默认选中的树节点 */
|
/** 默认选中的树节点 */
|
||||||
defaultSelectedKeys: PropTypes.arrayOf(
|
defaultSelectedKeys: { type: Array as PropType<Key[]> },
|
||||||
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
selectable: { type: Boolean, default: undefined },
|
||||||
),
|
|
||||||
selectable: PropTypes.looseBool,
|
|
||||||
|
|
||||||
/** filter some AntTreeNodes as you need. it should return true */
|
/** filter some AntTreeNodes as you need. it should return true */
|
||||||
filterAntTreeNode: PropTypes.func,
|
filterAntTreeNode: { type: Function as PropType<(node: AntTreeNode) => boolean> },
|
||||||
/** 异步加载数据 */
|
loadedKeys: { type: Array as PropType<Key[]> },
|
||||||
loadData: PropTypes.func,
|
draggable: { type: Boolean, default: undefined },
|
||||||
loadedKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
showIcon: { type: Boolean, default: undefined },
|
||||||
// onLoaded: (loadedKeys: string[], info: { event: 'load', node: AntTreeNode; }) => void,
|
icon: { type: Function as PropType<(nodeProps: AntdTreeNodeAttribute) => any> },
|
||||||
/** 响应右键点击 */
|
|
||||||
// onRightClick: (options: AntTreeNodeMouseEvent) => void,
|
|
||||||
/** 设置节点可拖拽(IE>8)*/
|
|
||||||
draggable: PropTypes.looseBool,
|
|
||||||
// /** 开始拖拽时调用 */
|
|
||||||
// onDragStart: (options: AntTreeNodeMouseEvent) => void,
|
|
||||||
// /** dragenter 触发时调用 */
|
|
||||||
// onDragEnter: (options: AntTreeNodeMouseEvent) => void,
|
|
||||||
// /** dragover 触发时调用 */
|
|
||||||
// onDragOver: (options: AntTreeNodeMouseEvent) => void,
|
|
||||||
// /** dragleave 触发时调用 */
|
|
||||||
// onDragLeave: (options: AntTreeNodeMouseEvent) => void,
|
|
||||||
// /** drop 触发时调用 */
|
|
||||||
// onDrop: (options: AntTreeNodeMouseEvent) => void,
|
|
||||||
showIcon: PropTypes.looseBool,
|
|
||||||
icon: PropTypes.func,
|
|
||||||
switcherIcon: PropTypes.any,
|
switcherIcon: PropTypes.any,
|
||||||
prefixCls: PropTypes.string,
|
prefixCls: PropTypes.string,
|
||||||
filterTreeNode: PropTypes.func,
|
|
||||||
openAnimation: PropTypes.any,
|
|
||||||
treeData: {
|
|
||||||
type: Array as PropType<TreeDataItem[]>,
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* @default{title,key,children}
|
* @default{title,key,children}
|
||||||
|
* deprecated, please use `fieldNames` instead
|
||||||
* 替换treeNode中 title,key,children字段为treeData中对应的字段
|
* 替换treeNode中 title,key,children字段为treeData中对应的字段
|
||||||
*/
|
*/
|
||||||
replaceFields: PropTypes.object,
|
replaceFields: { type: Object as PropType<FieldNames> },
|
||||||
blockNode: PropTypes.looseBool,
|
blockNode: { type: Boolean, default: undefined },
|
||||||
/** 展开/收起节点时触发 */
|
|
||||||
onExpand: PropTypes.func,
|
|
||||||
/** 点击复选框触发 */
|
|
||||||
onCheck: PropTypes.func,
|
|
||||||
/** 点击树节点触发 */
|
|
||||||
onSelect: PropTypes.func,
|
|
||||||
/** 单击树节点触发 */
|
|
||||||
onClick: PropTypes.func,
|
|
||||||
/** 双击树节点触发 */
|
|
||||||
onDoubleclick: PropTypes.func,
|
|
||||||
onDblclick: PropTypes.func,
|
|
||||||
'onUpdate:selectedKeys': PropTypes.func,
|
|
||||||
'onUpdate:checkedKeys': PropTypes.func,
|
|
||||||
'onUpdate:expandedKeys': PropTypes.func,
|
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
export { TreeProps };
|
export type TreeProps = Partial<ExtractPropTypes<ReturnType<typeof treeProps>>>;
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'ATree',
|
name: 'ATree',
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: initDefaultProps(TreeProps(), {
|
props: initDefaultProps(treeProps(), {
|
||||||
checkable: false,
|
checkable: false,
|
||||||
|
selectable: true,
|
||||||
showIcon: false,
|
showIcon: false,
|
||||||
openAnimation: {
|
openAnimation: {
|
||||||
...animation,
|
...animation,
|
||||||
appear: null,
|
appear: false,
|
||||||
},
|
},
|
||||||
blockNode: false,
|
blockNode: false,
|
||||||
}),
|
}),
|
||||||
setup() {
|
slots: ['icon', 'title', 'switcherIcon'],
|
||||||
return {
|
emits: [
|
||||||
tree: null,
|
'update:selectedKeys',
|
||||||
configProvider: inject('configProvider', defaultConfigProvider),
|
'update:checkedKeys',
|
||||||
};
|
'update:expandedKeys',
|
||||||
},
|
'expand',
|
||||||
|
'select',
|
||||||
|
'check',
|
||||||
|
],
|
||||||
TreeNode,
|
TreeNode,
|
||||||
methods: {
|
setup(props, { attrs, expose, emit, slots }) {
|
||||||
renderSwitcherIcon(prefixCls: string, switcherIcon: VNode, { isLeaf, loading, expanded }) {
|
const { prefixCls, direction, virtual } = useConfigInject('tree', props);
|
||||||
const { showLine } = this.$props;
|
const tree = ref();
|
||||||
if (loading) {
|
expose({
|
||||||
return <LoadingOutlined class={`${prefixCls}-switcher-loading-icon`} />;
|
tree,
|
||||||
}
|
});
|
||||||
|
|
||||||
if (isLeaf) {
|
const handleCheck: TreeProps['onCheck'] = (checkedObjOrKeys, eventObj) => {
|
||||||
return showLine ? <FileOutlined class={`${prefixCls}-switcher-line-icon`} /> : null;
|
emit('update:checkedKeys', checkedObjOrKeys);
|
||||||
}
|
emit('check', checkedObjOrKeys, eventObj);
|
||||||
const switcherCls = `${prefixCls}-switcher-icon`;
|
|
||||||
if (switcherIcon) {
|
|
||||||
return cloneElement(switcherIcon, {
|
|
||||||
class: switcherCls,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return showLine ? (
|
|
||||||
expanded ? (
|
|
||||||
<MinusSquareOutlined class={`${prefixCls}-switcher-line-icon`} />
|
|
||||||
) : (
|
|
||||||
<PlusSquareOutlined class={`${prefixCls}-switcher-line-icon`} />
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<CaretDownFilled class={switcherCls} />
|
|
||||||
);
|
|
||||||
},
|
|
||||||
updateTreeData(treeData: TreeDataItem[]) {
|
|
||||||
const { $slots } = this;
|
|
||||||
const defaultFields = { children: 'children', title: 'title', key: 'key' };
|
|
||||||
const replaceFields = { ...defaultFields, ...this.$props.replaceFields };
|
|
||||||
return treeData.map(item => {
|
|
||||||
const key = item[replaceFields.key];
|
|
||||||
const children = item[replaceFields.children];
|
|
||||||
const { slots = {}, class: cls, style, ...restProps } = item;
|
|
||||||
const treeNodeProps = {
|
|
||||||
...restProps,
|
|
||||||
icon: $slots[slots.icon] || restProps.icon,
|
|
||||||
switcherIcon: $slots[slots.switcherIcon] || restProps.switcherIcon,
|
|
||||||
title: $slots[slots.title] || $slots.title || restProps[replaceFields.title],
|
|
||||||
dataRef: item,
|
|
||||||
key,
|
|
||||||
class: cls,
|
|
||||||
style,
|
|
||||||
};
|
};
|
||||||
if (children) {
|
const handleExpand: TreeProps['onExpand'] = (expandedKeys, eventObj) => {
|
||||||
return { ...treeNodeProps, children: this.updateTreeData(children) };
|
emit('update:expandedKeys', expandedKeys);
|
||||||
}
|
emit('expand', expandedKeys, eventObj);
|
||||||
return treeNodeProps;
|
};
|
||||||
});
|
const handleSelect: TreeProps['onSelect'] = (selectedKeys, eventObj) => {
|
||||||
},
|
emit('update:selectedKeys', selectedKeys);
|
||||||
setTreeRef(node: VNode) {
|
emit('select', selectedKeys, eventObj);
|
||||||
this.tree = node;
|
};
|
||||||
},
|
return () => {
|
||||||
handleCheck(checkedObj: (number | string)[], eventObj: CheckEvent) {
|
const {
|
||||||
this.$emit('update:checkedKeys', checkedObj);
|
showIcon,
|
||||||
this.$emit('check', checkedObj, eventObj);
|
showLine,
|
||||||
},
|
switcherIcon = slots.switcherIcon?.(),
|
||||||
handleExpand(expandedKeys: (number | string)[], eventObj: ExpendEvent) {
|
icon = slots.icon,
|
||||||
this.$emit('update:expandedKeys', expandedKeys);
|
blockNode,
|
||||||
this.$emit('expand', expandedKeys, eventObj);
|
checkable,
|
||||||
},
|
selectable,
|
||||||
handleSelect(selectedKeys: (number | string)[], eventObj: SelectEvent) {
|
fieldNames,
|
||||||
this.$emit('update:selectedKeys', selectedKeys);
|
replaceFields,
|
||||||
this.$emit('select', selectedKeys, eventObj);
|
} = props;
|
||||||
},
|
const newProps = {
|
||||||
},
|
...attrs,
|
||||||
render() {
|
|
||||||
const props = getOptionProps(this);
|
|
||||||
const { prefixCls: customizePrefixCls, showIcon, treeNodes, blockNode } = props;
|
|
||||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
|
||||||
const prefixCls = getPrefixCls('tree', customizePrefixCls);
|
|
||||||
const switcherIcon = getComponent(this, 'switcherIcon');
|
|
||||||
const checkable = props.checkable;
|
|
||||||
let treeData = props.treeData || treeNodes;
|
|
||||||
if (treeData) {
|
|
||||||
treeData = this.updateTreeData(treeData);
|
|
||||||
}
|
|
||||||
const { class: className, ...restAttrs } = this.$attrs;
|
|
||||||
const vcTreeProps = {
|
|
||||||
...props,
|
...props,
|
||||||
prefixCls,
|
showLine: Boolean(showLine),
|
||||||
checkable: checkable ? <span class={`${prefixCls}-checkbox-inner`} /> : checkable,
|
dropIndicatorRender,
|
||||||
children: getSlot(this),
|
fieldNames: fieldNames || (replaceFields as FieldNames),
|
||||||
switcherIcon: nodeProps => this.renderSwitcherIcon(prefixCls, switcherIcon, nodeProps),
|
icon,
|
||||||
ref: this.setTreeRef,
|
};
|
||||||
...restAttrs,
|
|
||||||
class: classNames(className, {
|
return (
|
||||||
[`${prefixCls}-icon-hide`]: !showIcon,
|
<VcTree
|
||||||
[`${prefixCls}-block-node`]: blockNode,
|
itemHeight={20}
|
||||||
}),
|
virtual={virtual.value}
|
||||||
onCheck: this.handleCheck,
|
{...newProps}
|
||||||
onExpand: this.handleExpand,
|
ref={tree}
|
||||||
onSelect: this.handleSelect,
|
prefixCls={prefixCls.value}
|
||||||
} as Record<string, any>;
|
class={classNames(
|
||||||
if (treeData) {
|
{
|
||||||
vcTreeProps.treeData = treeData;
|
[`${prefixCls.value}-icon-hide`]: !showIcon,
|
||||||
}
|
[`${prefixCls.value}-block-node`]: blockNode,
|
||||||
return <VcTree {...vcTreeProps} __propsSymbol__={[]} />;
|
[`${prefixCls.value}-unselectable`]: !selectable,
|
||||||
|
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
|
||||||
},
|
},
|
||||||
|
attrs.class,
|
||||||
|
)}
|
||||||
|
direction={direction.value}
|
||||||
|
checkable={checkable}
|
||||||
|
selectable={selectable}
|
||||||
|
switcherIcon={(nodeProps: AntTreeNodeProps) =>
|
||||||
|
renderSwitcherIcon(prefixCls.value, switcherIcon, showLine, nodeProps)
|
||||||
|
}
|
||||||
|
onCheck={handleCheck}
|
||||||
|
onExpand={handleExpand}
|
||||||
|
onSelect={handleSelect}
|
||||||
|
v-slots={{
|
||||||
|
checkable: () => <span class={`${prefixCls.value}-checkbox-inner`} />,
|
||||||
|
}}
|
||||||
|
children={filterEmpty(slots.default?.())}
|
||||||
|
></VcTree>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
// methods: {
|
||||||
|
// renderSwitcherIcon(prefixCls: string, switcherIcon: VNode, { isLeaf, loading, expanded }) {
|
||||||
|
// const { showLine } = this.$props;
|
||||||
|
// if (loading) {
|
||||||
|
// return <LoadingOutlined class={`${prefixCls}-switcher-loading-icon`} />;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (isLeaf) {
|
||||||
|
// return showLine ? <FileOutlined class={`${prefixCls}-switcher-line-icon`} /> : null;
|
||||||
|
// }
|
||||||
|
// const switcherCls = `${prefixCls}-switcher-icon`;
|
||||||
|
// if (switcherIcon) {
|
||||||
|
// return cloneElement(switcherIcon, {
|
||||||
|
// class: switcherCls,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// return showLine ? (
|
||||||
|
// expanded ? (
|
||||||
|
// <MinusSquareOutlined class={`${prefixCls}-switcher-line-icon`} />
|
||||||
|
// ) : (
|
||||||
|
// <PlusSquareOutlined class={`${prefixCls}-switcher-line-icon`} />
|
||||||
|
// )
|
||||||
|
// ) : (
|
||||||
|
// <CaretDownFilled class={switcherCls} />
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// updateTreeData(treeData: TreeDataItem[]) {
|
||||||
|
// const { $slots } = this;
|
||||||
|
// const defaultFields = { children: 'children', title: 'title', key: 'key' };
|
||||||
|
// const replaceFields = { ...defaultFields, ...this.$props.replaceFields };
|
||||||
|
// return treeData.map(item => {
|
||||||
|
// const key = item[replaceFields.key];
|
||||||
|
// const children = item[replaceFields.children];
|
||||||
|
// const { slots = {}, class: cls, style, ...restProps } = item;
|
||||||
|
// const treeNodeProps = {
|
||||||
|
// ...restProps,
|
||||||
|
// icon: $slots[slots.icon] || restProps.icon,
|
||||||
|
// switcherIcon: $slots[slots.switcherIcon] || restProps.switcherIcon,
|
||||||
|
// title: $slots[slots.title] || $slots.title || restProps[replaceFields.title],
|
||||||
|
// dataRef: item,
|
||||||
|
// key,
|
||||||
|
// class: cls,
|
||||||
|
// style,
|
||||||
|
// };
|
||||||
|
// if (children) {
|
||||||
|
// return { ...treeNodeProps, children: this.updateTreeData(children) };
|
||||||
|
// }
|
||||||
|
// return treeNodeProps;
|
||||||
|
// });
|
||||||
|
// },
|
||||||
|
// setTreeRef(node: VNode) {
|
||||||
|
// this.tree = node;
|
||||||
|
// },
|
||||||
|
// handleCheck(checkedObj: (number | string)[], eventObj: CheckEvent) {
|
||||||
|
// this.$emit('update:checkedKeys', checkedObj);
|
||||||
|
// this.$emit('check', checkedObj, eventObj);
|
||||||
|
// },
|
||||||
|
// handleExpand(expandedKeys: (number | string)[], eventObj: ExpendEvent) {
|
||||||
|
// this.$emit('update:expandedKeys', expandedKeys);
|
||||||
|
// this.$emit('expand', expandedKeys, eventObj);
|
||||||
|
// },
|
||||||
|
// handleSelect(selectedKeys: (number | string)[], eventObj: SelectEvent) {
|
||||||
|
// this.$emit('update:selectedKeys', selectedKeys);
|
||||||
|
// this.$emit('select', selectedKeys, eventObj);
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// render() {
|
||||||
|
// const props = getOptionProps(this);
|
||||||
|
// const { prefixCls: customizePrefixCls, showIcon, treeNodes, blockNode } = props;
|
||||||
|
// const getPrefixCls = this.configProvider.getPrefixCls;
|
||||||
|
// const prefixCls = getPrefixCls('tree', customizePrefixCls);
|
||||||
|
// const switcherIcon = getComponent(this, 'switcherIcon');
|
||||||
|
// const checkable = props.checkable;
|
||||||
|
// let treeData = props.treeData || treeNodes;
|
||||||
|
// if (treeData) {
|
||||||
|
// treeData = this.updateTreeData(treeData);
|
||||||
|
// }
|
||||||
|
// const { class: className, ...restAttrs } = this.$attrs;
|
||||||
|
// const vcTreeProps = {
|
||||||
|
// ...props,
|
||||||
|
// prefixCls,
|
||||||
|
// checkable: checkable ? <span class={`${prefixCls}-checkbox-inner`} /> : checkable,
|
||||||
|
// children: getSlot(this),
|
||||||
|
// switcherIcon: nodeProps => this.renderSwitcherIcon(prefixCls, switcherIcon, nodeProps),
|
||||||
|
// ref: this.setTreeRef,
|
||||||
|
// ...restAttrs,
|
||||||
|
// class: classNames(className, {
|
||||||
|
// [`${prefixCls}-icon-hide`]: !showIcon,
|
||||||
|
// [`${prefixCls}-block-node`]: blockNode,
|
||||||
|
// }),
|
||||||
|
// onCheck: this.handleCheck,
|
||||||
|
// onExpand: this.handleExpand,
|
||||||
|
// onSelect: this.handleSelect,
|
||||||
|
// } as Record<string, any>;
|
||||||
|
// if (treeData) {
|
||||||
|
// vcTreeProps.treeData = treeData;
|
||||||
|
// }
|
||||||
|
// return <VcTree {...vcTreeProps} />;
|
||||||
|
// },
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,6 +2,21 @@ import type { App, Plugin } from 'vue';
|
||||||
import Tree from './Tree';
|
import Tree from './Tree';
|
||||||
import DirectoryTree from './DirectoryTree';
|
import DirectoryTree from './DirectoryTree';
|
||||||
|
|
||||||
|
export { EventDataNode, DataNode } from '../vc-tree/interface';
|
||||||
|
|
||||||
|
export {
|
||||||
|
TreeProps,
|
||||||
|
AntTreeNode,
|
||||||
|
AntTreeNodeMouseEvent,
|
||||||
|
AntTreeNodeExpandedEvent,
|
||||||
|
AntTreeNodeCheckedEvent,
|
||||||
|
AntTreeNodeSelectedEvent,
|
||||||
|
AntdTreeNodeAttribute,
|
||||||
|
AntTreeNodeProps,
|
||||||
|
} from './Tree';
|
||||||
|
|
||||||
|
export { ExpandAction as DirectoryTreeExpandAction, DirectoryTreeProps } from './DirectoryTree';
|
||||||
|
|
||||||
Tree.TreeNode.name = 'ATreeNode';
|
Tree.TreeNode.name = 'ATreeNode';
|
||||||
Tree.DirectoryTree = DirectoryTree;
|
Tree.DirectoryTree = DirectoryTree;
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
|
|
|
@ -2,93 +2,70 @@
|
||||||
|
|
||||||
@tree-prefix-cls: ~'@{ant-prefix}-tree';
|
@tree-prefix-cls: ~'@{ant-prefix}-tree';
|
||||||
|
|
||||||
.@{tree-prefix-cls} {
|
.@{tree-prefix-cls}.@{tree-prefix-cls}-directory {
|
||||||
&.@{tree-prefix-cls}-directory {
|
// ================== TreeNode ==================
|
||||||
|
.@{tree-prefix-cls}-treenode {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
// Stretch selector width
|
// Hover color
|
||||||
> li,
|
&::before {
|
||||||
.@{tree-prefix-cls}-child-tree > li {
|
position: absolute;
|
||||||
span {
|
top: 0;
|
||||||
&.@{tree-prefix-cls}-switcher {
|
right: 0;
|
||||||
position: relative;
|
bottom: 4px;
|
||||||
z-index: 1;
|
left: 0;
|
||||||
|
transition: background-color 0.3s;
|
||||||
&.@{tree-prefix-cls}-switcher-noop {
|
content: '';
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
&::before {
|
||||||
|
background: @item-hover-bg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.@{tree-prefix-cls}-checkbox {
|
// Elements
|
||||||
position: relative;
|
> * {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.@{tree-prefix-cls}-node-content-wrapper {
|
// >>> Switcher
|
||||||
|
.@{tree-prefix-cls}-switcher {
|
||||||
|
transition: color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
// >>> Title
|
||||||
|
.@{tree-prefix-cls}-node-content-wrapper {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|
||||||
&::before {
|
|
||||||
background: @item-hover-bg;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.@{tree-prefix-cls}-node-selected {
|
&.@{tree-prefix-cls}-node-selected {
|
||||||
color: @tree-directory-selected-color;
|
color: @tree-directory-selected-color;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::before {
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
left: 0;
|
|
||||||
height: @tree-title-height;
|
|
||||||
transition: all 0.3s;
|
|
||||||
content: '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
> span {
|
// ============= Selected =============
|
||||||
position: relative;
|
&-selected {
|
||||||
z-index: 1;
|
&:hover::before,
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.@{tree-prefix-cls}-treenode-selected {
|
|
||||||
> span {
|
|
||||||
&.@{tree-prefix-cls}-switcher {
|
|
||||||
color: @tree-directory-selected-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.@{tree-prefix-cls}-checkbox {
|
|
||||||
.@{tree-prefix-cls}-checkbox-inner {
|
|
||||||
border-color: @primary-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.@{tree-prefix-cls}-checkbox-checked {
|
|
||||||
&::after {
|
|
||||||
border-color: @checkbox-check-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.@{tree-prefix-cls}-checkbox-inner {
|
|
||||||
background: @checkbox-check-color;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
border-color: @primary-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.@{tree-prefix-cls}-node-content-wrapper {
|
|
||||||
&::before {
|
&::before {
|
||||||
background: @tree-directory-selected-bg;
|
background: @tree-directory-selected-bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// >>> Switcher
|
||||||
|
.@{tree-prefix-cls}-switcher {
|
||||||
|
color: @tree-directory-selected-color;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// >>> Title
|
||||||
|
.@{tree-prefix-cls}-node-content-wrapper {
|
||||||
|
color: @tree-directory-selected-color;
|
||||||
|
background: transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,276 +5,12 @@
|
||||||
@import './directory';
|
@import './directory';
|
||||||
|
|
||||||
@tree-prefix-cls: ~'@{ant-prefix}-tree';
|
@tree-prefix-cls: ~'@{ant-prefix}-tree';
|
||||||
@tree-showline-icon-color: @text-color-secondary;
|
@tree-node-prefix-cls: ~'@{tree-prefix-cls}-treenode';
|
||||||
@tree-node-padding: 4px;
|
|
||||||
|
|
||||||
.antCheckboxFn(@checkbox-prefix-cls: ~'@{ant-prefix}-tree-checkbox');
|
.antCheckboxFn(@checkbox-prefix-cls: ~'@{ant-prefix}-tree-checkbox');
|
||||||
|
|
||||||
.@{tree-prefix-cls} {
|
.@{tree-prefix-cls} {
|
||||||
/* see https://github.com/ant-design/ant-design/issues/16259 */
|
.antTreeFn(@tree-prefix-cls);
|
||||||
&-checkbox-checked::after {
|
|
||||||
position: absolute;
|
|
||||||
top: 16.67%;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 66.67%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reset-component();
|
|
||||||
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
ol,
|
|
||||||
ul {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin: 0;
|
|
||||||
padding: @tree-node-padding 0;
|
|
||||||
white-space: nowrap;
|
|
||||||
list-style: none;
|
|
||||||
outline: 0;
|
|
||||||
span[draggable],
|
|
||||||
span[draggable='true'] {
|
|
||||||
line-height: @tree-title-height - 4px;
|
|
||||||
border-top: 2px transparent solid;
|
|
||||||
border-bottom: 2px transparent solid;
|
|
||||||
user-select: none;
|
|
||||||
/* Required to make elements draggable in old WebKit */
|
|
||||||
-khtml-user-drag: element;
|
|
||||||
-webkit-user-drag: element;
|
|
||||||
}
|
|
||||||
&.drag-over {
|
|
||||||
> span[draggable] {
|
|
||||||
color: white;
|
|
||||||
background-color: @primary-color;
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.drag-over-gap-top {
|
|
||||||
> span[draggable] {
|
|
||||||
border-top-color: @primary-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.drag-over-gap-bottom {
|
|
||||||
> span[draggable] {
|
|
||||||
border-bottom-color: @primary-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.filter-node {
|
|
||||||
> span {
|
|
||||||
color: @highlight-color !important;
|
|
||||||
font-weight: 500 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// When node is loading
|
|
||||||
&.@{tree-prefix-cls}-treenode-loading {
|
|
||||||
span {
|
|
||||||
&.@{tree-prefix-cls}-switcher {
|
|
||||||
&.@{tree-prefix-cls}-switcher_open,
|
|
||||||
&.@{tree-prefix-cls}-switcher_close {
|
|
||||||
.@{tree-prefix-cls}-switcher-loading-icon {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
display: inline-block;
|
|
||||||
width: 24px;
|
|
||||||
height: @tree-title-height;
|
|
||||||
color: @primary-color;
|
|
||||||
font-size: 14px;
|
|
||||||
transform: none;
|
|
||||||
svg {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:root &::after {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0 0 0 @tree-child-padding;
|
|
||||||
}
|
|
||||||
.@{tree-prefix-cls}-node-content-wrapper {
|
|
||||||
display: inline-block;
|
|
||||||
height: @tree-title-height;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0 5px;
|
|
||||||
color: @text-color;
|
|
||||||
line-height: @tree-title-height;
|
|
||||||
text-decoration: none;
|
|
||||||
vertical-align: top;
|
|
||||||
border-radius: @border-radius-sm;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s;
|
|
||||||
&:hover {
|
|
||||||
background-color: @tree-node-hover-bg;
|
|
||||||
}
|
|
||||||
&.@{tree-prefix-cls}-node-selected {
|
|
||||||
background-color: @tree-node-selected-bg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
span {
|
|
||||||
&.@{tree-prefix-cls}-checkbox {
|
|
||||||
top: initial;
|
|
||||||
height: @tree-title-height;
|
|
||||||
margin: 0 4px 0 2px;
|
|
||||||
padding: ((@tree-title-height - 16px) / 2) 0;
|
|
||||||
}
|
|
||||||
&.@{tree-prefix-cls}-switcher,
|
|
||||||
&.@{tree-prefix-cls}-iconEle {
|
|
||||||
display: inline-block;
|
|
||||||
width: 24px;
|
|
||||||
height: @tree-title-height;
|
|
||||||
margin: 0;
|
|
||||||
line-height: @tree-title-height;
|
|
||||||
text-align: center;
|
|
||||||
vertical-align: top;
|
|
||||||
border: 0 none;
|
|
||||||
outline: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.@{tree-prefix-cls}-iconEle:empty {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.@{tree-prefix-cls}-switcher {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&.@{tree-prefix-cls}-switcher-noop {
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
&.@{tree-prefix-cls}-switcher_open {
|
|
||||||
.antTreeSwitcherIcon();
|
|
||||||
}
|
|
||||||
&.@{tree-prefix-cls}-switcher_close {
|
|
||||||
.antTreeSwitcherIcon();
|
|
||||||
.@{tree-prefix-cls}-switcher-icon {
|
|
||||||
svg {
|
|
||||||
transform: rotate(-90deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:last-child > span {
|
|
||||||
&.@{tree-prefix-cls}-switcher,
|
|
||||||
&.@{tree-prefix-cls}-iconEle {
|
|
||||||
&::before {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> li {
|
|
||||||
&:first-child {
|
|
||||||
padding-top: 7px;
|
|
||||||
}
|
|
||||||
&:last-child {
|
|
||||||
padding-bottom: 7px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&-child-tree {
|
|
||||||
// https://github.com/ant-design/ant-design/issues/14958
|
|
||||||
> li {
|
|
||||||
// Provide additional padding between top child node and parent node
|
|
||||||
&:first-child {
|
|
||||||
padding-top: 2 * @tree-node-padding;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hide additional padding between last child node and next parent node
|
|
||||||
&:last-child {
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
li&-treenode-disabled {
|
|
||||||
> span:not(.@{tree-prefix-cls}-switcher),
|
|
||||||
> .@{tree-prefix-cls}-node-content-wrapper,
|
|
||||||
> .@{tree-prefix-cls}-node-content-wrapper span {
|
|
||||||
color: @disabled-color;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
> .@{tree-prefix-cls}-node-content-wrapper:hover {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&-icon__open {
|
|
||||||
margin-right: 2px;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
&-icon__close {
|
|
||||||
margin-right: 2px;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
// Tree with line
|
|
||||||
&&-show-line {
|
|
||||||
li {
|
|
||||||
position: relative;
|
|
||||||
span {
|
|
||||||
&.@{tree-prefix-cls}-switcher {
|
|
||||||
color: @tree-showline-icon-color;
|
|
||||||
background: @component-background;
|
|
||||||
&.@{tree-prefix-cls}-switcher-noop {
|
|
||||||
.antTreeShowLineIcon('tree-doc-icon');
|
|
||||||
}
|
|
||||||
&.@{tree-prefix-cls}-switcher_open {
|
|
||||||
.antTreeShowLineIcon('tree-showline-open-icon');
|
|
||||||
}
|
|
||||||
&.@{tree-prefix-cls}-switcher_close {
|
|
||||||
.antTreeShowLineIcon('tree-showline-close-icon');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
li:not(:last-child)::before {
|
|
||||||
position: absolute;
|
|
||||||
left: 12px;
|
|
||||||
width: 1px;
|
|
||||||
height: 100%;
|
|
||||||
height: calc(100% - 22px); // Remove additional height if support
|
|
||||||
margin: 22px 0 0;
|
|
||||||
border-left: 1px solid @border-color-base;
|
|
||||||
content: ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.@{tree-prefix-cls}-icon-hide {
|
|
||||||
.@{tree-prefix-cls}-treenode-loading {
|
|
||||||
.@{tree-prefix-cls}-iconEle {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.@{tree-prefix-cls}-block-node {
|
|
||||||
li {
|
|
||||||
.@{tree-prefix-cls}-node-content-wrapper {
|
|
||||||
width: ~'calc(100% - 24px)';
|
|
||||||
}
|
|
||||||
span {
|
|
||||||
&.@{tree-prefix-cls}-checkbox {
|
|
||||||
+ .@{tree-prefix-cls}-node-content-wrapper {
|
|
||||||
width: ~'calc(100% - 46px)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@import './rtl';
|
||||||
|
|
|
@ -1,29 +1,274 @@
|
||||||
@import '../../style/mixins/index';
|
@import '../../style/mixins/index';
|
||||||
|
|
||||||
@tree-prefix-cls: ~'@{ant-prefix}-tree';
|
@tree-prefix-cls: ~'@{ant-prefix}-tree';
|
||||||
@tree-select-prefix-cls: ~'@{ant-prefix}-select';
|
@tree-node-prefix-cls: ~'@{tree-prefix-cls}-treenode';
|
||||||
|
@select-tree-prefix-cls: ~'@{ant-prefix}-select-tree';
|
||||||
|
@tree-motion: ~'@{ant-prefix}-motion-collapse';
|
||||||
|
@tree-node-padding: (@padding-xs / 2);
|
||||||
|
@tree-node-hightlight-color: inherit;
|
||||||
|
|
||||||
.antTreeSwitcherIcon(@type: 'tree-default-open-icon') {
|
.antTreeSwitcherIcon(@type: 'tree-default-open-icon') {
|
||||||
.@{tree-prefix-cls}-switcher-icon,
|
.@{tree-prefix-cls}-switcher-icon,
|
||||||
.@{tree-select-prefix-cls}-switcher-icon {
|
.@{select-tree-prefix-cls}-switcher-icon {
|
||||||
.iconfont-size-under-12px(10px);
|
|
||||||
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-weight: bold;
|
font-size: 10px;
|
||||||
|
vertical-align: baseline;
|
||||||
svg {
|
svg {
|
||||||
transition: transform 0.3s;
|
transition: transform 0.3s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.antTreeShowLineIcon(@type) {
|
.drop-indicator() {
|
||||||
.@{tree-prefix-cls}-switcher-icon,
|
.@{tree-prefix-cls}-drop-indicator {
|
||||||
.@{tree-select-prefix-cls}-switcher-icon {
|
position: absolute;
|
||||||
display: inline-block;
|
// it should displayed over the following node
|
||||||
font-weight: normal;
|
z-index: 1;
|
||||||
font-size: 12px;
|
height: 2px;
|
||||||
svg {
|
background-color: @primary-color;
|
||||||
transition: transform 0.3s;
|
border-radius: 1px;
|
||||||
|
pointer-events: none;
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
top: -3px;
|
||||||
|
left: -6px;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
background-color: transparent;
|
||||||
|
border: 2px solid @primary-color;
|
||||||
|
border-radius: 50%;
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.antTreeFn(@custom-tree-prefix-cls) {
|
||||||
|
@custom-tree-node-prefix-cls: ~'@{custom-tree-prefix-cls}-treenode';
|
||||||
|
.reset-component();
|
||||||
|
background: @tree-bg;
|
||||||
|
border-radius: @border-radius-base;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
|
||||||
|
&-focused:not(:hover):not(&-active-focused) {
|
||||||
|
background: @primary-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================== Virtual List ===================
|
||||||
|
&-list-holder-inner {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.@{custom-tree-prefix-cls}-block-node {
|
||||||
|
.@{custom-tree-prefix-cls}-list-holder-inner {
|
||||||
|
align-items: stretch;
|
||||||
|
|
||||||
|
// >>> Title
|
||||||
|
.@{custom-tree-prefix-cls}-node-content-wrapper {
|
||||||
|
flex: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================== TreeNode =====================
|
||||||
|
.@{custom-tree-node-prefix-cls} {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 0 0 @tree-node-padding 0;
|
||||||
|
outline: none;
|
||||||
|
// Disabled
|
||||||
|
&-disabled {
|
||||||
|
// >>> Title
|
||||||
|
.@{custom-tree-prefix-cls}-node-content-wrapper {
|
||||||
|
color: @disabled-color;
|
||||||
|
cursor: not-allowed;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-active .@{custom-tree-prefix-cls}-node-content-wrapper {
|
||||||
|
background: @tree-node-hover-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(&-disabled).filter-node .@{custom-tree-prefix-cls}-title {
|
||||||
|
color: @tree-node-hightlight-color;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// >>> Indent
|
||||||
|
&-indent {
|
||||||
|
align-self: stretch;
|
||||||
|
white-space: nowrap;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
&-unit {
|
||||||
|
display: inline-block;
|
||||||
|
width: @tree-title-height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// >>> Switcher
|
||||||
|
&-switcher {
|
||||||
|
.antTreeSwitcherIcon();
|
||||||
|
position: relative;
|
||||||
|
flex: none;
|
||||||
|
align-self: stretch;
|
||||||
|
width: @tree-title-height;
|
||||||
|
margin: 0;
|
||||||
|
line-height: @tree-title-height;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
&-noop {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
&_close {
|
||||||
|
.@{custom-tree-prefix-cls}-switcher-icon {
|
||||||
|
svg {
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-loading-icon {
|
||||||
|
color: @primary-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-leaf-line {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: -@tree-node-padding;
|
||||||
|
margin-left: -1px;
|
||||||
|
border-left: 1px solid @normal-color;
|
||||||
|
content: ' ';
|
||||||
|
}
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
width: @tree-title-height - 14px;
|
||||||
|
height: @tree-title-height - 10px;
|
||||||
|
margin-left: -1px;
|
||||||
|
border-bottom: 1px solid @normal-color;
|
||||||
|
content: ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// >>> Checkbox
|
||||||
|
&-checkbox {
|
||||||
|
top: initial;
|
||||||
|
margin: ((@tree-title-height - @checkbox-size) / 2) 8px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// >>> Title
|
||||||
|
& &-node-content-wrapper {
|
||||||
|
position: relative;
|
||||||
|
z-index: auto;
|
||||||
|
min-height: @tree-title-height;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 4px;
|
||||||
|
color: inherit;
|
||||||
|
line-height: @tree-title-height;
|
||||||
|
background: transparent;
|
||||||
|
border-radius: @border-radius-base;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s, border 0s, line-height 0s, box-shadow 0s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: @tree-node-hover-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.@{custom-tree-prefix-cls}-node-selected {
|
||||||
|
background-color: @tree-node-selected-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Icon
|
||||||
|
.@{custom-tree-prefix-cls}-iconEle {
|
||||||
|
display: inline-block;
|
||||||
|
width: @tree-title-height;
|
||||||
|
height: @tree-title-height;
|
||||||
|
line-height: @tree-title-height;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: top;
|
||||||
|
&:empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/ant-design/ant-design/issues/28217
|
||||||
|
&-unselectable &-node-content-wrapper:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== Draggable =====================
|
||||||
|
&-node-content-wrapper[draggable='true'] {
|
||||||
|
line-height: @tree-title-height;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
.drop-indicator();
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{custom-tree-node-prefix-cls}.drop-container {
|
||||||
|
> [draggable] {
|
||||||
|
box-shadow: 0 0 0 2px @primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== Show Line =====================
|
||||||
|
&-show-line {
|
||||||
|
// ================ Indent lines ================
|
||||||
|
.@{custom-tree-prefix-cls}-indent {
|
||||||
|
&-unit {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: (@tree-title-height / 2);
|
||||||
|
bottom: -@tree-node-padding;
|
||||||
|
border-right: 1px solid @border-color-base;
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
&-end {
|
||||||
|
&::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============== Cover Background ==============
|
||||||
|
.@{custom-tree-prefix-cls}-switcher {
|
||||||
|
background: @component-background;
|
||||||
|
|
||||||
|
&-line-icon {
|
||||||
|
vertical-align: -0.225em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{tree-node-prefix-cls}-leaf-last {
|
||||||
|
.@{tree-prefix-cls}-switcher {
|
||||||
|
&-leaf-line {
|
||||||
|
&::before {
|
||||||
|
top: auto !important;
|
||||||
|
bottom: auto !important;
|
||||||
|
height: @tree-title-height - 10px !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
@import '../../style/themes/index';
|
||||||
|
@import '../../style/mixins/index';
|
||||||
|
@import '../../checkbox/style/mixin';
|
||||||
|
|
||||||
|
@tree-prefix-cls: ~'@{ant-prefix}-tree';
|
||||||
|
@select-tree-prefix-cls: ~'@{ant-prefix}-select-tree';
|
||||||
|
@tree-node-prefix-cls: ~'@{tree-prefix-cls}-treenode';
|
||||||
|
|
||||||
|
.@{tree-prefix-cls} {
|
||||||
|
&-rtl {
|
||||||
|
direction: rtl;
|
||||||
|
.@{tree-prefix-cls}-node-content-wrapper[draggable='true'] {
|
||||||
|
.@{tree-prefix-cls}-drop-indicator {
|
||||||
|
&::after {
|
||||||
|
right: -6px;
|
||||||
|
left: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================== TreeNode =====================
|
||||||
|
.@{tree-node-prefix-cls} {
|
||||||
|
&-rtl {
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// >>> Switcher
|
||||||
|
&-switcher {
|
||||||
|
&_close {
|
||||||
|
.@{tree-prefix-cls}-switcher-icon {
|
||||||
|
svg {
|
||||||
|
.@{tree-prefix-cls}-rtl & {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ==================== Show Line =====================
|
||||||
|
&-show-line {
|
||||||
|
// ================ Indent lines ================
|
||||||
|
.@{tree-prefix-cls}-indent {
|
||||||
|
&-unit {
|
||||||
|
&::before {
|
||||||
|
.@{tree-prefix-cls}-rtl& {
|
||||||
|
right: auto;
|
||||||
|
left: -(@tree-title-height / 2) - 1px;
|
||||||
|
border-right: none;
|
||||||
|
border-left: 1px solid @border-color-base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// >>> Checkbox
|
||||||
|
&-checkbox {
|
||||||
|
.@{tree-prefix-cls}-rtl& {
|
||||||
|
margin: ((@tree-title-height - @checkbox-size) / 2) 0 0 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{select-tree-prefix-cls} {
|
||||||
|
// >>> Checkbox
|
||||||
|
&-checkbox {
|
||||||
|
.@{tree-prefix-cls}-select-dropdown-rtl & {
|
||||||
|
margin: ((@tree-title-height - @checkbox-size) / 2) 0 0 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
import type { DataNode, Key } from 'ant-design-vue/es/vc-tree/interface';
|
||||||
|
|
||||||
|
enum Record {
|
||||||
|
None,
|
||||||
|
Start,
|
||||||
|
End,
|
||||||
|
}
|
||||||
|
|
||||||
|
function traverseNodesKey(
|
||||||
|
treeData: DataNode[],
|
||||||
|
callback: (key: Key | number | null, node: DataNode) => boolean,
|
||||||
|
) {
|
||||||
|
function processNode(dataNode: DataNode) {
|
||||||
|
const { key, children } = dataNode;
|
||||||
|
if (callback(key, dataNode) !== false) {
|
||||||
|
traverseNodesKey(children || [], callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
treeData.forEach(processNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 计算选中范围,只考虑expanded情况以优化性能 */
|
||||||
|
export function calcRangeKeys({
|
||||||
|
treeData,
|
||||||
|
expandedKeys,
|
||||||
|
startKey,
|
||||||
|
endKey,
|
||||||
|
}: {
|
||||||
|
treeData: DataNode[];
|
||||||
|
expandedKeys: Key[];
|
||||||
|
startKey?: Key;
|
||||||
|
endKey?: Key;
|
||||||
|
}): Key[] {
|
||||||
|
const keys: Key[] = [];
|
||||||
|
let record: Record = Record.None;
|
||||||
|
|
||||||
|
if (startKey && startKey === endKey) {
|
||||||
|
return [startKey];
|
||||||
|
}
|
||||||
|
if (!startKey || !endKey) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function matchKey(key: Key) {
|
||||||
|
return key === startKey || key === endKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
traverseNodesKey(treeData, (key: Key) => {
|
||||||
|
if (record === Record.End) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchKey(key)) {
|
||||||
|
// Match test
|
||||||
|
keys.push(key);
|
||||||
|
|
||||||
|
if (record === Record.None) {
|
||||||
|
record = Record.Start;
|
||||||
|
} else if (record === Record.Start) {
|
||||||
|
record = Record.End;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (record === Record.Start) {
|
||||||
|
// Append selection
|
||||||
|
keys.push(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expandedKeys.indexOf(key) === -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertDirectoryKeysToNodes(treeData: DataNode[], keys: Key[]) {
|
||||||
|
const restKeys: Key[] = [...keys];
|
||||||
|
const nodes: DataNode[] = [];
|
||||||
|
traverseNodesKey(treeData, (key: Key, node: DataNode) => {
|
||||||
|
const index = restKeys.indexOf(key);
|
||||||
|
if (index !== -1) {
|
||||||
|
nodes.push(node);
|
||||||
|
restKeys.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !!restKeys.length;
|
||||||
|
});
|
||||||
|
return nodes;
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { CSSProperties } from 'vue';
|
||||||
|
|
||||||
|
export const offset = 4;
|
||||||
|
|
||||||
|
export default function dropIndicatorRender(props: {
|
||||||
|
dropPosition: -1 | 0 | 1;
|
||||||
|
dropLevelOffset: number;
|
||||||
|
indent: number;
|
||||||
|
prefixCls: string;
|
||||||
|
direction: 'ltr' | 'rtl';
|
||||||
|
}) {
|
||||||
|
const { dropPosition, dropLevelOffset, prefixCls, indent, direction = 'ltr' } = props;
|
||||||
|
const startPosition = direction === 'ltr' ? 'left' : 'right';
|
||||||
|
const endPosition = direction === 'ltr' ? 'right' : 'left';
|
||||||
|
const style: CSSProperties = {
|
||||||
|
[startPosition]: `${-dropLevelOffset * indent + offset}px`,
|
||||||
|
[endPosition]: 0,
|
||||||
|
};
|
||||||
|
switch (dropPosition) {
|
||||||
|
case -1:
|
||||||
|
style.top = `${-3}px`;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
style.bottom = `${-3}px`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// dropPosition === 0
|
||||||
|
style.bottom = `${-3}px`;
|
||||||
|
style[startPosition] = `${indent + offset}px`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return <div style={style} class={`${prefixCls}-drop-indicator`} />;
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined';
|
||||||
|
import FileOutlined from '@ant-design/icons-vue/FileOutlined';
|
||||||
|
import MinusSquareOutlined from '@ant-design/icons-vue/MinusSquareOutlined';
|
||||||
|
import PlusSquareOutlined from '@ant-design/icons-vue/PlusSquareOutlined';
|
||||||
|
import CaretDownFilled from '@ant-design/icons-vue/CaretDownFilled';
|
||||||
|
import { AntTreeNodeProps } from '../Tree';
|
||||||
|
import { isValidElement } from 'ant-design-vue/es/_util/props-util';
|
||||||
|
|
||||||
|
import { cloneVNode } from 'vue';
|
||||||
|
|
||||||
|
export default function renderSwitcherIcon(
|
||||||
|
prefixCls: string,
|
||||||
|
switcherIcon: any,
|
||||||
|
showLine: boolean | { showLeafIcon: boolean } | undefined,
|
||||||
|
{ isLeaf, expanded, loading }: AntTreeNodeProps,
|
||||||
|
) {
|
||||||
|
if (loading) {
|
||||||
|
return <LoadingOutlined class={`${prefixCls}-switcher-loading-icon`} />;
|
||||||
|
}
|
||||||
|
let showLeafIcon;
|
||||||
|
if (showLine && typeof showLine === 'object') {
|
||||||
|
showLeafIcon = showLine.showLeafIcon;
|
||||||
|
}
|
||||||
|
if (isLeaf) {
|
||||||
|
if (showLine) {
|
||||||
|
if (typeof showLine === 'object' && !showLeafIcon) {
|
||||||
|
return <span class={`${prefixCls}-switcher-leaf-line`} />;
|
||||||
|
}
|
||||||
|
return <FileOutlined class={`${prefixCls}-switcher-line-icon`} />;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const switcherCls = `${prefixCls}-switcher-icon`;
|
||||||
|
if (isValidElement(switcherIcon)) {
|
||||||
|
return cloneVNode(switcherIcon, {
|
||||||
|
class: switcherCls,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (switcherIcon) {
|
||||||
|
return switcherIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showLine) {
|
||||||
|
return expanded ? (
|
||||||
|
<MinusSquareOutlined class={`${prefixCls}-switcher-line-icon`} />
|
||||||
|
) : (
|
||||||
|
<PlusSquareOutlined class={`${prefixCls}-switcher-line-icon`} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <CaretDownFilled class={switcherCls} />;
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ import UploadList from './UploadList';
|
||||||
import { UploadProps } from './interface';
|
import { UploadProps } from './interface';
|
||||||
import { T, fileToObject, genPercentAdd, getFileItem, removeFileItem } from './utils';
|
import { T, fileToObject, genPercentAdd, getFileItem, removeFileItem } from './utils';
|
||||||
import { defineComponent, inject } from 'vue';
|
import { defineComponent, inject } from 'vue';
|
||||||
import { getDataAndAria } from '../vc-tree/src/util';
|
import { getDataAndAriaProps } from '../_util/util';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'AUpload',
|
name: 'AUpload',
|
||||||
|
@ -280,7 +280,7 @@ export default defineComponent({
|
||||||
[`${prefixCls}-disabled`]: disabled,
|
[`${prefixCls}-disabled`]: disabled,
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<span class={className} {...getDataAndAria(this.$attrs)}>
|
<span class={className} {...getDataAndAriaProps(this.$attrs)}>
|
||||||
<div
|
<div
|
||||||
class={dragCls}
|
class={dragCls}
|
||||||
onDrop={this.onFileDrop}
|
onDrop={this.onFileDrop}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {
|
||||||
convertDataToTree as vcConvertDataToTree,
|
convertDataToTree as vcConvertDataToTree,
|
||||||
convertTreeToEntities as vcConvertTreeToEntities,
|
convertTreeToEntities as vcConvertTreeToEntities,
|
||||||
conductCheck as rcConductCheck,
|
conductCheck as rcConductCheck,
|
||||||
} from '../../vc-tree/src/util';
|
} from '../../vc-tree/utils/treeUtil';
|
||||||
import { hasClass } from '../../vc-util/Dom/class';
|
import { hasClass } from '../../vc-util/Dom/class';
|
||||||
import { SHOW_CHILD, SHOW_PARENT } from './strategies';
|
import { SHOW_CHILD, SHOW_PARENT } from './strategies';
|
||||||
import { getSlot, getPropsData, isEmptyElement } from '../../_util/props-util';
|
import { getSlot, getPropsData, isEmptyElement } from '../../_util/props-util';
|
||||||
|
|
|
@ -18,7 +18,7 @@ export default defineComponent({
|
||||||
motionType: String,
|
motionType: String,
|
||||||
treeNodeRequiredProps: { type: Object as PropType<TreeNodeRequiredProps> },
|
treeNodeRequiredProps: { type: Object as PropType<TreeNodeRequiredProps> },
|
||||||
},
|
},
|
||||||
slots: ['title', 'icon', 'switcherIcon'],
|
slots: ['title', 'icon', 'switcherIcon', 'checkable'],
|
||||||
setup(props, { attrs, slots }) {
|
setup(props, { attrs, slots }) {
|
||||||
const visible = ref(true);
|
const visible = ref(true);
|
||||||
const context = useInjectTreeContext();
|
const context = useInjectTreeContext();
|
||||||
|
|
|
@ -94,6 +94,7 @@ export default defineComponent({
|
||||||
name: 'NodeList',
|
name: 'NodeList',
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: nodeListProps,
|
props: nodeListProps,
|
||||||
|
slots: ['checkable'],
|
||||||
setup(props, { expose, attrs, slots }) {
|
setup(props, { expose, attrs, slots }) {
|
||||||
// =============================== Ref ================================
|
// =============================== Ref ================================
|
||||||
const listRef = ref(null);
|
const listRef = ref(null);
|
||||||
|
@ -275,8 +276,7 @@ export default defineComponent({
|
||||||
itemHeight={itemHeight}
|
itemHeight={itemHeight}
|
||||||
prefixCls={`${prefixCls}-list`}
|
prefixCls={`${prefixCls}-list`}
|
||||||
ref={listRef}
|
ref={listRef}
|
||||||
>
|
children={(treeNode: FlattenNode) => {
|
||||||
{(treeNode: FlattenNode) => {
|
|
||||||
const {
|
const {
|
||||||
pos,
|
pos,
|
||||||
data: { ...restProps },
|
data: { ...restProps },
|
||||||
|
@ -310,10 +310,11 @@ export default defineComponent({
|
||||||
onMousemove={() => {
|
onMousemove={() => {
|
||||||
onActiveChange(null);
|
onActiveChange(null);
|
||||||
}}
|
}}
|
||||||
|
v-slots={slots}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
</VirtualList>
|
></VirtualList>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -32,6 +32,7 @@ import classNames from '../_util/classNames';
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'Tree',
|
name: 'Tree',
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
|
slots: ['checkable'],
|
||||||
props: initDefaultProps(treeProps(), {
|
props: initDefaultProps(treeProps(), {
|
||||||
prefixCls: 'vc-tree',
|
prefixCls: 'vc-tree',
|
||||||
showLine: false,
|
showLine: false,
|
||||||
|
@ -52,7 +53,7 @@ export default defineComponent({
|
||||||
allowDrop: () => true,
|
allowDrop: () => true,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
setup(props, { attrs }) {
|
setup(props, { attrs, slots }) {
|
||||||
const destroyed = ref(false);
|
const destroyed = ref(false);
|
||||||
let delayedDragEnterLogic: Record<Key, number> = {};
|
let delayedDragEnterLogic: Record<Key, number> = {};
|
||||||
|
|
||||||
|
@ -91,6 +92,34 @@ export default defineComponent({
|
||||||
return props.treeData !== undefined ? props.treeData : convertTreeToData(props.children);
|
return props.treeData !== undefined ? props.treeData : convertTreeToData(props.children);
|
||||||
});
|
});
|
||||||
const keyEntities = ref({});
|
const keyEntities = ref({});
|
||||||
|
|
||||||
|
const focused = ref(false);
|
||||||
|
const activeKey = ref<Key>(null);
|
||||||
|
|
||||||
|
const listChanging = ref(false);
|
||||||
|
|
||||||
|
const fieldNames = computed(() => fillFieldNames(props.fieldNames));
|
||||||
|
|
||||||
|
const listRef = ref();
|
||||||
|
|
||||||
|
let dragStartMousePosition = null;
|
||||||
|
|
||||||
|
let dragNode = null;
|
||||||
|
|
||||||
|
const treeNodeRequiredProps = computed(() => {
|
||||||
|
return {
|
||||||
|
expandedKeys: expandedKeys.value || [],
|
||||||
|
selectedKeys: selectedKeys.value || [],
|
||||||
|
loadedKeys: loadedKeys.value || [],
|
||||||
|
loadingKeys: loadingKeys.value || [],
|
||||||
|
checkedKeys: checkedKeys.value || [],
|
||||||
|
halfCheckedKeys: halfCheckedKeys.value || [],
|
||||||
|
dragOverNodeKey: dragState.dragOverNodeKey,
|
||||||
|
dropPosition: dragState.dropPosition,
|
||||||
|
keyEntities: keyEntities.value,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (treeData.value) {
|
if (treeData.value) {
|
||||||
const entitiesMap = convertDataToEntities(treeData.value, { fieldNames: fieldNames.value });
|
const entitiesMap = convertDataToEntities(treeData.value, { fieldNames: fieldNames.value });
|
||||||
|
@ -186,32 +215,6 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const focused = ref(false);
|
|
||||||
const activeKey = ref<Key>(null);
|
|
||||||
|
|
||||||
const listChanging = ref(false);
|
|
||||||
|
|
||||||
const fieldNames = computed(() => fillFieldNames(props.fieldNames));
|
|
||||||
|
|
||||||
const listRef = ref();
|
|
||||||
|
|
||||||
let dragStartMousePosition = null;
|
|
||||||
|
|
||||||
let dragNode = null;
|
|
||||||
|
|
||||||
const treeNodeRequiredProps = computed(() => {
|
|
||||||
return {
|
|
||||||
expandedKeys: expandedKeys.value || [],
|
|
||||||
selectedKeys: selectedKeys.value || [],
|
|
||||||
loadedKeys: loadedKeys.value || [],
|
|
||||||
loadingKeys: loadingKeys.value || [],
|
|
||||||
checkedKeys: checkedKeys.value || [],
|
|
||||||
halfCheckedKeys: halfCheckedKeys.value || [],
|
|
||||||
dragOverNodeKey: dragState.dragOverNodeKey,
|
|
||||||
dropPosition: dragState.dropPosition,
|
|
||||||
keyEntities: keyEntities.value,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
const scrollTo: ScrollTo = scroll => {
|
const scrollTo: ScrollTo = scroll => {
|
||||||
listRef.value.scrollTo(scroll);
|
listRef.value.scrollTo(scroll);
|
||||||
};
|
};
|
||||||
|
@ -983,7 +986,7 @@ export default defineComponent({
|
||||||
checkable,
|
checkable,
|
||||||
checkStrictly,
|
checkStrictly,
|
||||||
disabled,
|
disabled,
|
||||||
motion,
|
openAnimation,
|
||||||
loadData,
|
loadData,
|
||||||
filterTreeNode,
|
filterTreeNode,
|
||||||
height,
|
height,
|
||||||
|
@ -1059,7 +1062,7 @@ export default defineComponent({
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
selectable={selectable}
|
selectable={selectable}
|
||||||
checkable={!!checkable}
|
checkable={!!checkable}
|
||||||
motion={motion}
|
motion={openAnimation}
|
||||||
dragging={dragging}
|
dragging={dragging}
|
||||||
height={height}
|
height={height}
|
||||||
itemHeight={itemHeight}
|
itemHeight={itemHeight}
|
||||||
|
@ -1078,6 +1081,7 @@ export default defineComponent({
|
||||||
onScroll={onScroll}
|
onScroll={onScroll}
|
||||||
{...treeNodeRequiredProps.value}
|
{...treeNodeRequiredProps.value}
|
||||||
{...domProps}
|
{...domProps}
|
||||||
|
v-slots={slots}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</TreeContext>
|
</TreeContext>
|
||||||
|
|
|
@ -16,7 +16,7 @@ export default defineComponent({
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: treeNodeProps,
|
props: treeNodeProps,
|
||||||
isTreeNode: 1,
|
isTreeNode: 1,
|
||||||
slots: ['title', 'icon', 'switcherIcon'],
|
slots: ['title', 'icon', 'switcherIcon', 'checkable'],
|
||||||
setup(props, { attrs, expose, slots }) {
|
setup(props, { attrs, expose, slots }) {
|
||||||
const dragNodeHighlight = ref(false);
|
const dragNodeHighlight = ref(false);
|
||||||
const context = useInjectTreeContext();
|
const context = useInjectTreeContext();
|
||||||
|
@ -275,9 +275,6 @@ export default defineComponent({
|
||||||
|
|
||||||
if (!checkable) return null;
|
if (!checkable) return null;
|
||||||
|
|
||||||
// [Legacy] Custom element should be separate with `checkable` in future
|
|
||||||
const $custom = typeof checkable !== 'boolean' ? checkable : null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
class={classNames(
|
class={classNames(
|
||||||
|
@ -288,7 +285,7 @@ export default defineComponent({
|
||||||
)}
|
)}
|
||||||
onClick={onCheck}
|
onClick={onCheck}
|
||||||
>
|
>
|
||||||
{$custom}
|
{slots.checkable?.()}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -84,6 +84,7 @@ export interface TreeContextProps {
|
||||||
const TreeContextKey: InjectionKey<ComputedRef<TreeContextProps>> = Symbol('TreeContextKey');
|
const TreeContextKey: InjectionKey<ComputedRef<TreeContextProps>> = Symbol('TreeContextKey');
|
||||||
|
|
||||||
export const TreeContext = defineComponent({
|
export const TreeContext = defineComponent({
|
||||||
|
name: 'TreeContext',
|
||||||
props: {
|
props: {
|
||||||
value: { type: Object as PropType<TreeContextProps> },
|
value: { type: Object as PropType<TreeContextProps> },
|
||||||
},
|
},
|
||||||
|
@ -92,7 +93,7 @@ export const TreeContext = defineComponent({
|
||||||
TreeContextKey,
|
TreeContextKey,
|
||||||
computed(() => props.value),
|
computed(() => props.value),
|
||||||
);
|
);
|
||||||
return slots.default?.();
|
return () => slots.default?.();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
|
import type { TreeProps, TreeNodeProps } from './props';
|
||||||
import Tree from './Tree';
|
import Tree from './Tree';
|
||||||
import TreeNode from './TreeNode';
|
import TreeNode from './TreeNode';
|
||||||
import type { TreeProps } from './Tree';
|
|
||||||
import type { TreeNodeProps } from './TreeNode';
|
|
||||||
|
|
||||||
export { TreeNode };
|
export { TreeNode };
|
||||||
export type { TreeProps, TreeNodeProps };
|
export type { TreeProps, TreeNodeProps };
|
||||||
export default Tree;
|
export default Tree;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { VNode } from 'vue';
|
import { CSSProperties, VNode } from 'vue';
|
||||||
export type { ScrollTo } from '../vc-virtual-list/List';
|
export type { ScrollTo } from '../vc-virtual-list/List';
|
||||||
|
|
||||||
export interface DataNode {
|
export interface DataNode {
|
||||||
|
@ -14,8 +14,8 @@ export interface DataNode {
|
||||||
switcherIcon?: IconType;
|
switcherIcon?: IconType;
|
||||||
|
|
||||||
/** Set style of TreeNode. This is not recommend if you don't have any force requirement */
|
/** Set style of TreeNode. This is not recommend if you don't have any force requirement */
|
||||||
// className?: string;
|
class?: string;
|
||||||
// style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EventDataNode extends DataNode {
|
export interface EventDataNode extends DataNode {
|
||||||
|
|
|
@ -5,8 +5,15 @@ import type {
|
||||||
NodeMouseEventHandler,
|
NodeMouseEventHandler,
|
||||||
NodeMouseEventParams,
|
NodeMouseEventParams,
|
||||||
} from './contextTypes';
|
} from './contextTypes';
|
||||||
import type { DataNode, Key, FlattenNode, DataEntity, EventDataNode, Direction } from './interface';
|
import type {
|
||||||
import { fillFieldNames } from './utils/treeUtil';
|
DataNode,
|
||||||
|
Key,
|
||||||
|
FlattenNode,
|
||||||
|
DataEntity,
|
||||||
|
EventDataNode,
|
||||||
|
Direction,
|
||||||
|
FieldNames,
|
||||||
|
} from './interface';
|
||||||
|
|
||||||
export interface CheckInfo {
|
export interface CheckInfo {
|
||||||
event: 'check';
|
event: 'check';
|
||||||
|
@ -103,7 +110,7 @@ export const treeProps = () => ({
|
||||||
tabindex: Number,
|
tabindex: Number,
|
||||||
children: PropTypes.VNodeChild,
|
children: PropTypes.VNodeChild,
|
||||||
treeData: { type: Array as PropType<DataNode[]> }, // Generate treeNode by children
|
treeData: { type: Array as PropType<DataNode[]> }, // Generate treeNode by children
|
||||||
fieldNames: fillFieldNames,
|
fieldNames: { type: Object as PropType<FieldNames> },
|
||||||
showLine: { type: Boolean, default: undefined },
|
showLine: { type: Boolean, default: undefined },
|
||||||
showIcon: { type: Boolean, default: undefined },
|
showIcon: { type: Boolean, default: undefined },
|
||||||
icon: PropTypes.any,
|
icon: PropTypes.any,
|
||||||
|
@ -218,7 +225,7 @@ export const treeProps = () => ({
|
||||||
*/
|
*/
|
||||||
onActiveChange: { type: Function as PropType<(key: Key) => void> },
|
onActiveChange: { type: Function as PropType<(key: Key) => void> },
|
||||||
filterTreeNode: { type: Function as PropType<(treeNode: EventDataNode) => boolean> },
|
filterTreeNode: { type: Function as PropType<(treeNode: EventDataNode) => boolean> },
|
||||||
motion: PropTypes.any,
|
openAnimation: PropTypes.any,
|
||||||
switcherIcon: PropTypes.any,
|
switcherIcon: PropTypes.any,
|
||||||
|
|
||||||
// Virtual List
|
// Virtual List
|
||||||
|
|
|
@ -1,39 +1,62 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<a-tree
|
||||||
<demo />
|
v-model:expandedKeys="expandedKeys"
|
||||||
</div>
|
v-model:selectedKeys="selectedKeys"
|
||||||
|
v-model:checkedKeys="checkedKeys"
|
||||||
|
checkable
|
||||||
|
:tree-data="treeData"
|
||||||
|
>
|
||||||
|
<template #title0010><span style="color: #1890ff">sss</span></template>
|
||||||
|
</a-tree>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent, ref, watch } from 'vue';
|
||||||
import demo from '../v2-doc/src/docs/mentions/demo/index.vue';
|
import { TreeDataItem } from 'ant-design-vue/es/tree/Tree';
|
||||||
// import Affix from '../components/affix';
|
|
||||||
|
const treeData: TreeDataItem[] = [
|
||||||
|
{
|
||||||
|
title: 'parent 1',
|
||||||
|
key: '0-0',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
title: 'parent 1-0',
|
||||||
|
key: '0-0-0',
|
||||||
|
disabled: true,
|
||||||
|
children: [
|
||||||
|
{ title: 'leaf', key: '0-0-0-0', disableCheckbox: true },
|
||||||
|
{ title: 'leaf', key: '0-0-0-1' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'parent 1-1',
|
||||||
|
key: '0-0-1',
|
||||||
|
children: [{ key: '0-0-1-0', slots: { title: 'title0010' } }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
setup() {
|
||||||
demo,
|
const expandedKeys = ref<string[]>(['0-0-0', '0-0-1']);
|
||||||
// Affix,
|
const selectedKeys = ref<string[]>(['0-0-0', '0-0-1']);
|
||||||
},
|
const checkedKeys = ref<string[]>(['0-0-0', '0-0-1']);
|
||||||
data() {
|
watch(expandedKeys, () => {
|
||||||
|
console.log('expandedKeys', expandedKeys);
|
||||||
|
});
|
||||||
|
watch(selectedKeys, () => {
|
||||||
|
console.log('selectedKeys', selectedKeys);
|
||||||
|
});
|
||||||
|
watch(checkedKeys, () => {
|
||||||
|
console.log('checkedKeys', checkedKeys);
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
visible: false,
|
treeData,
|
||||||
pStyle: {
|
expandedKeys,
|
||||||
fontSize: '16px',
|
selectedKeys,
|
||||||
color: 'rgba(0,0,0,0.85)',
|
checkedKeys,
|
||||||
lineHeight: '24px',
|
|
||||||
display: 'block',
|
|
||||||
marginBottom: '16px',
|
|
||||||
},
|
|
||||||
pStyle2: {
|
|
||||||
marginBottom: '24px',
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
|
||||||
showDrawer() {
|
|
||||||
this.visible = true;
|
|
||||||
},
|
|
||||||
onClose() {
|
|
||||||
this.visible = false;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Reference in New Issue