Skip to content

Commit 6848cc7

Browse files
committed
Refactor tree operations with generator and unified action handling
1 parent afd4170 commit 6848cc7

File tree

1 file changed

+142
-154
lines changed

1 file changed

+142
-154
lines changed

src/index.ts

Lines changed: 142 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
1-
import { toReactive } from "@vueuse/core";
21
import { computed, isReactive, reactive } from "vue";
32

3+
/* -------------------------------------------------------------------------- */
4+
/* Тип произвольного объекта */
5+
/* -------------------------------------------------------------------------- */
6+
7+
export type unObject = Record<string, unknown>;
8+
9+
/* -------------------------------------------------------------------------- */
10+
/* Композабл для работы с древовидным объектом */
11+
/* -------------------------------------------------------------------------- */
12+
413
export default (
5-
tree: Record<string, unknown>[],
14+
tree: unObject[],
615
{
716
branch: keyBranch = "branch",
817
children: keyChildren = "children",
@@ -14,186 +23,165 @@ export default (
1423
siblings: keySiblings = "siblings",
1524
} = {},
1625
) => {
26+
/* -------------------------------------------------------------------------- */
27+
/* Расчетные свойства для работы с древовидным объектом */
28+
/* -------------------------------------------------------------------------- */
29+
1730
const properties: PropertyDescriptorMap = {
1831
[keyBranch]: {
19-
get(this: Record<string, unknown>) {
32+
get(this: unObject) {
2033
const ret = [this];
21-
while (ret[0]?.[keyParent])
22-
ret.unshift(ret[0][keyParent] as Record<string, unknown>);
34+
while (ret[0]?.[keyParent]) ret.unshift(ret[0][keyParent] as unObject);
2335
return ret;
2436
},
2537
},
2638
[keyIndex]: {
27-
get(this: Record<string, unknown>) {
28-
return (this[keySiblings] as Record<string, unknown>[]).findIndex(
39+
get(this: unObject) {
40+
return (this[keySiblings] as unObject[]).findIndex(
2941
(sibling) => this[keyId] === sibling[keyId],
3042
);
3143
},
3244
},
3345
[keyNext]: {
34-
get(this: Record<string, unknown>) {
35-
return (this[keySiblings] as Record<string, unknown>[])[
46+
get(this: unObject) {
47+
return (this[keySiblings] as unObject[])[
3648
(this[keyIndex] as number) + 1
3749
];
3850
},
3951
},
4052
[keyPrev]: {
41-
get(this: Record<string, unknown>) {
42-
return (this[keySiblings] as Record<string, unknown>[])[
53+
get(this: unObject) {
54+
return (this[keySiblings] as unObject[])[
4355
(this[keyIndex] as number) - 1
4456
];
4557
},
4658
},
4759
};
48-
const getLeaves = (
49-
siblings: { configurable?: boolean; value: Record<string, unknown>[] },
50-
parent = {},
51-
) =>
52-
siblings.value.flatMap((value): Record<string, unknown>[] => {
53-
Object.defineProperties(value, {
54-
...properties,
55-
[keyParent]: parent,
56-
[keySiblings]: siblings,
57-
});
58-
return [
59-
value,
60-
...getLeaves(
61-
{
62-
configurable: true,
63-
value: (value[keyChildren] ?? []) as Record<string, unknown>[],
64-
},
65-
{ configurable: true, value },
66-
),
67-
];
68-
}),
69-
leaves = computed(() =>
70-
getLeaves({ value: isReactive(tree) ? tree : reactive(tree) }),
71-
),
72-
objLeaves = toReactive(
73-
computed(() =>
74-
Object.fromEntries(
75-
leaves.value.map((leaf) => [leaf[keyId] as string, leaf]),
76-
),
77-
),
78-
);
79-
return {
80-
add: (pId: string) => {
81-
const the = objLeaves[pId];
82-
if (the) {
83-
const children = the[keyChildren] as
84-
| Record<string, unknown>[]
85-
| undefined,
86-
url = URL.createObjectURL(new Blob()),
87-
id = url.split("/").pop() ?? crypto.randomUUID(),
88-
index = the[keyIndex] as number,
89-
siblings = the[keySiblings] as Record<string, unknown>[];
90-
URL.revokeObjectURL(url);
91-
switch (true) {
92-
case !!the[keyParent]:
93-
siblings.splice(index + 1, 0, { [keyId]: id });
94-
break;
95-
case !!children:
96-
children.unshift({ [keyId]: id });
97-
break;
98-
default:
99-
siblings.splice(index + 1, 0, { [keyId]: id });
100-
break;
101-
}
102-
return id;
103-
}
104-
return undefined;
105-
},
106-
arrLeaves: toReactive(leaves),
107-
down: (pId: string) => {
108-
const the = objLeaves[pId];
109-
if (the) {
110-
const index = the[keyIndex] as number,
111-
nextIndex = index + 1,
112-
siblings = the[keySiblings] as Record<string, unknown>[];
113-
if (
114-
index < siblings.length - 1 &&
115-
siblings[index] &&
116-
siblings[nextIndex]
117-
)
118-
[siblings[index], siblings[nextIndex]] = [
119-
siblings[nextIndex],
120-
siblings[index],
121-
];
122-
}
123-
},
124-
leaves,
125-
left: (pId: string) => {
126-
const the = objLeaves[pId];
127-
if (the) {
128-
const parent = the[keyParent] as Record<string, unknown> | undefined;
129-
if (parent?.[keyParent]) {
130-
const children = (parent[keyChildren] ?? []) as Record<
131-
string,
132-
unknown
133-
>[],
134-
siblings = parent[keySiblings] as Record<string, unknown>[];
135-
siblings.splice(
136-
(parent[keyIndex] as number) + 1,
137-
0,
138-
...children.splice(the[keyIndex] as number, 1),
60+
61+
/* -------------------------------------------------------------------------- */
62+
/* Формирование массива элементов дерева простого и ассоциативного */
63+
/* -------------------------------------------------------------------------- */
64+
65+
const getItems = (nodes: unObject[], node?: unObject) =>
66+
nodes.toReversed().map((child) => ({
67+
node: child,
68+
parent: { configurable: true, value: node },
69+
siblings: { configurable: true, value: nodes },
70+
})),
71+
getNodes = function* (nodes: unObject[]) {
72+
const stack = getItems(nodes);
73+
while (stack.length > 0) {
74+
const { node, parent, siblings } = stack.pop() ?? {};
75+
if (node && parent && siblings) {
76+
Object.defineProperties(node, {
77+
...properties,
78+
[keyParent]: parent,
79+
[keySiblings]: siblings,
80+
});
81+
yield node;
82+
stack.push(
83+
...getItems((node[keyChildren] ?? []) as unObject[], node),
13984
);
140-
return parent[keyId] as string;
141-
}
142-
}
143-
return undefined;
144-
},
145-
objLeaves,
146-
remove: (pId: string) => {
147-
const the = objLeaves[pId];
148-
if (the) {
149-
const parent = the[keyParent] as Record<string, unknown> | undefined;
150-
if (parent) {
151-
const [root] = leaves.value,
152-
next = the[keyNext] as Record<string, unknown> | undefined,
153-
prev = the[keyPrev] as Record<string, unknown> | undefined,
154-
id = (next?.[keyId] ??
155-
prev?.[keyId] ??
156-
parent[keyId] ??
157-
root?.[keyId]) as string,
158-
siblings = the[keySiblings] as Record<string, unknown>[];
159-
siblings.splice(the[keyIndex] as number, 1);
160-
return id;
16185
}
16286
}
163-
return undefined;
16487
},
165-
right: (pId: string) => {
166-
const the = objLeaves[pId];
167-
if (the) {
168-
const prev = the[keyPrev] as Record<string, unknown> | undefined;
169-
if (prev) {
170-
const children = (prev[keyChildren] ?? []) as Record<
171-
string,
172-
unknown
173-
>[],
174-
id = prev[keyId] as string,
175-
siblings = the[keySiblings] as Record<string, unknown>[];
176-
prev[keyChildren] = [
177-
...children,
178-
...siblings.splice(the[keyIndex] as number, 1),
179-
];
88+
nodes = computed(() => [
89+
...getNodes(isReactive(tree) ? tree : reactive(tree)),
90+
]),
91+
nodesMap = computed(() =>
92+
Object.fromEntries(
93+
nodes.value.map((node) => [node[keyId] as string, node]),
94+
),
95+
);
96+
97+
/* -------------------------------------------------------------------------- */
98+
/* Служебная функция для выполнения действия над элементом дерева */
99+
/* -------------------------------------------------------------------------- */
100+
101+
const run = (pId: string, action: string) => {
102+
const the = nodesMap.value[pId];
103+
if (the) {
104+
const [root] = nodes.value,
105+
children = the[keyChildren] as undefined | unObject[],
106+
index = the[keyIndex] as number,
107+
next = the[keyNext] as undefined | unObject,
108+
nextIndex = index + 1,
109+
parent = the[keyParent] as undefined | unObject,
110+
prev = the[keyPrev] as undefined | unObject,
111+
prevIndex = index - 1,
112+
siblings = the[keySiblings] as unObject[];
113+
switch (action) {
114+
case "add": {
115+
const url = URL.createObjectURL(new Blob()),
116+
id = url.split("/").pop();
117+
URL.revokeObjectURL(url);
118+
if (parent && Array.isArray(children))
119+
children.unshift({ [keyId]: id });
120+
else siblings.splice(index + 1, 0, { [keyId]: id });
180121
return id;
181122
}
123+
case "down":
124+
if (
125+
index < siblings.length - 1 &&
126+
siblings[index] &&
127+
siblings[nextIndex]
128+
)
129+
[siblings[index], siblings[nextIndex]] = [
130+
siblings[nextIndex],
131+
siblings[index],
132+
];
133+
break;
134+
case "left":
135+
if (parent?.[keyParent]) {
136+
(parent[keySiblings] as unObject[]).splice(
137+
(parent[keyIndex] as number) + 1,
138+
0,
139+
...siblings.splice(index, 1),
140+
);
141+
return parent[keyId] as string;
142+
}
143+
break;
144+
case "remove":
145+
if (parent) {
146+
const id = (next?.[keyId] ??
147+
prev?.[keyId] ??
148+
parent[keyId] ??
149+
root?.[keyId]) as string | undefined;
150+
siblings.splice(index, 1);
151+
return id;
152+
}
153+
break;
154+
case "right":
155+
if (prev) {
156+
const children = (prev[keyChildren] ?? []) as unObject[],
157+
id = prev[keyId] as string;
158+
prev[keyChildren] = [...children, ...siblings.splice(index, 1)];
159+
return id;
160+
}
161+
break;
162+
case "up":
163+
if (index && siblings[index] && siblings[prevIndex])
164+
[siblings[prevIndex], siblings[index]] = [
165+
siblings[index],
166+
siblings[prevIndex],
167+
];
168+
break;
182169
}
183-
return undefined;
184-
},
185-
up: (pId: string) => {
186-
const the = objLeaves[pId];
187-
if (the) {
188-
const index = the[keyIndex] as number,
189-
prevIndex = index - 1,
190-
siblings = the[keySiblings] as Record<string, unknown>[];
191-
if (index && siblings[index] && siblings[prevIndex])
192-
[siblings[prevIndex], siblings[index]] = [
193-
siblings[index],
194-
siblings[prevIndex],
195-
];
196-
}
197-
},
170+
}
171+
};
172+
173+
/* -------------------------------------------------------------------------- */
174+
/* Формирование возвращаемого объекта композабл функции */
175+
/* -------------------------------------------------------------------------- */
176+
177+
return {
178+
add: (pId: string) => run(pId, "add"),
179+
down: (pId: string) => run(pId, "down"),
180+
left: (pId: string) => run(pId, "left"),
181+
nodes,
182+
nodesMap,
183+
remove: (pId: string) => run(pId, "remove"),
184+
right: (pId: string) => run(pId, "right"),
185+
up: (pId: string) => run(pId, "up"),
198186
};
199187
};

0 commit comments

Comments
 (0)