aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/TileTree.vue80
-rw-r--r--src/lib.ts33
2 files changed, 59 insertions, 54 deletions
diff --git a/src/components/TileTree.vue b/src/components/TileTree.vue
index b915601..0285505 100644
--- a/src/components/TileTree.vue
+++ b/src/components/TileTree.vue
@@ -5,7 +5,7 @@ import ParentBar from './ParentBar.vue';
import TileInfoModal from './TileInfoModal.vue';
import SearchModal from './SearchModal.vue';
import Settings from './Settings.vue';
-import {TolNode, LayoutNode, initLayoutTree, tryLayout} from '../lib';
+import {TolNode, LayoutNode, initLayoutTree, initLayoutMap, tryLayout} from '../lib';
import type {LayoutOptions} from '../lib';
// Import paths lack a .ts or .js extension because .ts makes vue-tsc complain, and .js makes vite complain
@@ -86,7 +86,7 @@ export default defineComponent({
return {
layoutTree: layoutTree,
activeRoot: layoutTree,
- layoutMap: this.initLayoutMap(layoutTree), // Maps names to LayoutNode objects
+ layoutMap: initLayoutMap(layoutTree), // Maps names to LayoutNode objects
tolMap: tolMap, // Maps names to TolNode objects
//
infoModalNode: null as TolNode | null, // Hides/unhides info modal, and provides the node to display
@@ -167,7 +167,8 @@ export default defineComponent({
if (!this.resizeThrottled){
this.width = document.documentElement.clientWidth;
this.height = document.documentElement.clientHeight;
- tryLayout(this.activeRoot, this.tileAreaPos, this.tileAreaDims, this.layoutOptions, true);
+ tryLayout(this.activeRoot, this.layoutMap,
+ this.tileAreaPos, this.tileAreaDims, this.layoutOptions, true);
// Prevent re-triggering until after a delay
this.resizeThrottled = true;
setTimeout(() => {this.resizeThrottled = false;}, this.resizeDelay);
@@ -175,33 +176,25 @@ export default defineComponent({
},
// For tile expand/collapse events
onInnerLeafClicked({layoutNode, domNode}: {layoutNode: LayoutNode, domNode?: HTMLElement}){
- let success = tryLayout(this.activeRoot, this.tileAreaPos, this.tileAreaDims, this.layoutOptions, false,
- {type: 'expand', node: layoutNode});
- if (success){
- layoutNode.children.forEach(n => this.layoutMap.set(n.tolNode.name, n));
- } else {
- if (domNode != null){
- // Trigger failure animation
- domNode.classList.remove('animate-expand-shrink');
- domNode.offsetWidth; // Triggers reflow
- domNode.classList.add('animate-expand-shrink');
- }
+ let success = tryLayout(this.activeRoot, this.layoutMap,
+ this.tileAreaPos, this.tileAreaDims, this.layoutOptions, false, {type: 'expand', node: layoutNode});
+ if (!success && domNode != null){
+ // Trigger failure animation
+ domNode.classList.remove('animate-expand-shrink');
+ domNode.offsetWidth; // Triggers reflow
+ domNode.classList.add('animate-expand-shrink');
}
return success;
},
onInnerHeaderClicked({layoutNode, domNode}: {layoutNode: LayoutNode, domNode?: HTMLElement}){
let oldChildren = layoutNode.children;
- let success = tryLayout(this.activeRoot, this.tileAreaPos, this.tileAreaDims, this.layoutOptions, false,
- {type: 'collapse', node: layoutNode});
- if (success){
- oldChildren.forEach(n => this.removeFromLayoutMap(n, this.layoutMap));
- } else {
- if (domNode != null){
- // Trigger failure animation
- domNode.classList.remove('animate-shrink-expand');
- domNode.offsetWidth; // Triggers reflow
- domNode.classList.add('animate-shrink-expand');
- }
+ let success = tryLayout(this.activeRoot, this.layoutMap,
+ this.tileAreaPos, this.tileAreaDims, this.layoutOptions, false, {type: 'collapse', node: layoutNode});
+ if (!success && domNode != null){
+ // Trigger failure animation
+ domNode.classList.remove('animate-shrink-expand');
+ domNode.offsetWidth; // Triggers reflow
+ domNode.classList.add('animate-shrink-expand');
}
return success;
},
@@ -213,8 +206,8 @@ export default defineComponent({
}
LayoutNode.hideUpward(layoutNode);
this.activeRoot = layoutNode;
- tryLayout(layoutNode, this.tileAreaPos, this.tileAreaDims, this.layoutOptions, true,
- {type: 'expand', node: layoutNode});
+ tryLayout(this.activeRoot, this.layoutMap,
+ this.tileAreaPos, this.tileAreaDims, this.layoutOptions, true, {type: 'expand', node: layoutNode});
},
onInnerHeaderClickHeld(layoutNode: LayoutNode){
if (layoutNode == this.activeRoot){
@@ -223,12 +216,12 @@ export default defineComponent({
}
LayoutNode.hideUpward(layoutNode);
this.activeRoot = layoutNode;
- tryLayout(layoutNode, this.tileAreaPos, this.tileAreaDims, this.layoutOptions, true);
+ tryLayout(this.activeRoot, this.layoutMap, this.tileAreaPos, this.tileAreaDims, this.layoutOptions, true);
},
onSepdParentClicked(layoutNode: LayoutNode){
LayoutNode.showDownward(layoutNode);
this.activeRoot = layoutNode;
- tryLayout(layoutNode, this.tileAreaPos, this.tileAreaDims, this.layoutOptions, true);
+ tryLayout(this.activeRoot, this.layoutMap, this.tileAreaPos, this.tileAreaDims, this.layoutOptions, true);
},
// For info modal events
onInnerInfoIconClicked(node: LayoutNode){
@@ -247,7 +240,7 @@ export default defineComponent({
this.settingsOpen = false;
},
onLayoutOptionChange(){
- tryLayout(this.activeRoot, this.tileAreaPos, this.tileAreaDims, this.layoutOptions, true);
+ tryLayout(this.activeRoot, this.layoutMap, this.tileAreaPos, this.tileAreaDims, this.layoutOptions, true);
},
//
onSearchIconClick(){
@@ -276,31 +269,18 @@ export default defineComponent({
}
}
},
- initLayoutMap(node: LayoutNode): Map<string,LayoutNode> {
- function helper(node: LayoutNode, map: Map<string,LayoutNode>){
- map.set(node.tolNode.name, node);
- node.children.forEach(n => helper(n, map));
- }
- let map = new Map();
- helper(node, map);
- return map;
- },
- removeFromLayoutMap(node: LayoutNode, map: Map<string,LayoutNode>){
- map.delete(node.tolNode.name);
- node.children.forEach(n => this.removeFromLayoutMap(n, map));
- },
expandToTolNode(tolNode: TolNode){
// Check if searched node is shown
if (this.layoutMap.get(tolNode.name) != null){
return;
}
// Get ancestor to expand
- let ancestor = tolNode.parent;
+ let ancestor = tolNode.parent!;
while (this.layoutMap.get(ancestor.name) == null){
- ancestor = ancestor.parent;
+ ancestor = ancestor.parent!;
}
// Attempt expansion
- let layoutNode = this.layoutMap.get(ancestor.name);
+ let layoutNode = this.layoutMap.get(ancestor.name)!;
let success = this.onInnerLeafClicked({layoutNode});
if (success){
console.log('Expanded ' + ancestor.name);
@@ -314,12 +294,12 @@ export default defineComponent({
return;
}
while (true){
- if (ancestor.parent.name == this.activeRoot.tolNode.name){
+ if (ancestor.parent!.name == this.activeRoot.tolNode.name){
break;
}
- ancestor = ancestor.parent;
+ ancestor = ancestor.parent!;
}
- layoutNode = this.layoutMap.get(ancestor.name);
+ layoutNode = this.layoutMap.get(ancestor.name)!;
this.onInnerHeaderClickHeld(layoutNode);
console.log('Expanded-to-view ' + ancestor.name);
setTimeout(() => this.expandToTolNode(tolNode), 300);
@@ -328,7 +308,7 @@ export default defineComponent({
created(){
window.addEventListener('resize', this.onResize);
window.addEventListener('keyup', this.onKeyUp);
- tryLayout(this.activeRoot, this.tileAreaPos, this.tileAreaDims, this.layoutOptions, true);
+ tryLayout(this.activeRoot, this.layoutMap, this.tileAreaPos, this.tileAreaDims, this.layoutOptions, true);
},
unmounted(){
window.removeEventListener('resize', this.onResize);
diff --git a/src/lib.ts b/src/lib.ts
index fcec19c..4daa1b2 100644
--- a/src/lib.ts
+++ b/src/lib.ts
@@ -75,7 +75,7 @@ export class LayoutNode {
}
// Copies render-relevant data to a given LayoutNode tree
// If a target node has more/less children, removes/gives own children
- copyTreeForRender(target: LayoutNode): void {
+ copyTreeForRender(target: LayoutNode, map?: LayoutMap): void {
target.pos = this.pos;
target.dims = this.dims;
target.showHeader = this.showHeader;
@@ -84,12 +84,18 @@ export class LayoutNode {
target.empSpc = this.empSpc; // Currently redundant, but maintains data-consistency
// Handle children
if (this.children.length == target.children.length){
- this.children.forEach((n,i) => n.copyTreeForRender(target.children[i]));
+ this.children.forEach((n,i) => n.copyTreeForRender(target.children[i], map));
} else if (this.children.length < target.children.length){
+ if (map != null){
+ target.children.forEach(child => removeFromLayoutMap(child, map));
+ }
target.children = [];
} else {
target.children = this.children;
target.children.forEach(n => {n.parent = target});
+ if (map != null){
+ target.children.forEach(child => {addToLayoutMap(child, map)});
+ }
}
}
// Assigns render-relevant data to this single node
@@ -157,6 +163,8 @@ export class SepSweptArea {
return new SepSweptArea([...this.pos], [...this.dims], this.sweptLeft);
}
}
+//
+export type LayoutMap = Map<string, LayoutNode>;
// Creates a LayoutNode representing a TolNode tree, up to a given depth (0 means just the root)
export function initLayoutTree(tol: TolNode, depth: number): LayoutNode {
@@ -174,10 +182,27 @@ export function initLayoutTree(tol: TolNode, depth: number): LayoutNode {
}
return initHelper(tol, depth);
}
+export function initLayoutMap(node: LayoutNode): LayoutMap {
+ function helper(node: LayoutNode, map: LayoutMap){
+ map.set(node.tolNode.name, node);
+ node.children.forEach(n => helper(n, map));
+ }
+ let map = new Map();
+ helper(node, map);
+ return map;
+}
+function removeFromLayoutMap(node: LayoutNode, map: LayoutMap){
+ map.delete(node.tolNode.name);
+ node.children.forEach(n => removeFromLayoutMap(n, map));
+}
+function addToLayoutMap(node: LayoutNode, map: LayoutMap){
+ map.set(node.tolNode.name, node);
+ node.children.forEach(n => addToLayoutMap(n, map));
+}
// Attempts layout on a LayoutNode's corresponding TolNode tree, for an area with given xy-position and width+height
// 'allowCollapse' allows the layout algorithm to collapse nodes to avoid layout failure
// 'chg' allows for performing layout after expanding/collapsing a node
-export function tryLayout(layoutTree: LayoutNode, pos: [number,number], dims: [number,number],
+export function tryLayout(layoutTree: LayoutNode, layoutMap: LayoutMap, pos: [number,number], dims: [number,number],
options: LayoutOptions, allowCollapse: boolean = false, chg?: LayoutTreeChg){
// Create a new LayoutNode tree, in case of layout failure
let tempTree = layoutTree.cloneNodeTree(chg);
@@ -192,7 +217,7 @@ export function tryLayout(layoutTree: LayoutNode, pos: [number,number], dims: [n
tempTree.pos[0] += (dims[0] - tempTree.dims[0]) / 2;
tempTree.pos[1] += (dims[1] - tempTree.dims[1]) / 2;
// Apply to active LayoutNode tree
- tempTree.copyTreeForRender(layoutTree);
+ tempTree.copyTreeForRender(layoutTree, layoutMap);
}
return success;
}