aboutsummaryrefslogtreecommitdiff
path: root/src/App.vue
diff options
context:
space:
mode:
authorTerry Truong <terry06890@gmail.com>2022-05-27 19:37:25 +1000
committerTerry Truong <terry06890@gmail.com>2022-05-27 19:37:25 +1000
commit422e43532b36c8cca387e0a64a280138593bb22a (patch)
treef7a64ba465e4e2c1dd0092e8cbb636a8eec944ed /src/App.vue
parent60f4e7b296d4b4dfafabcbabd670649277612170 (diff)
Use static-layout for ancestry-bar and tutorial-pane
Diffstat (limited to 'src/App.vue')
-rw-r--r--src/App.vue217
1 files changed, 83 insertions, 134 deletions
diff --git a/src/App.vue b/src/App.vue
index 4cc7aa0..e9db4c9 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -18,7 +18,7 @@ import type {TolMap} from './tol';
import {TolNode} from './tol';
import {LayoutNode, initLayoutTree, initLayoutMap, tryLayout} from './layout';
import type {LayoutOptions, LayoutTreeChg} from './layout';
-import {arraySum, randWeightedChoice, getScrollBarWidth} from './lib';
+import {arraySum, randWeightedChoice} from './lib';
import type {Action} from './lib';
// Note: Import paths lack a .ts or .js extension because .ts makes vue-tsc complain, and .js makes vite complain
@@ -78,8 +78,7 @@ const defaultUiOpts = {
// For other components
appBgColor: '#292524',
tileAreaOffset: 5, //px (space between root tile and display boundary)
- scrollGap: getScrollBarWidth(), //px (gap for overflown-root and ancestry-bar scrollbars, used to prevent overlap)
- ancestryBarSz: defaultLytOpts.minTileSz * 2, //px (breadth of ancestry-bar area)
+ ancestryBarImgSz: defaultLytOpts.minTileSz * 2, //px
ancestryBarBgColor: '#44403c',
ancestryTileMargin: 5, //px (gap between detached-ancestor tiles)
infoModalImgSz: 200,
@@ -123,18 +122,15 @@ export default defineComponent({
// Options
lytOpts: this.getLytOpts(),
uiOpts: this.getUiOpts(),
- // For window-resize handling
- width: document.documentElement.clientWidth,
- height: document.documentElement.clientHeight,
+ // For layout and resize-handling
+ mainAreaDims: [0, 0] as [number, number],
+ tileAreaDims: [0, 0] as [number, number],
pendingResizeHdlr: 0, // Set to a setTimeout() value
// Other
excessTolNodeThreshold: 1000, // Threshold where excess tolMap entries are removed (done on tile collapse)
};
},
computed: {
- wideArea(): boolean {
- return this.width >= this.height;
- },
// Nodes to show in ancestry-bar, with tol root first
detachedAncestors(): LayoutNode[] | null {
if (this.activeRoot == this.layoutTree){
@@ -148,59 +144,6 @@ export default defineComponent({
}
return ancestors.reverse();
},
- // Placement info for Tile and AncestryBar
- tileAreaPos(){
- let pos = [this.uiOpts.tileAreaOffset, this.uiOpts.tileAreaOffset] as [number, number];
- if (this.detachedAncestors != null){
- if (this.wideArea){
- pos[0] += this.uiOpts.ancestryBarSz;
- } else {
- pos[1] += this.uiOpts.ancestryBarSz;
- }
- }
- if (this.tutorialOpen){
- pos[1] += this.uiOpts.tutorialPaneSz;
- }
- return pos;
- },
- tileAreaDims(){
- let dims = [
- this.width - this.uiOpts.tileAreaOffset*2,
- this.height - this.uiOpts.tileAreaOffset*2
- ] as [number, number];
- if (this.detachedAncestors != null){
- if (this.wideArea){
- dims[0] -= this.uiOpts.ancestryBarSz;
- } else {
- dims[1] -= this.uiOpts.ancestryBarSz;
- }
- }
- if (this.tutorialOpen){
- dims[1] -= this.uiOpts.tutorialPaneSz;
- }
- return dims;
- },
- ancestryBarPos(): [number, number] {
- let pos = [0, 0] as [number, number];
- if (this.tutorialOpen){
- pos[1] += this.uiOpts.tutorialPaneSz;
- }
- return pos;
- },
- ancestryBarDims(): [number, number] {
- if (this.wideArea){
- let dims = [this.uiOpts.ancestryBarSz, this.height] as [number, number];
- if (this.tutorialOpen){
- dims[1] -= this.uiOpts.tutorialPaneSz;
- }
- return dims;
- } else {
- return [this.width, this.uiOpts.ancestryBarSz];
- }
- },
- tutorialPaneDims(): [number, number] {
- return [this.width, this.uiOpts.tutorialPaneSz];
- },
},
methods: {
// For tile expand/collapse events
@@ -221,12 +164,10 @@ export default defineComponent({
chg: {type: 'expand', node: layoutNode, tolMap: this.tolMap} as LayoutTreeChg,
layoutMap: this.layoutMap
};
- let success = tryLayout(
- this.activeRoot, this.tileAreaPos, this.tileAreaDims, this.lytOpts, lytFnOpts);
+ let success = tryLayout(this.activeRoot, [0,0], this.tileAreaDims, this.lytOpts, lytFnOpts);
// If expanding active-root with too many children to fit, allow overflow
if (!success && layoutNode == this.activeRoot){
- success = tryLayout(this.activeRoot, this.tileAreaPos,
- [this.tileAreaDims[0] - this.uiOpts.scrollGap, this.tileAreaDims[1]],
+ success = tryLayout(this.activeRoot, [0,0], this.tileAreaDims,
{...this.lytOpts, layoutType: 'flex-sqr'}, lytFnOpts);
if (success){
this.overflownRoot = true;
@@ -261,7 +202,7 @@ export default defineComponent({
return false;
}
this.setLastFocused(null);
- let success = tryLayout(this.activeRoot, this.tileAreaPos, this.tileAreaDims, this.lytOpts, {
+ let success = tryLayout(this.activeRoot, [0,0], this.tileAreaDims, this.lytOpts, {
allowCollapse: false,
chg: {type: 'collapse', node: layoutNode, tolMap: this.tolMap},
layoutMap: this.layoutMap
@@ -301,27 +242,27 @@ export default defineComponent({
let doExpansion = () => {
LayoutNode.hideUpward(layoutNode, this.layoutMap);
this.activeRoot = layoutNode;
- this.overflownRoot = false;
- let lytFnOpts = {
- allowCollapse: false,
- chg: {type: 'expand', node: layoutNode, tolMap: this.tolMap} as LayoutTreeChg,
- layoutMap: this.layoutMap
- };
- let success = tryLayout(
- this.activeRoot, this.tileAreaPos, this.tileAreaDims, this.lytOpts, lytFnOpts);
- if (!success){
- success = tryLayout(this.activeRoot, this.tileAreaPos,
- [this.tileAreaDims[0] - this.uiOpts.scrollGap, this.tileAreaDims[1]],
- {...this.lytOpts, layoutType: 'flex-sqr'}, lytFnOpts);
- if (success){
- this.overflownRoot = true;
+ return this.updateAreaDims().then(() => {
+ this.overflownRoot = false;
+ let lytFnOpts = {
+ allowCollapse: false,
+ chg: {type: 'expand', node: layoutNode, tolMap: this.tolMap} as LayoutTreeChg,
+ layoutMap: this.layoutMap
+ };
+ let success = tryLayout(this.activeRoot, [0,0], this.tileAreaDims, this.lytOpts, lytFnOpts);
+ if (!success){
+ success = tryLayout(this.activeRoot, [0,0], this.tileAreaDims,
+ {...this.lytOpts, layoutType: 'flex-sqr'}, lytFnOpts);
+ if (success){
+ this.overflownRoot = true;
+ }
}
- }
- // Check for failure
- if (!success){
- layoutNode.failFlag = !layoutNode.failFlag; // Triggers failure animation
- }
- return success;
+ // Check for failure
+ if (!success){
+ layoutNode.failFlag = !layoutNode.failFlag; // Triggers failure animation
+ }
+ return success;
+ });
};
// Check if data for node-to-expand exists, getting from server if needed
let tolNode = this.tolMap.get(layoutNode.name)!;
@@ -330,15 +271,13 @@ export default defineComponent({
urlPath += this.uiOpts.useReducedTree ? '&tree=reduced' : '';
return fetch(urlPath)
.then(response => response.json())
- .then(obj => {
- Object.getOwnPropertyNames(obj).forEach(key => {this.tolMap.set(key, obj[key])});
- doExpansion();
- })
+ .then(obj => {Object.getOwnPropertyNames(obj).forEach(key => {this.tolMap.set(key, obj[key])})})
+ .then(doExpansion)
.catch(error => {
console.log('ERROR loading tolnode data', error);
});
} else {
- return new Promise((resolve, reject) => resolve(doExpansion()));
+ return doExpansion();
}
},
onNonleafClickHeld(layoutNode: LayoutNode){
@@ -352,7 +291,7 @@ export default defineComponent({
}
LayoutNode.hideUpward(layoutNode, this.layoutMap);
this.activeRoot = layoutNode;
- this.relayoutWithCollapse();
+ this.updateAreaDims().then(() => this.relayoutWithCollapse());
},
onDetachedAncestorClick(layoutNode: LayoutNode){
if (!this.handleActionForTutorial('unhideAncestor')){
@@ -361,8 +300,7 @@ export default defineComponent({
this.setLastFocused(null);
LayoutNode.showDownward(layoutNode);
this.activeRoot = layoutNode;
- this.relayoutWithCollapse();
- this.overflownRoot = false;
+ this.updateAreaDims().then(() => this.relayoutWithCollapse());
},
// For tile-info events
onInfoIconClick(nodeName: string){
@@ -618,14 +556,14 @@ export default defineComponent({
onStartTutorial(){
if (this.tutorialOpen == false){
this.tutorialOpen = true;
- this.relayoutWithCollapse();
+ this.updateAreaDims().then(() => this.relayoutWithCollapse());
}
},
onTutorialClose(){
this.tutorialOpen = false;
this.welcomeOpen = false;
this.disabledActions.clear();
- this.relayoutWithCollapse();
+ this.updateAreaDims().then(() => this.relayoutWithCollapse());
},
onTutStageChg(disabledActions: Set<Action>, triggerAction: Action | null){
this.welcomeOpen = false;
@@ -651,12 +589,10 @@ export default defineComponent({
onResize(){
if (this.pendingResizeHdlr == 0){
this.pendingResizeHdlr = setTimeout(() => {
- this.width = document.documentElement.clientWidth;
- this.height = document.documentElement.clientHeight;
- this.uiOpts.scrollGap = getScrollBarWidth();
- this.relayoutWithCollapse();
- this.overflownRoot = false;
- this.pendingResizeHdlr = 0;
+ this.updateAreaDims().then(() => {
+ this.relayoutWithCollapse();
+ this.pendingResizeHdlr = 0;
+ });
}, 100);
}
},
@@ -683,7 +619,7 @@ export default defineComponent({
this.layoutTree = initLayoutTree(this.tolMap, this.layoutTree.name, 0);
this.activeRoot = this.layoutTree;
this.layoutMap = initLayoutMap(this.layoutTree);
- this.relayoutWithCollapse();
+ this.updateAreaDims().then(() => this.relayoutWithCollapse());
})
.catch(error => {
console.log('ERROR loading initial tolnode data', error);
@@ -737,17 +673,27 @@ export default defineComponent({
}
},
relayoutWithCollapse(){
- tryLayout(this.activeRoot, this.tileAreaPos, this.tileAreaDims, this.lytOpts,
+ tryLayout(this.activeRoot, [0,0], this.tileAreaDims, this.lytOpts,
{allowCollapse: true, layoutMap: this.layoutMap});
// Relayout again to allocate remaining tiles 'evenly'
- tryLayout(this.activeRoot, this.tileAreaPos, this.tileAreaDims, this.lytOpts,
+ tryLayout(this.activeRoot, [0,0], this.tileAreaDims, this.lytOpts,
{allowCollapse: false, layoutMap: this.layoutMap});
+ this.overflownRoot = false;
+ },
+ updateAreaDims(){
+ console.log('updating dims')
+ let mainAreaEl = this.$refs.mainArea as HTMLElement;
+ this.mainAreaDims = [mainAreaEl.offsetWidth, mainAreaEl.offsetHeight];
+ // Need to wait until ancestor-bar is laid-out before computing tileAreaDims
+ return this.$nextTick(() => {
+ let tileAreaEl = this.$refs.tileArea as HTMLElement;
+ this.tileAreaDims = [tileAreaEl.offsetWidth, tileAreaEl.offsetHeight];
+ });
},
},
created(){
window.addEventListener('resize', this.onResize);
window.addEventListener('keyup', this.onKeyUp);
- this.relayoutWithCollapse();
this.initTreeFromServer();
},
unmounted(){
@@ -763,33 +709,36 @@ export default defineComponent({
</script>
<template>
-<div class="absolute left-0 top-0 w-screen h-screen overflow-hidden" :style="{backgroundColor: uiOpts.appBgColor}">
- <!-- Note: Making the above enclosing div's width/height dynamic seems to cause white flashes when resizing -->
- <tile :layoutNode="layoutTree" :tolMap="tolMap" :lytOpts="lytOpts" :uiOpts="uiOpts"
- :overflownDim="overflownRoot ? tileAreaDims[1] : 0"
- @leaf-click="onLeafClick" @nonleaf-click="onNonleafClick"
- @leaf-click-held="onLeafClickHeld" @nonleaf-click-held="onNonleafClickHeld"
- @info-icon-click="onInfoIconClick"/>
- <ancestry-bar v-if="detachedAncestors != null"
- :pos="ancestryBarPos" :dims="ancestryBarDims" :nodes="detachedAncestors"
- :tolMap="tolMap" :lytOpts="lytOpts" :uiOpts="uiOpts"
- @detached-ancestor-click="onDetachedAncestorClick" @info-icon-click="onInfoIconClick"/>
- <tutorial-pane v-if="tutorialOpen"
- :pos="[0,0]" :dims="tutorialPaneDims" :uiOpts="uiOpts" :triggerFlag="tutTriggerFlag"
- :skipWelcome="!welcomeOpen" @tutorial-close="onTutorialClose" @tutorial-stage-chg="onTutStageChg"/>
- <!-- Icons -->
- <search-icon @click="onSearchIconClick"
- class="absolute bottom-[6px] right-[78px] w-[18px] h-[18px]
- text-white/40 hover:text-white hover:cursor-pointer"/>
- <play-icon @click="onPlayIconClick"
- class="absolute bottom-[6px] right-[54px] w-[18px] h-[18px]
- text-white/40 hover:text-white hover:cursor-pointer"/>
- <settings-icon @click="onSettingsIconClick"
- class="absolute bottom-[6px] right-[30px] w-[18px] h-[18px]
- text-white/40 hover:text-white hover:cursor-pointer"/>
- <help-icon @click="onHelpIconClick"
- class="absolute bottom-[6px] right-[6px] w-[18px] h-[18px]
- text-white/40 hover:text-white hover:cursor-pointer"/>
+<div class="absolute left-0 top-0 w-screen h-screen overflow-hidden flex flex-col"
+ :style="{backgroundColor: uiOpts.appBgColor}">
+ <div class="flex bg-black">
+ <h1 class="text-white text-bold px-2">Title</h1>
+ <!-- Icons -->
+ <search-icon @click="onSearchIconClick"
+ class="ml-auto mr-[6px] my-[6px] w-[18px] h-[18px] text-white/40 hover:text-white hover:cursor-pointer"/>
+ <play-icon @click="onPlayIconClick"
+ class="mr-[6px] my-[6px] w-[18px] h-[18px] text-white/40 hover:text-white hover:cursor-pointer"/>
+ <settings-icon @click="onSettingsIconClick"
+ class="mr-[6px] my-[6px] w-[18px] h-[18px] text-white/40 hover:text-white hover:cursor-pointer"/>
+ <help-icon @click="onHelpIconClick"
+ class="mr-[6px] my-[6px] w-[18px] h-[18px] text-white/40 hover:text-white hover:cursor-pointer"/>
+ </div>
+ <tutorial-pane v-if="tutorialOpen" :height="uiOpts.tutorialPaneSz + 'px'" class="grow-0 shrink-0"
+ :uiOpts="uiOpts" :triggerFlag="tutTriggerFlag" :skipWelcome="!welcomeOpen"
+ @tutorial-close="onTutorialClose" @tutorial-stage-chg="onTutStageChg"/>
+ <div :class="['flex', mainAreaDims[0] > mainAreaDims[1] ? 'flex-row' : 'flex-col', 'grow', 'min-h-0']" ref="mainArea">
+ <ancestry-bar v-if="detachedAncestors != null"
+ :nodes="detachedAncestors" :vert="mainAreaDims[0] > mainAreaDims[1]"
+ :tolMap="tolMap" :lytOpts="lytOpts" :uiOpts="uiOpts"
+ @detached-ancestor-click="onDetachedAncestorClick" @info-icon-click="onInfoIconClick"/>
+ <div class="relative m-[5px] grow" ref="tileArea">
+ <tile :layoutNode="layoutTree" :tolMap="tolMap" :lytOpts="lytOpts" :uiOpts="uiOpts"
+ :overflownDim="overflownRoot ? mainAreaDims[1] : 0"
+ @leaf-click="onLeafClick" @nonleaf-click="onNonleafClick"
+ @leaf-click-held="onLeafClickHeld" @nonleaf-click-held="onNonleafClickHeld"
+ @info-icon-click="onInfoIconClick"/>
+ </div>
+ </div>
<!-- Modals -->
<transition name="fade">
<tile-info-modal v-if="infoModalNodeName != null"