1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | Object.defineProperty(exports, "__esModule", { value: true });
|
18 | exports.TreeSelectionState = exports.FocusableTreeSelection = void 0;
|
19 | const tree_iterator_1 = require("./tree-iterator");
|
20 | const tree_selection_1 = require("./tree-selection");
|
21 | var FocusableTreeSelection;
|
22 | (function (FocusableTreeSelection) {
|
23 | |
24 |
|
25 |
|
26 | function is(arg) {
|
27 | return tree_selection_1.TreeSelection.is(arg) && 'focus' in arg;
|
28 | }
|
29 | FocusableTreeSelection.is = is;
|
30 | |
31 |
|
32 |
|
33 |
|
34 | function focus(arg) {
|
35 | return is(arg) ? arg.focus : undefined;
|
36 | }
|
37 | FocusableTreeSelection.focus = focus;
|
38 | })(FocusableTreeSelection = exports.FocusableTreeSelection || (exports.FocusableTreeSelection = {}));
|
39 |
|
40 |
|
41 |
|
42 | class TreeSelectionState {
|
43 | constructor(tree, selectionStack = []) {
|
44 | this.tree = tree;
|
45 | this.selectionStack = selectionStack;
|
46 | }
|
47 | nextState(selection) {
|
48 | const { node, type } = {
|
49 | type: tree_selection_1.TreeSelection.SelectionType.DEFAULT,
|
50 | ...selection
|
51 | };
|
52 | switch (type) {
|
53 | case tree_selection_1.TreeSelection.SelectionType.DEFAULT: return this.handleDefault(this, node);
|
54 | case tree_selection_1.TreeSelection.SelectionType.TOGGLE: return this.handleToggle(this, node);
|
55 | case tree_selection_1.TreeSelection.SelectionType.RANGE: return this.handleRange(this, node);
|
56 | default: throw new Error(`Unexpected tree selection type: ${type}.`);
|
57 | }
|
58 | }
|
59 | selection() {
|
60 | const copy = this.checkNoDefaultSelection(this.selectionStack);
|
61 | const nodeIds = new Set();
|
62 | for (let i = 0; i < copy.length; i++) {
|
63 | const { node, type } = copy[i];
|
64 | if (tree_selection_1.TreeSelection.isRange(type)) {
|
65 | const selection = copy[i];
|
66 | for (const id of this.selectionRange(selection).map(n => n.id)) {
|
67 | nodeIds.add(id);
|
68 | }
|
69 | }
|
70 | else if (tree_selection_1.TreeSelection.isToggle(type)) {
|
71 | if (nodeIds.has(node.id)) {
|
72 | nodeIds.delete(node.id);
|
73 | }
|
74 | else {
|
75 | nodeIds.add(node.id);
|
76 | }
|
77 | }
|
78 | }
|
79 | return Array.from(nodeIds.keys()).map(id => this.tree.getNode(id)).filter(tree_selection_1.SelectableTreeNode.is).reverse();
|
80 | }
|
81 | get focus() {
|
82 | var _a;
|
83 | const copy = this.checkNoDefaultSelection(this.selectionStack);
|
84 | const candidate = (_a = copy[copy.length - 1]) === null || _a === void 0 ? void 0 : _a.focus;
|
85 | return this.toSelectableTreeNode(candidate);
|
86 | }
|
87 | get node() {
|
88 | var _a;
|
89 | const copy = this.checkNoDefaultSelection(this.selectionStack);
|
90 | return this.toSelectableTreeNode((_a = copy[copy.length - 1]) === null || _a === void 0 ? void 0 : _a.node);
|
91 | }
|
92 | handleDefault(state, node) {
|
93 | const { tree } = state;
|
94 | return new TreeSelectionState(tree, [{
|
95 | node,
|
96 | type: tree_selection_1.TreeSelection.SelectionType.TOGGLE,
|
97 | focus: node
|
98 | }]);
|
99 | }
|
100 | handleToggle(state, node) {
|
101 | const { tree, selectionStack } = state;
|
102 | const copy = this.checkNoDefaultSelection(selectionStack).slice();
|
103 | const focus = (() => {
|
104 | const allRanges = copy.filter(selection => tree_selection_1.TreeSelection.isRange(selection));
|
105 | for (let i = allRanges.length - 1; i >= 0; i--) {
|
106 | const latestRangeIndex = copy.indexOf(allRanges[i]);
|
107 | const latestRangeSelection = copy[latestRangeIndex];
|
108 | const latestRange = (latestRangeSelection === null || latestRangeSelection === void 0 ? void 0 : latestRangeSelection.focus) ? this.selectionRange(latestRangeSelection) : [];
|
109 | if (latestRange.indexOf(node) !== -1) {
|
110 | if (this.focus === latestRangeSelection.focus) {
|
111 | return latestRangeSelection.focus || node;
|
112 | }
|
113 | else {
|
114 | return this.focus;
|
115 | }
|
116 | }
|
117 | }
|
118 | return node;
|
119 | })();
|
120 | return new TreeSelectionState(tree, [...copy, {
|
121 | node,
|
122 | type: tree_selection_1.TreeSelection.SelectionType.TOGGLE,
|
123 | focus
|
124 | }]);
|
125 | }
|
126 | handleRange(state, node) {
|
127 | const { tree, selectionStack } = state;
|
128 | const copy = this.checkNoDefaultSelection(selectionStack).slice();
|
129 | let focus = FocusableTreeSelection.focus(copy[copy.length - 1]);
|
130 |
|
131 | if (tree_selection_1.TreeSelection.isRange(copy[copy.length - 1])) {
|
132 | const range = this.selectionRange(copy.pop());
|
133 |
|
134 |
|
135 | for (let i = copy.length - 1; i >= 0; i--) {
|
136 | if (range.indexOf(copy[i].node) !== -1) {
|
137 |
|
138 | focus = copy[i].focus;
|
139 | copy.splice(i, 1);
|
140 | }
|
141 | }
|
142 | }
|
143 | return new TreeSelectionState(tree, [...copy, {
|
144 | node,
|
145 | type: tree_selection_1.TreeSelection.SelectionType.RANGE,
|
146 | focus
|
147 | }]);
|
148 | }
|
149 | |
150 |
|
151 |
|
152 |
|
153 | selectionRange(selection) {
|
154 | const fromNode = selection.focus;
|
155 | const toNode = selection.node;
|
156 | if (fromNode === undefined) {
|
157 | return [];
|
158 | }
|
159 | if (toNode === fromNode) {
|
160 | return [toNode];
|
161 | }
|
162 | const { root } = this.tree;
|
163 | if (root === undefined) {
|
164 | return [];
|
165 | }
|
166 | const to = this.tree.validateNode(toNode);
|
167 | if (to === undefined) {
|
168 | return [];
|
169 | }
|
170 | const from = this.tree.validateNode(fromNode);
|
171 | if (from === undefined) {
|
172 | return [];
|
173 | }
|
174 | let started = false;
|
175 | let finished = false;
|
176 | const range = [];
|
177 | for (const node of new tree_iterator_1.DepthFirstTreeIterator(root, { pruneCollapsed: true })) {
|
178 | if (finished) {
|
179 | break;
|
180 | }
|
181 |
|
182 | if (node === from || node === to) {
|
183 | if (started) {
|
184 | finished = true;
|
185 | }
|
186 | else {
|
187 | started = true;
|
188 | }
|
189 | }
|
190 | if (started) {
|
191 | range.push(node);
|
192 | }
|
193 | }
|
194 |
|
195 | if (range.indexOf(from) > range.indexOf(to)) {
|
196 | range.reverse();
|
197 | }
|
198 | return range.filter(tree_selection_1.SelectableTreeNode.is);
|
199 | }
|
200 | toSelectableTreeNode(node) {
|
201 | if (!!node) {
|
202 | const candidate = this.tree.getNode(node.id);
|
203 | if (!!candidate) {
|
204 | if (tree_selection_1.SelectableTreeNode.is(candidate)) {
|
205 | return candidate;
|
206 | }
|
207 | else {
|
208 | console.warn(`Could not map to a selectable tree node. Node with ID: ${node.id} is not a selectable node.`);
|
209 | }
|
210 | }
|
211 | else {
|
212 | console.warn(`Could not map to a selectable tree node. Node does not exist with ID: ${node.id}.`);
|
213 | }
|
214 | }
|
215 | return undefined;
|
216 | }
|
217 | |
218 |
|
219 |
|
220 | checkNoDefaultSelection(selections) {
|
221 | if (selections.some(selection => selection.type === undefined || selection.type === tree_selection_1.TreeSelection.SelectionType.DEFAULT)) {
|
222 | throw new Error(`Unexpected DEFAULT selection type. [${selections.map(selection => `ID: ${selection.node.id} | ${selection.type}`).join(', ')}]`);
|
223 | }
|
224 | return selections;
|
225 | }
|
226 | }
|
227 | exports.TreeSelectionState = TreeSelectionState;
|
228 |
|
\ | No newline at end of file |