aboutsummaryrefslogtreecommitdiff
path: root/src/App.vue
diff options
context:
space:
mode:
Diffstat (limited to 'src/App.vue')
-rw-r--r--src/App.vue436
1 files changed, 194 insertions, 242 deletions
diff --git a/src/App.vue b/src/App.vue
index b28531a..cf25b18 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,114 +1,114 @@
<script lang="ts">
import {defineComponent, PropType} from 'vue';
-//
+// Components
import Tile from './components/Tile.vue';
import AncestryBar from './components/AncestryBar.vue';
import TileInfoModal from './components/TileInfoModal.vue';
-import SearchModal from './components/SearchModal.vue';
import HelpModal from './components/HelpModal.vue';
+import SearchModal from './components/SearchModal.vue';
import SettingsPane from './components/SettingsPane.vue';
-//
+// Icons
+import HelpIcon from './components/icon/HelpIcon.vue';
import SearchIcon from './components/icon/SearchIcon.vue';
import PlayIcon from './components/icon/PlayIcon.vue';
-import HelpIcon from './components/icon/HelpIcon.vue';
import SettingsIcon from './components/icon/SettingsIcon.vue';
-//
+// Other
import {TolNode, TolNodeRaw, tolFromRaw, getTolMap} from './tol';
import {LayoutNode, initLayoutTree, initLayoutMap, tryLayout} from './layout';
import type {LayoutOptions} from './layout';
import {arraySum, randWeightedChoice} from './util';
-// Import paths lack a .ts or .js extension because .ts makes vue-tsc complain, and .js makes vite complain
+// Note: Import paths lack a .ts or .js extension because .ts makes vue-tsc complain, and .js makes vite complain
+
+// Type representing auto-mode actions
+type Action = 'move across' | 'move down' | 'move up' |
+ 'expand' | 'collapse' | 'expand to view' | 'expand ancestry bar';
+// Used in auto-mode to help avoid action cycles
+function getReverseAction(action: Action): Action | null {
+ let reversePairs: Action[][] = [
+ ['move down', 'move up'],
+ ['expand', 'collapse'],
+ ['expand to view', 'expand ancestry bar'],
+ ];
+ let pair = reversePairs.find(pair => pair.includes(action));
+ if (pair != null){
+ return pair[0] == action ? pair[1] : pair[0];
+ } else {
+ return null;
+ }
+}
-// Obtain tree-of-life data
+// Get tree-of-life data
import tolRaw from './tolData.json';
-const tol: TolNode = tolFromRaw(tolRaw);
+const tol = tolFromRaw(tolRaw);
const tolMap = getTolMap(tol);
-// Configurable settings
+// Configurable options
const defaultLytOpts: LayoutOptions = {
tileSpacing: 8, //px
- headerSz: 20, //px
+ headerSz: 22, //px
minTileSz: 50, //px
maxTileSz: 200, //px
+ // Layout-algorithm related
layoutType: 'sweep', //'sqr' | 'rect' | 'sweep'
rectMode: 'auto first-row', //'horz' | 'vert' | 'linear' | 'auto' | 'auto first-row'
sweepMode: 'left', //'left' | 'top' | 'shorter' | 'auto'
sweptNodesPrio: 'pow-2/3', //'linear' | 'sqrt' | 'pow-2/3'
- sweepingToParent: true,
+ sweepToParent: true,
};
const defaultUiOpts = {
- // For leaf/non_leaf tile and detached-ancestor components
+ // For tiles
borderRadius: 5, //px
shadowNormal: '0 0 2px black',
shadowHighlight: '0 0 1px 2px greenyellow',
shadowFocused: '0 0 1px 2px orange',
- // For leaf and detached-ancestor components
- imgTilePadding: 4, //px
- imgTileFontSz: 15, //px
- imgTileColor: '#fafaf9',
- expandableImgTileColor: 'greenyellow', //yellow, greenyellow, turquoise,
- // For non-leaf tile-group components
- nonLeafBgColors: ['#44403c', '#57534e'], //tiles at depth N use the Nth color, repeating from the start as needed
- nonLeafHeaderFontSz: 15, //px
- nonLeafHeaderColor: '#fafaf9',
- nonLeafHeaderBgColor: '#1c1917',
- // For tile-info modal
+ infoIconSz: 18, //px
+ infoIconMargin: 2, //px
+ // For leaf tiles
+ leafTilePadding: 4, //px
+ leafHeaderFontSz: 15, //px
+ leafHeaderColor: '#fafaf9',
+ leafHeaderExColor: 'greenyellow', //yellow, greenyellow, turquoise,
+ // For non-leaf tiles
+ nonleafBgColors: ['#44403c', '#57534e'], //tiles at depth N use the Nth color, repeating from the start as needed
+ nonleafHeaderFontSz: 15, //px
+ nonleafHeaderColor: '#fafaf9',
+ nonleafHeaderBgColor: '#1c1917',
+ // For other components
+ appBgColor: '#292524',
+ tileAreaOffset: 5, //px (space between root tile and display boundary)
+ ancestryBarSz: defaultLytOpts.minTileSz * 2, //px (breadth of ancestry-bar area)
+ ancestryBarBgColor: '#44403c',
+ ancestryTileMargin: 5, //px (gap between detached-ancestor tiles)
+ ancestryBarScrollGap: 10, //px (gap for ancestry-bar scrollbar, used to prevent overlap with tiles)
infoModalImgSz: 200,
+ autoWaitTime: 500, //ms (time to wait between actions (with their transitions))
// Timing related
- transitionDuration: 300, //ms
+ tileChgDuration: 300, //ms (for tile move/expand/collapse)
clickHoldDuration: 400, //ms (duration after mousedown when a click-and-hold is recognised)
};
-const defaultOwnOptions = {
- tileAreaOffset: 5, //px (space between root tile and display boundary)
- ancestryBarSz: defaultLytOpts.minTileSz * 2, //px (breadth of ancestry-bar area)
-};
-
-// Type representing auto-mode actions
-type Action = 'move across' | 'move down' | 'move up' | 'expand' | 'collapse' | 'expand to view' | 'expand ancestry bar';
-// Used in auto-mode to help avoid action cycles
-function getReverseAction(action: Action): Action | null {
- switch (action){
- case 'move across':
- return null;
- case 'move down':
- return 'move up';
- case 'move up':
- return 'move down';
- case 'expand':
- return 'collapse';
- case 'collapse':
- return 'expand';
- case 'expand to view':
- return 'expand ancestry bar';
- case 'expand ancestry bar':
- return 'expand to view';
- }
-}
-// Holds a tree structure representing a subtree of 'tol' to be rendered
-// Collects events about tile expansion/collapse and window-resize, and initiates relayout of tiles
export default defineComponent({
data(){
let layoutTree = initLayoutTree(tol, 0);
return {
layoutTree: layoutTree,
- activeRoot: layoutTree,
+ activeRoot: layoutTree, // Differs from layoutTree root when expand-to-view is used
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
+ // Modals and settings related
+ infoModalNode: null as TolNode | null, // Node to display info for, or null
+ helpOpen: false,
searchOpen: false,
settingsOpen: false,
+ // For search and auto-mode
+ modeRunning: false,
lastFocused: null as LayoutNode | null,
- animationActive: false,
- autoWaitTime: 500, //ms (in auto mode, time to wait after an action ends)
- autoPrevAction: null as Action | null, // Used in auto-mode for reducing action cycles
- autoPrevActionFail: false, // Used in auto-mode to avoid re-trying a failed expand/collapse
- helpOpen: false,
+ // For auto-mode
+ autoPrevAction: null as Action | null, // Used to help prevent action cycles
+ autoPrevActionFail: false, // Used to avoid re-trying a failed expand/collapse
// Options
lytOpts: {...defaultLytOpts},
uiOpts: {...defaultUiOpts},
- ...defaultOwnOptions,
// For window-resize handling
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
@@ -117,9 +117,10 @@ export default defineComponent({
};
},
computed: {
- wideArea(): boolean{
+ 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){
return null;
@@ -132,166 +133,114 @@ export default defineComponent({
}
return ancestors.reverse();
},
+ // Placement info for Tile and AncestryBar
tileAreaPos(){
- let pos = [this.tileAreaOffset, this.tileAreaOffset] as [number, number];
+ let pos = [this.uiOpts.tileAreaOffset, this.uiOpts.tileAreaOffset] as [number, number];
if (this.detachedAncestors != null){
if (this.wideArea){
- pos[0] += this.ancestryBarSz;
+ pos[0] += this.uiOpts.ancestryBarSz;
} else {
- pos[1] += this.ancestryBarSz;
+ pos[1] += this.uiOpts.ancestryBarSz;
}
}
return pos;
},
tileAreaDims(){
let dims = [
- this.width - this.tileAreaOffset*2,
- this.height - this.tileAreaOffset*2
+ 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.ancestryBarSz;
+ dims[0] -= this.uiOpts.ancestryBarSz;
} else {
- dims[1] -= this.ancestryBarSz;
+ dims[1] -= this.uiOpts.ancestryBarSz;
}
}
return dims;
},
ancestryBarDims(): [number, number] {
if (this.wideArea){
- return [this.ancestryBarSz, this.height];
+ return [this.uiOpts.ancestryBarSz, this.height];
} else {
- return [this.width, this.ancestryBarSz];
+ return [this.width, this.uiOpts.ancestryBarSz];
}
},
- styles(): Record<string,string> {
- return {
- position: 'absolute',
- left: '0',
- top: '0',
- width: '100vw', // Making this dynamic causes white flashes when resizing
- height: '100vh',
- backgroundColor: '#292524',
- overflow: 'hidden',
- };
- },
},
methods: {
- onResize(){
- if (!this.resizeThrottled){
- this.width = document.documentElement.clientWidth;
- this.height = document.documentElement.clientHeight;
- tryLayout(this.activeRoot, this.layoutMap,
- this.tileAreaPos, this.tileAreaDims, this.lytOpts, true);
- // Prevent re-triggering until after a delay
- this.resizeThrottled = true;
- setTimeout(() => {this.resizeThrottled = false;}, this.resizeDelay);
- }
- },
// For tile expand/collapse events
- onInnerLeafClicked(layoutNode: LayoutNode){
- let success = tryLayout(this.activeRoot, this.layoutMap,
- this.tileAreaPos, this.tileAreaDims, this.lytOpts, false, {type: 'expand', node: layoutNode});
+ onLeafClick(layoutNode: LayoutNode){
+ let success = tryLayout(this.activeRoot, this.tileAreaPos, this.tileAreaDims, this.lytOpts,
+ {allowCollapse: false, chg: {type: 'expand', node: layoutNode}, layoutMap: this.layoutMap});
if (!success){
- layoutNode.expandFailFlag = !layoutNode.expandFailFlag; // Triggers failure animation
+ layoutNode.failFlag = !layoutNode.failFlag; // Triggers failure animation
}
return success;
},
- onInnerHeaderClicked(layoutNode: LayoutNode){
- let oldChildren = layoutNode.children;
- let success = tryLayout(this.activeRoot, this.layoutMap,
- this.tileAreaPos, this.tileAreaDims, this.lytOpts, false, {type: 'collapse', node: layoutNode});
+ onNonleafClick(layoutNode: LayoutNode){
+ let success = tryLayout(this.activeRoot, this.tileAreaPos, this.tileAreaDims, this.lytOpts,
+ {allowCollapse: false, chg: {type: 'collapse', node: layoutNode}, layoutMap: this.layoutMap});
if (!success){
- layoutNode.collapseFailFlag = !layoutNode.collapseFailFlag; // Triggers failure animation
+ layoutNode.failFlag = !layoutNode.failFlag; // Triggers failure animation
}
return success;
},
- // For expand-to-view events
- onInnerLeafClickHeld(layoutNode: LayoutNode){
+ // For expand-to-view and ancestry-bar events
+ onLeafClickHeld(layoutNode: LayoutNode){
if (layoutNode == this.activeRoot){
- console.log('Ignored expand-to-view on root node');
+ console.log('Ignored expand-to-view on active-root node');
return;
}
LayoutNode.hideUpward(layoutNode);
this.activeRoot = layoutNode;
- tryLayout(this.activeRoot, this.layoutMap,
- this.tileAreaPos, this.tileAreaDims, this.lytOpts, true, {type: 'expand', node: layoutNode});
+ tryLayout(this.activeRoot, this.tileAreaPos, this.tileAreaDims, this.lytOpts,
+ {allowCollapse: true, chg: {type: 'expand', node: layoutNode}, layoutMap: this.layoutMap});
},
- onInnerHeaderClickHeld(layoutNode: LayoutNode){
+ onNonleafClickHeld(layoutNode: LayoutNode){
if (layoutNode == this.activeRoot){
console.log('Ignored expand-to-view on active-root node');
return;
}
LayoutNode.hideUpward(layoutNode);
this.activeRoot = layoutNode;
- tryLayout(this.activeRoot, this.layoutMap, this.tileAreaPos, this.tileAreaDims, this.lytOpts, true);
+ tryLayout(this.activeRoot, this.tileAreaPos, this.tileAreaDims, this.lytOpts,
+ {allowCollapse: true, layoutMap: this.layoutMap, });
},
- onDetachedAncestorClicked(layoutNode: LayoutNode){
+ onDetachedAncestorClick(layoutNode: LayoutNode){
LayoutNode.showDownward(layoutNode);
this.activeRoot = layoutNode;
- tryLayout(this.activeRoot, this.layoutMap, this.tileAreaPos, this.tileAreaDims, this.lytOpts, true);
+ tryLayout(this.activeRoot, this.tileAreaPos, this.tileAreaDims, this.lytOpts,
+ {allowCollapse: true, layoutMap: this.layoutMap});
},
- // For info modal events
- onInnerInfoIconClicked(node: LayoutNode){
- this.closeModesAndSettings();
+ // For tile-info events
+ onInfoIconClick(node: LayoutNode){
+ this.resetMode();
this.infoModalNode = node.tolNode;
},
- onInfoModalClose(){
- this.infoModalNode = null;
- },
- //
- onSettingsIconClick(){
- this.closeModesAndSettings();
- this.settingsOpen = true;
- },
- onSettingsClose(){
- this.settingsOpen = false;
- },
- onLayoutOptionChange(){
- tryLayout(this.activeRoot, this.layoutMap, this.tileAreaPos, this.tileAreaDims, this.lytOpts, true);
+ // For help events
+ onHelpIconClick(){
+ this.resetMode();
+ this.helpOpen = true;
},
- //
+ // For search events
onSearchIconClick(){
- this.closeModesAndSettings();
+ this.resetMode();
this.searchOpen = true;
},
- onSearchClose(){
- this.searchOpen = false;
- },
onSearchNode(tolNode: TolNode){
this.searchOpen = false;
- this.animationActive = true;
+ this.modeRunning = true;
this.expandToTolNode(tolNode);
},
- //
- closeModesAndSettings(){
- this.infoModalNode = null;
- this.searchOpen = false;
- this.helpOpen = false;
- this.settingsOpen = false;
- this.animationActive = false;
- this.setLastFocused(null);
- },
- onKeyUp(evt: KeyboardEvent){
- if (evt.key == 'Escape'){
- this.closeModesAndSettings();
- } else if (evt.key == 'F' && evt.ctrlKey){
- if (!this.searchOpen){
- this.onSearchIconClick();
- } else {
- (this.$refs.searchModal as InstanceType<typeof SearchModal>).focusInput();
- }
- }
- },
expandToTolNode(tolNode: TolNode){
- if (!this.animationActive){
+ if (!this.modeRunning){
return;
}
- // Check if searched node is shown
+ // Check if searched node is displayed
let layoutNode = this.layoutMap.get(tolNode.name);
if (layoutNode != null && !layoutNode.hidden){
this.setLastFocused(layoutNode);
- this.animationActive = false;
+ this.modeRunning = false;
return;
}
// Get nearest in-layout-tree ancestor
@@ -300,28 +249,27 @@ export default defineComponent({
ancestor = ancestor.parent!;
}
layoutNode = this.layoutMap.get(ancestor.name)!;
- // If hidden, expand ancestor in ancestry-bar
+ // If hidden, expand self/ancestor in ancestry-bar
if (layoutNode.hidden){
- // Get self/ancestor in ancestry-bar
while (!this.detachedAncestors!.includes(layoutNode)){
ancestor = ancestor.parent!;
layoutNode = this.layoutMap.get(ancestor.name)!;
}
- this.onDetachedAncestorClicked(layoutNode!);
- setTimeout(() => this.expandToTolNode(tolNode), this.uiOpts.transitionDuration);
+ this.onDetachedAncestorClick(layoutNode!);
+ setTimeout(() => this.expandToTolNode(tolNode), this.uiOpts.tileChgDuration);
return;
}
// Attempt tile-expand
- let success = this.onInnerLeafClicked(layoutNode);
+ let success = this.onLeafClick(layoutNode);
if (success){
- setTimeout(() => this.expandToTolNode(tolNode), this.uiOpts.transitionDuration);
+ setTimeout(() => this.expandToTolNode(tolNode), this.uiOpts.tileChgDuration);
return;
}
// Attempt expand-to-view on ancestor just below activeRoot
if (ancestor.name == this.activeRoot.tolNode.name){
console.log('Unable to complete search (not enough room to expand active root)');
- // Happens if screen is very small or node has very many children
- this.animationActive = false;
+ // Note: Only happens if screen is significantly small or node has significantly many children
+ this.modeRunning = false;
return;
}
while (true){
@@ -331,19 +279,17 @@ export default defineComponent({
ancestor = ancestor.parent!;
}
layoutNode = this.layoutMap.get(ancestor.name)!;
- this.onInnerHeaderClickHeld(layoutNode);
- setTimeout(() => this.expandToTolNode(tolNode), this.uiOpts.transitionDuration);
- },
- onOverlayClick(){
- this.animationActive = false;
+ this.onNonleafClickHeld(layoutNode);
+ setTimeout(() => this.expandToTolNode(tolNode), this.uiOpts.tileChgDuration);
},
+ // For auto-mode events
onPlayIconClick(){
- this.closeModesAndSettings();
- this.animationActive = true;
+ this.resetMode();
+ this.modeRunning = true;
this.autoAction();
},
autoAction(){
- if (!this.animationActive){
+ if (!this.modeRunning){
this.setLastFocused(null);
return;
}
@@ -356,14 +302,14 @@ export default defineComponent({
layoutNode = layoutNode.children[idx!];
}
this.setLastFocused(layoutNode);
- setTimeout(this.autoAction, this.autoWaitTime);
+ setTimeout(this.autoAction, this.uiOpts.autoWaitTime);
} else {
// Determine available actions
let action: Action | null;
let actionWeights: {[key: string]: number}; // Maps actions to choice weights
let node = this.lastFocused;
if (node.children.length == 0){
- actionWeights = {'move across': 1, 'move up': 2, 'expand': 4};
+ actionWeights = {'move across': 1, 'move up': 2, 'expand': 3};
// Zero weights for disallowed actions
if (node == this.activeRoot || node.parent!.children.length == 1){
actionWeights['move across'] = 0;
@@ -377,7 +323,7 @@ export default defineComponent({
} else {
actionWeights = {
'move across': 1, 'move down': 2, 'move up': 1,
- 'collapse': 1, 'expand to view': 0.5, 'expand ancestry bar': 0.5
+ 'collapse': 1, 'expand to view': 1, 'expand ancestry bar': 1
};
// Zero weights for disallowed actions
if (node == this.activeRoot || node.parent!.children.length == 1){
@@ -416,12 +362,12 @@ export default defineComponent({
// Perform action
this.autoPrevActionFail = false;
switch (action){
- case 'move across':
+ case 'move across': // Bias towards siblings with higher dCount
let siblings = node.parent!.children.filter(n => n != node);
let siblingWeights = siblings.map(n => n.dCount + 1);
this.setLastFocused(siblings[randWeightedChoice(siblingWeights)!]);
break;
- case 'move down':
+ case 'move down': // Bias towards children with higher dCount
let childWeights = node.children.map(n => n.dCount + 1);
this.setLastFocused(node.children[randWeightedChoice(childWeights)!]);
break;
@@ -429,22 +375,63 @@ export default defineComponent({
this.setLastFocused(node.parent!);
break;
case 'expand':
- this.autoPrevActionFail = !this.onInnerLeafClicked(node);
+ this.autoPrevActionFail = !this.onLeafClick(node);
break;
case 'collapse':
- this.autoPrevActionFail = !this.onInnerHeaderClicked(node);
+ this.autoPrevActionFail = !this.onNonleafClick(node);
break;
case 'expand to view':
- this.onInnerHeaderClickHeld(node);
+ this.onNonleafClickHeld(node);
break;
case 'expand ancestry bar':
- this.onDetachedAncestorClicked(node.parent!);
+ this.onDetachedAncestorClick(node.parent!);
break;
}
- setTimeout(this.autoAction, this.uiOpts.transitionDuration + this.autoWaitTime);
+ setTimeout(this.autoAction, this.uiOpts.tileChgDuration + this.uiOpts.autoWaitTime);
this.autoPrevAction = action;
}
},
+ // For settings events
+ onSettingsIconClick(){
+ this.resetMode();
+ this.settingsOpen = true;
+ },
+ onLayoutOptionChange(){
+ tryLayout(this.activeRoot, this.tileAreaPos, this.tileAreaDims, this.lytOpts,
+ {allowCollapse: true, layoutMap: this.layoutMap});
+ },
+ // For other events
+ onResize(){
+ if (!this.resizeThrottled){
+ this.width = document.documentElement.clientWidth;
+ this.height = document.documentElement.clientHeight;
+ tryLayout(this.activeRoot, this.tileAreaPos, this.tileAreaDims, this.lytOpts,
+ {allowCollapse: true, layoutMap: this.layoutMap});
+ // Prevent re-triggering until after a delay
+ this.resizeThrottled = true;
+ setTimeout(() => {this.resizeThrottled = false;}, this.resizeDelay);
+ }
+ },
+ onKeyUp(evt: KeyboardEvent){
+ if (evt.key == 'Escape'){
+ this.resetMode();
+ } else if (evt.key == 'F' && evt.ctrlKey){ // On ctrl-shift-f
+ if (!this.searchOpen){
+ this.onSearchIconClick();
+ } else {
+ (this.$refs.searchModal as InstanceType<typeof SearchModal>).focusInput();
+ }
+ }
+ },
+ // Helper methods
+ resetMode(){
+ this.infoModalNode = null;
+ this.searchOpen = false;
+ this.helpOpen = false;
+ this.settingsOpen = false;
+ this.modeRunning = false;
+ this.setLastFocused(null);
+ },
setLastFocused(node: LayoutNode | null){
if (this.lastFocused != null){
this.lastFocused.hasFocus = false;
@@ -454,65 +441,64 @@ export default defineComponent({
node.hasFocus = true;
}
},
- onHelpIconClick(){
- this.closeModesAndSettings();
- this.helpOpen = true;
- },
- onHelpModalClose(){
- this.helpOpen = false;
- },
},
created(){
window.addEventListener('resize', this.onResize);
window.addEventListener('keyup', this.onKeyUp);
- tryLayout(this.activeRoot, this.layoutMap, this.tileAreaPos, this.tileAreaDims, this.lytOpts, true);
+ tryLayout(this.activeRoot, this.tileAreaPos, this.tileAreaDims, this.lytOpts,
+ {allowCollapse: true, layoutMap: this.layoutMap});
},
unmounted(){
window.removeEventListener('resize', this.onResize);
window.removeEventListener('keyup', this.onKeyUp);
},
components: {
- Tile, AncestryBar, TileInfoModal, SettingsPane, SearchModal, HelpModal,
- SearchIcon, PlayIcon, HelpIcon, SettingsIcon,
+ Tile, AncestryBar,
+ HelpIcon, SearchIcon, PlayIcon, SettingsIcon,
+ TileInfoModal, HelpModal, SearchModal, SettingsPane,
},
});
</script>
<template>
-<div :style="styles">
+<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" :lytOpts="lytOpts" :uiOpts="uiOpts"
- @leaf-clicked="onInnerLeafClicked" @header-clicked="onInnerHeaderClicked"
- @leaf-click-held="onInnerLeafClickHeld" @header-click-held="onInnerHeaderClickHeld"
- @info-icon-clicked="onInnerInfoIconClicked"/>
+ @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="[0,0]" :dims="ancestryBarDims" :nodes="detachedAncestors"
:lytOpts="lytOpts" :uiOpts="uiOpts"
- @detached-ancestor-clicked="onDetachedAncestorClicked" @info-icon-clicked="onInnerInfoIconClicked"/>
+ @detached-ancestor-click="onDetachedAncestorClick" @info-icon-click="onInfoIconClick"/>
<!-- Icons -->
<help-icon @click="onHelpIconClick"
- class="absolute bottom-[6px] left-[6px] w-[18px] h-[18px] text-white/40 hover:text-white hover:cursor-pointer"/>
+ class="absolute bottom-[6px] left-[6px] w-[18px] h-[18px]
+ text-white/40 hover:text-white hover:cursor-pointer"/>
<search-icon @click="onSearchIconClick"
- class="absolute bottom-[6px] left-[30px] w-[18px] h-[18px] text-white/40 hover:text-white hover:cursor-pointer"/>
+ class="absolute bottom-[6px] left-[30px] w-[18px] h-[18px]
+ text-white/40 hover:text-white hover:cursor-pointer"/>
<play-icon @click="onPlayIconClick"
- class="absolute bottom-[6px] left-[54px] w-[18px] h-[18px] text-white/40 hover:text-white hover:cursor-pointer"/>
+ class="absolute bottom-[6px] left-[54px] w-[18px] h-[18px]
+ text-white/40 hover:text-white hover:cursor-pointer"/>
<!-- Modals -->
<transition name="fade">
<tile-info-modal v-if="infoModalNode != null" :tolNode="infoModalNode" :uiOpts="uiOpts"
- @info-modal-close="onInfoModalClose"/>
+ @info-modal-close="infoModalNode = null"/>
</transition>
<transition name="fade">
- <search-modal v-if="searchOpen" :layoutTree="layoutTree" :tolMap="tolMap" :uiOpts="uiOpts"
- @search-close="onSearchClose" @search-node="onSearchNode" ref="searchModal"/>
+ <search-modal v-if="searchOpen" :tolMap="tolMap" :uiOpts="uiOpts"
+ @search-close="searchOpen = false" @search-node="onSearchNode" ref="searchModal"/>
</transition>
<transition name="fade">
- <help-modal v-if="helpOpen" :uiOpts="uiOpts" @help-modal-close="onHelpModalClose"/>
+ <help-modal v-if="helpOpen" :uiOpts="uiOpts" @help-modal-close="helpOpen = false"/>
</transition>
<!-- Settings -->
<transition name="slide-bottom-right">
<settings-pane v-if="settingsOpen" :lytOpts="lytOpts" :uiOpts="uiOpts"
- @settings-close="onSettingsClose" @layout-option-change="onLayoutOptionChange"/>
- <!-- outer div prevents transition interference with inner rotate -->
+ @settings-close="settingsOpen = false" @layout-option-change="onLayoutOptionChange"/>
<div v-else class="absolute bottom-0 right-0 w-[100px] h-[100px] invisible">
+ <!-- Note: Above enclosing div prevents transition interference with inner rotate -->
<div class="absolute bottom-[-50px] right-[-50px] w-[100px] h-[100px] visible -rotate-45
bg-black text-white hover:cursor-pointer" @click="onSettingsIconClick">
<settings-icon class="w-6 h-6 mx-auto mt-2"/>
@@ -520,46 +506,12 @@ export default defineComponent({
</div>
</transition>
<!-- Overlay used to prevent interaction and capture clicks -->
- <div :style="{visibility: animationActive ? 'visible' : 'hidden'}"
- class="absolute left-0 top-0 w-full h-full" @click="onOverlayClick"></div>
+ <div :style="{visibility: modeRunning ? 'visible' : 'hidden'}"
+ class="absolute left-0 top-0 w-full h-full" @click="modeRunning = false"></div>
</div>
</template>
<style>
-.animate-expand-shrink {
- animation-name: expand-shrink;
- animation-duration: 300ms;
- animation-iteration-count: 1;
- animation-timing-function: ease-in-out;
-}
-@keyframes expand-shrink {
- from {
- transform: scale(1, 1);
- }
- 50% {
- transform: scale(1.1, 1.1);
- }
- to {
- transform: scale(1, 1);
- }
-}
-.animate-shrink-expand {
- animation-name: shrink-expand;
- animation-duration: 300ms;
- animation-iteration-count: 1;
- animation-timing-function: ease-in-out;
-}
-@keyframes shrink-expand {
- from {
- transform: translate3d(0,0,0) scale(1, 1);
- }
- 50% {
- transform: translate3d(0,0,0) scale(0.9, 0.9);
- }
- to {
- transform: translate3d(0,0,0) scale(1, 1);
- }
-}
.fade-enter-from, .fade-leave-to {
opacity: 0;
}