From 422e43532b36c8cca387e0a64a280138593bb22a Mon Sep 17 00:00:00 2001 From: Terry Truong Date: Fri, 27 May 2022 19:37:25 +1000 Subject: Use static-layout for ancestry-bar and tutorial-pane --- src/App.vue | 217 +++++++++++++++------------------------- src/components/AncestryBar.vue | 50 ++------- src/components/SearchModal.vue | 2 +- src/components/TutorialPane.vue | 25 ++--- src/lib.ts | 18 ---- 5 files changed, 105 insertions(+), 207 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, 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({