aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/Tile.vue19
-rw-r--r--src/components/TileTree.vue35
-rw-r--r--src/lib.ts27
3 files changed, 58 insertions, 23 deletions
diff --git a/src/components/Tile.vue b/src/components/Tile.vue
index b1454ac..4009068 100644
--- a/src/components/Tile.vue
+++ b/src/components/Tile.vue
@@ -25,7 +25,6 @@ const defaultOptions = {
export default defineComponent({
props: {
layoutNode: {type: Object as PropType<LayoutNode>, required: true},
- isRoot: {type: Boolean, default: false},
// Settings from parent component
headerSz: {type: Number, required: true},
tileSpacing: {type: Number, required: true},
@@ -58,17 +57,17 @@ export default defineComponent({
return {
// Places div using layoutNode, with centering if root
position: 'absolute',
- left: this.isRoot ? '50%' : this.layoutNode.pos[0] + 'px',
- top: this.isRoot ? '50%' : this.layoutNode.pos[1] + 'px',
- transform: this.isRoot ? 'translate(-50%, -50%)' : 'none',
- width: this.layoutNode.dims[0] + 'px',
- height: this.layoutNode.dims[1] + 'px',
+ left: (this.layoutNode.hidden ? 0 : this.layoutNode.pos[0]) + 'px',
+ top: (this.layoutNode.hidden ? 0 : this.layoutNode.pos[1]) + 'px',
+ width: (this.layoutNode.hidden ? 0 : this.layoutNode.dims[0]) + 'px',
+ height: (this.layoutNode.hidden ? 0 : this.layoutNode.dims[1]) + 'px',
+ visibility: this.layoutNode.hidden ? 'hidden' : 'visible',
// Other bindings
transitionDuration: this.transitionDuration + 'ms',
zIndex: String(this.zIdx),
overflow: String(this.overflow),
// Static styles
- transitionProperty: 'left, top, width, height',
+ transitionProperty: 'left, top, width, height, visibility',
transitionTimingFunction: 'ease-out',
// CSS variables
'--nonLeafBgColor': this.nonLeafBgColor,
@@ -91,7 +90,7 @@ export default defineComponent({
position: 'absolute',
left: this.options.leafHeaderX + 'px',
top: this.options.leafHeaderY + 'px',
- maxWidth: (this.layoutNode.dims[0] - this.options.leafHeaderX * 2) + 'px',
+ maxWidth: this.layoutNode.hidden ? 0 : this.layoutNode.dims[0] - this.options.leafHeaderX * 2 + 'px',
height: this.options.leafHeaderFontSz + 'px',
lineHeight: this.options.leafHeaderFontSz + 'px',
fontSize: this.options.leafHeaderFontSz + 'px',
@@ -167,6 +166,10 @@ export default defineComponent({
methods: {
// For click handling
onLeafClick(evt: UIEvent){
+ if (!this.isExpandable){
+ console.log('Ignored click on non-expandable leaf node');
+ return;
+ }
let prepForTransition = () => {
(this.$refs.leaf as Element).classList.replace('shadow-highlight', 'shadow-normal');
// Temporary changes to prevent content overlap and overflow
diff --git a/src/components/TileTree.vue b/src/components/TileTree.vue
index 7f2f554..2cc2d53 100644
--- a/src/components/TileTree.vue
+++ b/src/components/TileTree.vue
@@ -40,8 +40,10 @@ const defaultOtherOptions = {
// Collects events about tile expansion/collapse and window-resize, and initiates relayout of tiles
export default defineComponent({
data(){
+ let layoutTree = new LayoutTree(tol, defaultLayoutOptions, 0);
return {
- layoutTree: new LayoutTree(tol, defaultLayoutOptions, 0),
+ layoutTree: layoutTree,
+ activeRoot: layoutTree.root,
layoutOptions: {...defaultLayoutOptions},
otherOptions: {...defaultOtherOptions},
width: document.documentElement.clientWidth - (defaultOtherOptions.rootOffset * 2),
@@ -55,7 +57,7 @@ export default defineComponent({
// Update data and relayout tiles
this.width = document.documentElement.clientWidth - (this.otherOptions.rootOffset * 2);
this.height = document.documentElement.clientHeight - (this.otherOptions.rootOffset * 2);
- if (!this.layoutTree.tryLayout([0,0], [this.width,this.height], true)){
+ if (!this.layoutTree.tryLayout(this.activeRoot, [0,0], [this.width,this.height], true)){
console.log('Unable to layout tree');
}
// Prevent re-triggering until after a delay
@@ -64,11 +66,7 @@ export default defineComponent({
}
},
onInnerLeafClicked({layoutNode, domNode}: {layoutNode: LayoutNode, domNode: HTMLElement}){
- if (layoutNode.tolNode.children.length == 0){
- //console.log('Tile to expand has no children');
- return;
- }
- let success = this.layoutTree.tryLayout([0,0], [this.width,this.height], false,
+ let success = this.layoutTree.tryLayout(this.activeRoot, [0,0], [this.width,this.height], false,
{type: 'expand', node: layoutNode});
if (!success){
// Trigger failure animation
@@ -79,7 +77,7 @@ export default defineComponent({
}
},
onInnerHeaderClicked({layoutNode, domNode}: {layoutNode: LayoutNode, domNode: HTMLElement}){
- let success = this.layoutTree.tryLayout([0,0], [this.width,this.height], false,
+ let success = this.layoutTree.tryLayout(this.activeRoot, [0,0], [this.width,this.height], false,
{type: 'collapse', node: layoutNode});
if (!success){
// Trigger failure animation
@@ -90,15 +88,28 @@ export default defineComponent({
}
},
onInnerLeafDblClicked(layoutNode: LayoutNode){
- console.log('double clicked leaf: ' + layoutNode.tolNode.name);
+ if (layoutNode == this.activeRoot){
+ console.log('Ignored expand-to-view on root node');
+ return;
+ }
+ LayoutNode.hideUpward(layoutNode);
+ this.activeRoot = layoutNode;
+ this.layoutTree.tryLayout(layoutNode, [0,0], [this.width,this.height], true,
+ {type: 'expand', node: layoutNode});
},
onInnerHeaderDblClicked(layoutNode: LayoutNode){
- console.log('double clicked header: ' + layoutNode.tolNode.name);
+ if (layoutNode.parent == null){
+ console.log('Ignored expand-to-view on root node');
+ return;
+ }
+ LayoutNode.hideUpward(layoutNode);
+ this.activeRoot = layoutNode;
+ this.layoutTree.tryLayout(layoutNode, [0,0], [this.width,this.height], true);
},
},
created(){
window.addEventListener('resize', this.onResize);
- if (!this.layoutTree.tryLayout([0,0], [this.width,this.height], true)){
+ if (!this.layoutTree.tryLayout(this.activeRoot, [0,0], [this.width,this.height], true)){
console.log('Unable to layout tree');
}
},
@@ -115,7 +126,7 @@ export default defineComponent({
<div class="h-screen bg-stone-800">
<tile :layoutNode="layoutTree.root"
:headerSz="layoutOptions.headerSz" :tileSpacing="layoutOptions.tileSpacing"
- :transitionDuration="otherOptions.transitionDuration" :isRoot="true"
+ :transitionDuration="otherOptions.transitionDuration"
@leaf-clicked="onInnerLeafClicked" @header-clicked="onInnerHeaderClicked"
@leaf-dbl-clicked="onInnerLeafDblClicked" @header-dbl-clicked="onInnerHeaderDblClicked"/>
</div>
diff --git a/src/lib.ts b/src/lib.ts
index 5566fba..c4bea0b 100644
--- a/src/lib.ts
+++ b/src/lib.ts
@@ -40,9 +40,10 @@ export class LayoutTree {
// Attempts layout of TolNode tree, for an area with given xy-coordinate and width+height (in pixels)
// 'allowCollapse' allows the layout algorithm to collapse nodes to avoid layout failure
// 'chg' allows for performing layout after expanding/collapsing a node
- tryLayout(pos: [number,number], dims: [number,number], allowCollapse: boolean = false, chg?: LayoutTreeChg){
+ tryLayout(root: LayoutNode, pos: [number,number], dims: [number,number], allowCollapse: boolean = false,
+ chg?: LayoutTreeChg){
// Create a new LayoutNode tree, keeping the old one in case of layout failure
- let tempTree = this.root.cloneNodeTree(chg);
+ let tempTree = root.cloneNodeTree(chg);
let success: boolean;
switch (this.options.layoutType){
case 'sqr': success = sqrLayout(tempTree, pos, dims, true, allowCollapse, this.options); break;
@@ -50,7 +51,11 @@ export class LayoutTree {
case 'sweep': success = sweepLayout(tempTree, pos, dims, true, allowCollapse, this.options); break;
}
if (success){
- tempTree.copyTreeForRender(this.root);
+ // Center root in layout area
+ 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(root);
}
return success;
}
@@ -77,6 +82,7 @@ export class LayoutNode {
dims: [number, number];
showHeader: boolean;
sepSweptArea: SepSweptArea | null;
+ hidden: boolean;
// Used for layout heuristics and info display
dCount: number; // Number of descendant leaf nodes
depth: number; // Number of ancestor nodes
@@ -90,6 +96,7 @@ export class LayoutNode {
this.dims = [0,0];
this.showHeader = false;
this.sepSweptArea = null;
+ this.hidden = false;
this.dCount = children.length == 0 ? 1 : arraySum(children.map(n => n.dCount));
this.depth = 0;
this.empSpc = 0;
@@ -155,6 +162,20 @@ export class LayoutNode {
node = node.parent;
}
}
+ //
+ static hideUpward(node: LayoutNode){
+ if (node.parent != null){
+ node.parent.hidden = true;
+ node.parent.children.filter(n => n != node).forEach(n => LayoutNode.hideDownward(n));
+ LayoutNode.hideUpward(node.parent);
+ }
+ }
+ static hideDownward(node: LayoutNode){
+ node.hidden = true;
+ node.children.forEach(n => {
+ LayoutNode.hideDownward(n)
+ });
+ }
}
export type LayoutTreeChg = {
type: 'expand' | 'collapse';