Skip to content

Commit a5e0bf2

Browse files
move tree utils to separate file
1 parent 0a5cc99 commit a5e0bf2

File tree

2 files changed

+140
-135
lines changed

2 files changed

+140
-135
lines changed

src/api/ui/views/tree-table.tsx

Lines changed: 11 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { GroupElement, Grouping, Groupings, Literal, Literals } from "expression/literal";
21
import { GroupingConfig, TableColumn, TableViewProps } from "./table";
32
import { useAsElement } from "ui/hooks";
43
import { useInterning, useStableCallback } from "ui/hooks";
@@ -11,133 +10,10 @@ import { useEditableDispatch } from "ui/fields/editable";
1110
import { combineClasses } from "../basics";
1211
import { Indexable } from "index/types/indexable";
1312
import { App } from "obsidian";
13+
import {TreeTableRowData, TreeUtils} from "utils/tree";
14+
import { GroupElement, Grouping, Groupings, Literal, Literals } from "expression/literal";
1415

15-
export interface TreeTableRowData<T> {
16-
value: T;
17-
children: TreeTableRowData<T>[];
18-
}
19-
20-
export namespace TreeUtils {
21-
export function isTreeTableRowData<T>(data: any): data is TreeTableRowData<T> {
22-
return (
23-
"children" in data &&
24-
"value" in data &&
25-
!Array.isArray(data) &&
26-
Object.keys(data).length == 2 &&
27-
Array.isArray(data.children)
28-
);
29-
}
30-
export function countInTreeRow<T>(node: TreeTableRowData<T>, top: boolean = true): number {
31-
let result = 0;
32-
if (!top) result++;
33-
for (let n of node.children) result += countInTreeRow(n, false);
34-
return result;
35-
}
36-
export function ofArray<T>(source: T[], childFn: (el: T) => T[]): TreeTableRowData<T>[] {
37-
const mapper = (el: T): TreeTableRowData<T> => {
38-
return {
39-
value: el,
40-
children: childFn(el).map(mapper),
41-
} as TreeTableRowData<T>;
42-
};
43-
return source.map(mapper);
44-
}
45-
export function ofNode<T>(source: T, childFn: (el: T) => T[]): TreeTableRowData<T> {
46-
return {
47-
value: source,
48-
children: ofArray(childFn(source), childFn),
49-
};
50-
}
51-
52-
export function ofGrouping<T>(elements: Grouping<T>, childFn: (el: T) => T[]): Grouping<TreeTableRowData<T>> {
53-
const mapper = (l: T | GroupElement<T>): GroupElement<TreeTableRowData<T>> | TreeTableRowData<T> => {
54-
if (Groupings.isElementGroup(l))
55-
return { key: l.key, rows: l.rows.map(mapper) } as GroupElement<TreeTableRowData<T>>;
56-
return {
57-
value: l,
58-
children: childFn(l).map(mapper),
59-
} as TreeTableRowData<T>;
60-
};
61-
return elements.map(mapper) as Grouping<TreeTableRowData<T>>;
62-
}
63-
64-
export function count<T>(elements: Grouping<TreeTableRowData<T>> | GroupElement<TreeTableRowData<T>>): number {
65-
if (Groupings.isElementGroup(elements)) {
66-
return count(elements.rows);
67-
} else if (Groupings.isGrouping(elements)) {
68-
let result = 0;
69-
for (let group of elements) result += count(group.rows);
70-
return result;
71-
} else {
72-
return elements.reduce((pv, cv) => pv + countInTreeRow(cv), 0);
73-
}
74-
}
7516

76-
export function slice<T>(
77-
elements: Grouping<TreeTableRowData<T>>,
78-
start: number,
79-
end: number
80-
): Grouping<TreeTableRowData<T>> {
81-
let initial = [...Groupings.slice(elements, start, end)] as Grouping<TreeTableRowData<T>>;
82-
let index = 0,
83-
seen = 0;
84-
85-
for (let element of initial) {
86-
if (Groupings.isElementGroup(element)) {
87-
let groupSize = Groupings.count(elements);
88-
let groupStart = Math.min(seen, start);
89-
let groupEnd = Math.min(groupSize, end);
90-
(initial[index] as GroupElement<TreeTableRowData<T>>).rows = Groupings.slice(
91-
element.rows,
92-
groupStart,
93-
groupEnd
94-
);
95-
seen += groupSize;
96-
} else {
97-
seen += countInTreeRow(element);
98-
}
99-
index++;
100-
}
101-
return initial;
102-
}
103-
/** recursively sort a tree */
104-
export function sort<T, V = Literal>(
105-
rows: (TreeTableRowData<T> | GroupElement<TreeTableRowData<T>>)[],
106-
comparators: {
107-
fn: (a: V, b: V, ao: T, ab: T) => number;
108-
direction: SortDirection;
109-
actualValue: (obj: T) => V;
110-
}[]
111-
): (TreeTableRowData<T> | GroupElement<TreeTableRowData<T>>)[] {
112-
const realComparator = (
113-
a: TreeTableRowData<T> | GroupElement<TreeTableRowData<T>>,
114-
b: TreeTableRowData<T> | GroupElement<TreeTableRowData<T>>
115-
): number => {
116-
for (let comp of comparators) {
117-
const direction = comp.direction.toLocaleLowerCase() === "ascending" ? 1 : -1;
118-
let result = 0;
119-
if (Groupings.isElementGroup(a) && Groupings.isElementGroup(b)) {
120-
result = 0;
121-
} else if (!Groupings.isElementGroup(a) && !Groupings.isElementGroup(b)) {
122-
result =
123-
direction * comp.fn(comp.actualValue(a.value), comp.actualValue(b.value), a.value, b.value);
124-
}
125-
if (result != 0) return result;
126-
}
127-
return 0;
128-
};
129-
const map = (
130-
t: TreeTableRowData<T> | GroupElement<TreeTableRowData<T>>
131-
): TreeTableRowData<T> | GroupElement<TreeTableRowData<T>> => {
132-
let r;
133-
if (Groupings.isElementGroup(t))
134-
r = { ...t, rows: sort(t.rows, comparators).map(map) } as GroupElement<TreeTableRowData<T>>;
135-
else r = { ...t, children: sort(t.children, comparators).map(map) } as TreeTableRowData<T>;
136-
return r;
137-
};
138-
return rows.map(map).sort(realComparator);
139-
}
140-
}
14117

14218
function useKeyFn<T>(id: TreeTableState<T>["id"], ...deps: any[]) {
14319
const ret = useCallback(
@@ -385,15 +261,14 @@ function CreateButton({
385261
level?: number;
386262
isGroup?: boolean;
387263
}) {
388-
const pad = (level - 1 == 0) ? undefined : `${1.2 * (level - 1)}em`;
264+
const mul = 1.12;
265+
if(level < 1) level == 1;
266+
const paddingLeft = `${mul * (level)}em`
389267
return (
390-
<tr>
391-
{!isGroup ? <td colSpan={1}></td> : null}
392-
<td
393-
colSpan={isGroup ? cols + 1 : cols}
394-
className="datacore-table-row"
395-
style={{paddingLeft: pad}}
396-
>
268+
<tr data-level={level} data-is-group={isGroup.toString()}>
269+
{/* {isGroup ? null : <td colSpan={1}></td>} */}
270+
<td colspan={1}></td>
271+
<td colSpan={cols} className="datacore-table-row" style={{ paddingLeft }}>
397272
<button className="dashed-default" style="padding: 0.75em; width: 100%" onClick={clickCallback}>
398273
Add item
399274
</button>
@@ -520,7 +395,7 @@ export function TreeTableRowCell<T>({
520395

521396
return (
522397
<td
523-
style={{ paddingLeft: isFirst ? `${(level - 1) * 1.2}em` : undefined }}
398+
style={{ paddingLeft: isFirst ? `${(level - 1) * 1.12}em` : undefined }}
524399
onDblClick={() => dispatch({ type: "editing-toggled", newValue: !editableState.isEditing })}
525400
className="datacore-table-cell"
526401
>
@@ -665,6 +540,7 @@ export function ControlledTreeTableView<T>(
665540
null,
666541
rows.length ? rows[rows.length - 1] : null
667542
)}
543+
isGroup={true}
668544
/>
669545
)}
670546
</tbody>

src/utils/tree.ts

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
2+
import { SortDirection } from "api/ui/views/table";
3+
import { GroupElement, Grouping, Groupings, Literal } from "expression/literal";
4+
5+
export interface TreeTableRowData<T> {
6+
value: T;
7+
children: TreeTableRowData<T>[];
8+
}
9+
export namespace TreeUtils {
10+
export function isTreeTableRowData<T>(data: any): data is TreeTableRowData<T> {
11+
return (
12+
"children" in data &&
13+
"value" in data &&
14+
!Array.isArray(data) &&
15+
Object.keys(data).length == 2 &&
16+
Array.isArray(data.children)
17+
);
18+
}
19+
export function countInTreeRow<T>(node: TreeTableRowData<T>, top: boolean = true): number {
20+
let result = 0;
21+
if (!top) result++;
22+
for (let n of node.children) result += countInTreeRow(n, false);
23+
return result;
24+
}
25+
export function ofArray<T>(source: T[], childFn: (el: T) => T[]): TreeTableRowData<T>[] {
26+
const mapper = (el: T): TreeTableRowData<T> => {
27+
return {
28+
value: el,
29+
children: childFn(el).map(mapper),
30+
} as TreeTableRowData<T>;
31+
};
32+
return source.map(mapper);
33+
}
34+
export function ofNode<T>(source: T, childFn: (el: T) => T[]): TreeTableRowData<T> {
35+
return {
36+
value: source,
37+
children: ofArray(childFn(source), childFn),
38+
};
39+
}
40+
41+
export function ofGrouping<T>(elements: Grouping<T>, childFn: (el: T) => T[]): Grouping<TreeTableRowData<T>> {
42+
const mapper = (l: T | GroupElement<T>): GroupElement<TreeTableRowData<T>> | TreeTableRowData<T> => {
43+
if (Groupings.isElementGroup(l))
44+
return { key: l.key, rows: l.rows.map(mapper) } as GroupElement<TreeTableRowData<T>>;
45+
return {
46+
value: l,
47+
children: childFn(l).map(mapper),
48+
} as TreeTableRowData<T>;
49+
};
50+
return elements.map(mapper) as Grouping<TreeTableRowData<T>>;
51+
}
52+
53+
export function count<T>(elements: Grouping<TreeTableRowData<T>> | GroupElement<TreeTableRowData<T>>): number {
54+
if (Groupings.isElementGroup(elements)) {
55+
return count(elements.rows);
56+
} else if (Groupings.isGrouping(elements)) {
57+
let result = 0;
58+
for (let group of elements) result += count(group.rows);
59+
return result;
60+
} else {
61+
return elements.reduce((pv, cv) => pv + countInTreeRow(cv), 0);
62+
}
63+
}
64+
65+
export function slice<T>(
66+
elements: Grouping<TreeTableRowData<T>>,
67+
start: number,
68+
end: number
69+
): Grouping<TreeTableRowData<T>> {
70+
let initial = [...Groupings.slice(elements, start, end)] as Grouping<TreeTableRowData<T>>;
71+
let index = 0,
72+
seen = 0;
73+
74+
for (let element of initial) {
75+
if (Groupings.isElementGroup(element)) {
76+
let groupSize = Groupings.count(elements);
77+
let groupStart = Math.min(seen, start);
78+
let groupEnd = Math.min(groupSize, end);
79+
(initial[index] as GroupElement<TreeTableRowData<T>>).rows = Groupings.slice(
80+
element.rows,
81+
groupStart,
82+
groupEnd
83+
);
84+
seen += groupSize;
85+
} else {
86+
seen += countInTreeRow(element);
87+
}
88+
index++;
89+
}
90+
return initial;
91+
}
92+
/** recursively sort a tree */
93+
export function sort<T, V = Literal>(
94+
rows: (TreeTableRowData<T> | GroupElement<TreeTableRowData<T>>)[],
95+
comparators: {
96+
fn: (a: V, b: V, ao: T, ab: T) => number;
97+
direction: SortDirection;
98+
actualValue: (obj: T) => V;
99+
}[]
100+
): (TreeTableRowData<T> | GroupElement<TreeTableRowData<T>>)[] {
101+
const realComparator = (
102+
a: TreeTableRowData<T> | GroupElement<TreeTableRowData<T>>,
103+
b: TreeTableRowData<T> | GroupElement<TreeTableRowData<T>>
104+
): number => {
105+
for (let comp of comparators) {
106+
const direction = comp.direction.toLocaleLowerCase() === "ascending" ? 1 : -1;
107+
let result = 0;
108+
if (Groupings.isElementGroup(a) && Groupings.isElementGroup(b)) {
109+
result = 0;
110+
} else if (!Groupings.isElementGroup(a) && !Groupings.isElementGroup(b)) {
111+
result =
112+
direction * comp.fn(comp.actualValue(a.value), comp.actualValue(b.value), a.value, b.value);
113+
}
114+
if (result != 0) return result;
115+
}
116+
return 0;
117+
};
118+
const map = (
119+
t: TreeTableRowData<T> | GroupElement<TreeTableRowData<T>>
120+
): TreeTableRowData<T> | GroupElement<TreeTableRowData<T>> => {
121+
let r;
122+
if (Groupings.isElementGroup(t))
123+
r = { ...t, rows: sort(t.rows, comparators).map(map) } as GroupElement<TreeTableRowData<T>>;
124+
else r = { ...t, children: sort(t.children, comparators).map(map) } as TreeTableRowData<T>;
125+
return r;
126+
};
127+
return rows.map(map).sort(realComparator);
128+
}
129+
}

0 commit comments

Comments
 (0)