Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions packages/devui-vue/devui/tree/src/components/icon-close.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { VNodeTypes } from 'vue';

export const IconClose = (): VNodeTypes => (
export const IconClose = () => (
<svg
width="16px"
height="16px"
Expand Down
4 changes: 1 addition & 3 deletions packages/devui-vue/devui/tree/src/components/icon-open.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { VNodeTypes } from 'vue';

export const IconOpen = (): VNodeTypes => (
export const IconOpen = () => (
<svg
width="16px"
height="16px"
Expand Down
30 changes: 30 additions & 0 deletions packages/devui-vue/devui/tree/src/components/tree-node-toggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { defineComponent, PropType, toRefs } from 'vue';
import { ITreeNode } from '../core/tree-factory-types';
import { IconClose } from './icon-close';
import { IconOpen } from './icon-open';

export default defineComponent({
name: 'DTreeNodeToggle',
props: {
data: {
type: Object as PropType<ITreeNode>,
},
},
setup(props) {
const { data } = toRefs(props);

return () => {
return (
<span class="devui-tree-node__folder">
{data.value.isLeaf ? (
<span class="devui-tree-node__indent" />
) : data.value.expanded ? (
<IconOpen class="mr-xs" />
) : (
<IconClose class="mr-xs" />
)}
</span>
);
};
},
});
30 changes: 30 additions & 0 deletions packages/devui-vue/devui/tree/src/components/tree-node.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { defineComponent, PropType, ref, toRefs } from 'vue';
import { ITreeNode } from '../core/tree-factory-types';
import DTreeNodeToggle from './tree-node-toggle';

export default defineComponent({
name: 'DTreeNode',
props: {
data: {
type: Object as PropType<ITreeNode>,
},
},
setup(props) {
const { data } = toRefs(props);

return () => {
return (
<div
class={['devui-tree-node', data.value.expanded && 'devui-tree-node__open']}
style={{ paddingLeft: `${24 * (data.value.level - 1)}px` }}>
<div class="devui-tree-node__content">
<div class="devui-tree-node__content--value-wrapper">
<DTreeNodeToggle data={data.value} />
<span class="devui-tree-node__title">{data.value.label}</span>
</div>
</div>
</div>
);
};
},
});
43 changes: 43 additions & 0 deletions packages/devui-vue/devui/tree/src/core/tree-factory-types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Ref } from 'vue';

// 外部数据结构先只考虑嵌套结构
export interface ITreeNode {
label: string;
Expand All @@ -18,6 +20,47 @@ export interface IInnerTreeNode extends ITreeNode {
level: number;
idType?: 'random';
parentId?: string;
isLeaf?: boolean;
}

export type valueof<T> = T[keyof T];

export interface IUseCore {
getLevel: (node: ITreeNode) => number;
getChildren: (node: ITreeNode) => IInnerTreeNode[];
getIndex: (node: ITreeNode) => number;
getNode: (node: ITreeNode) => IInnerTreeNode;
setNodeValue: (node: IInnerTreeNode, key: keyof IInnerTreeNode, value: valueof<IInnerTreeNode>) => void;
setTree: (newTree: ITreeNode[]) => void;
};

export interface IUseCheck {
checkNode: (node: IInnerTreeNode) => void;
uncheckNode: (node: IInnerTreeNode) => void;
}

export interface IUseDisable {
disableSelectNode: (node: IInnerTreeNode) => void;
disableCheckNode: (node: IInnerTreeNode) => void;
disableToggleNode: (node: IInnerTreeNode) => void;
}

export interface IUseOperate {
insertBefore: (parentNode: ITreeNode, node: ITreeNode, referenceNode: ITreeNode, cut: boolean) => void;
removeNode: (node: ITreeNode) => void;
editNode: (node: ITreeNode, label: string) => void;
}

export interface IUseSelect {
selectNode: (node: IInnerTreeNode) => void;
}

export interface IUseToggle {
expandNode: (node: ITreeNode) => void;
collapseNode: (node: ITreeNode) => void;
toggleNode: (node: ITreeNode) => void;
}

export type IUseTree = {
treeData: Ref<IInnerTreeNode[]>
} & IUseCore & IUseCheck & IUseDisable & IUseOperate & IUseSelect & IUseToggle;
2 changes: 1 addition & 1 deletion packages/devui-vue/devui/tree/src/core/tree-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default class TreeFactory {
this.setTree(tree);
}

getTree(flat?: boolean = false): IInnerTreeNode[] {
getTree(flat?: boolean = true): IInnerTreeNode[] {
if (flat) {
return this._innerTree;
} else {
Expand Down
21 changes: 21 additions & 0 deletions packages/devui-vue/devui/tree/src/core/use-check.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Ref } from 'vue';
import { IInnerTreeNode, IUseCore } from './tree-factory-types';

export default function useCheck(data: Ref<IInnerTreeNode[]>, core: IUseCore) {
console.log('useCheck:', data, data.value);

const { setNodeValue } = core;

const checkNode = (node: IInnerTreeNode): void => {
setNodeValue(node, 'checked', true);
}

const uncheckNode = (node: IInnerTreeNode): void => {
setNodeValue(node, 'checked', false);
}

return {
checkNode,
uncheckNode,
}
}
48 changes: 48 additions & 0 deletions packages/devui-vue/devui/tree/src/core/use-core.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Ref } from 'vue';
import { IInnerTreeNode, ITreeNode, IUseCore, valueof } from './tree-factory-types';
import { generateInnerTree } from './utils';

export default function useCore(data: Ref<IInnerTreeNode[]>): IUseCore {
console.log('useCore:', data, data.value);

const getLevel = (node: ITreeNode): number => {
return data.value.find((item) => item.id === node.id).level;
}

const getChildren = (node: ITreeNode): IInnerTreeNode[] => {
let result = [];
const startIndex = data.value.findIndex((item) => item.id === node.id);

for (let i = startIndex + 1; i < data.value.length && getLevel(node) < data.value[i].level; i++) {
result.push(data.value[i]);
}
console.log('result:', result);

return result;
}

const getIndex = (node: ITreeNode): number => {
return data.value.findIndex((item) => item.id === node.id);
}

const getNode = (node: ITreeNode): IInnerTreeNode => {
return data.value.find((item) => item.id === node.id);
}

const setNodeValue = (node: IInnerTreeNode, key: keyof IInnerTreeNode, value: valueof<IInnerTreeNode>): void => {
data.value[getIndex(node)][key] = value;
}

const setTree = (newTree: ITreeNode[]): void => {
data.value = generateInnerTree(newTree);
}

return {
getLevel,
getChildren,
getIndex,
getNode,
setNodeValue,
setTree,
}
}
26 changes: 26 additions & 0 deletions packages/devui-vue/devui/tree/src/core/use-disable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Ref } from 'vue';
import { IInnerTreeNode, IUseCore } from './tree-factory-types';

export default function useDisable(data: Ref<IInnerTreeNode[]>, core: IUseCore) {
console.log('useDisable:', data, data.value);

const { setNodeValue } = core;

const disableSelectNode = (node: IInnerTreeNode): void => {
setNodeValue(node, 'disableSelect', true);
}

const disableCheckNode = (node: IInnerTreeNode): void => {
setNodeValue(node, 'disableCheck', true);
}

const disableToggleNode = (node: IInnerTreeNode): void => {
setNodeValue(node, 'disableToggle', true);
}

return {
disableSelectNode,
disableCheckNode,
disableToggleNode,
}
}
28 changes: 28 additions & 0 deletions packages/devui-vue/devui/tree/src/core/use-operate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Ref } from 'vue';
import { IInnerTreeNode, ITreeNode, IUseCore } from './tree-factory-types';

export default function useOperate(data: Ref<IInnerTreeNode[]>, core: IUseCore) {
console.log('useOperate:', data, data.value);

const { setNodeValue, getChildren } = core;

const insertBefore = (parentNode: ITreeNode, node: ITreeNode, referenceNode: ITreeNode, cut: boolean = false): void => {
// TODO
}

const removeNode = (node: ITreeNode): void => {
data.value = data.value.filter(item => {
return item.id !== node.id && !getChildren(node).map(nodeItem => nodeItem.id).includes(item.id);
})
}

const editNode = (node: ITreeNode, label: string): void => {
setNodeValue(node, 'label', label);
}

return {
insertBefore,
removeNode,
editNode,
}
}
16 changes: 16 additions & 0 deletions packages/devui-vue/devui/tree/src/core/use-select.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Ref } from 'vue';
import { IInnerTreeNode, IUseCore } from './tree-factory-types';

export default function useSelect(data: Ref<IInnerTreeNode[]>, core: IUseCore) {
console.log('useSelect:', data, data.value);

const { setNodeValue } = core;

const selectNode = (node: IInnerTreeNode): void => {
setNodeValue(node, 'selected', true);
}

return {
selectNode,
}
}
32 changes: 32 additions & 0 deletions packages/devui-vue/devui/tree/src/core/use-toggle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Ref } from 'vue';
import { IInnerTreeNode, ITreeNode, IUseCore } from './tree-factory-types';

export default function useToggle(data: Ref<IInnerTreeNode[]>, core: IUseCore) {
console.log('useToggle:', data, data.value);

const { getNode, setNodeValue } = core;

const expandNode = (node: ITreeNode): void => {
setNodeValue(node, 'expanded', true);
}

const collapseNode = (node: ITreeNode): void => {
setNodeValue(node, 'expanded', false);
}

const toggleNode = (node: ITreeNode): void => {
console.log('toggleNode node:', node);

if (getNode(node).expanded) {
setNodeValue(node, 'expanded', false);
} else {
setNodeValue(node, 'expanded', true);
}
}

return {
expandNode,
collapseNode,
toggleNode,
}
}
22 changes: 22 additions & 0 deletions packages/devui-vue/devui/tree/src/core/use-tree.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ref } from 'vue';
import { IInnerTreeNode, ITreeNode, IUseCore, IUseTree } from './tree-factory-types';
import useToggle from './use-toggle';
import useCore from './use-core';
import { generateInnerTree } from './utils';

export const DEFAULT_TREE_PLUGINS = [useCore, useToggle];

export default function useTree(tree: ITreeNode[], plugins = []): Partial<IUseTree> {
const treeData = ref<IInnerTreeNode[]>(generateInnerTree(tree));

const core: IUseCore = useCore(treeData);

const pluginMethods = DEFAULT_TREE_PLUGINS.concat(plugins).reduce((acc, plugin) => {
return { ...acc, ...plugin(treeData, core) };
}, {});

return {
treeData,
...pluginMethods,
};
}
2 changes: 1 addition & 1 deletion packages/devui-vue/devui/tree/src/core/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export function generateInnerTree(
}

if (!newItem[key]) {
return acc.concat(newItem);
return acc.concat({ ...newItem, isLeaf: true });
} else {
return acc.concat(omit<ITreeNode>(newItem, 'children'), generateInnerTree(newItem[key], key, level, path));
}
Expand Down