diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/App.vue | 114 | ||||
| -rw-r--r-- | src/components/AncestryBar.vue | 4 | ||||
| -rw-r--r-- | src/components/SettingsModal.vue | 2 | ||||
| -rw-r--r-- | src/components/Tile.vue | 28 | ||||
| -rw-r--r-- | src/components/TutorialPane.vue | 4 | ||||
| -rw-r--r-- | src/lib.ts | 25 |
6 files changed, 87 insertions, 90 deletions
diff --git a/src/App.vue b/src/App.vue index db7292f..92607ad 100644 --- a/src/App.vue +++ b/src/App.vue @@ -3,12 +3,12 @@ import {defineComponent, PropType} from 'vue'; // Components import Tile from './components/Tile.vue'; -import AncestryBar from './components/AncestryBar.vue'; -import TutorialPane from './components/TutorialPane.vue'; import TileInfoModal from './components/TileInfoModal.vue'; import SearchModal from './components/SearchModal.vue'; import SettingsModal from './components/SettingsModal.vue'; import HelpModal from './components/HelpModal.vue'; +import AncestryBar from './components/AncestryBar.vue'; +import TutorialPane from './components/TutorialPane.vue'; // Icons import SearchIcon from './components/icon/SearchIcon.vue'; import PlayIcon from './components/icon/PlayIcon.vue'; @@ -16,7 +16,7 @@ import SettingsIcon from './components/icon/SettingsIcon.vue'; import HelpIcon from './components/icon/HelpIcon.vue'; // Other // Note: Import paths lack a .ts or .js extension because .ts makes vue-tsc complain, and .js makes vite complain -import {TolNode, TolMap, UiOptions, Action} from './lib'; +import {TolNode, TolMap, Action, UiOptions} from './lib'; import {LayoutNode, LayoutOptions, LayoutTreeChg} from './layout'; import {initLayoutTree, initLayoutMap, tryLayout} from './layout'; import {arraySum, randWeightedChoice, getScrollBarWidth, getBreakpoint} from './util'; @@ -37,62 +37,56 @@ function getReverseAction(action: AutoAction): AutoAction | null { return null; } } - -// Initialise tree-of-life data -const initialTolMap: TolMap = new Map(); -initialTolMap.set("", new TolNode()); - -// Configurable options +// Functions providing default option values function getDefaultLytOpts(): LayoutOptions { let screenSz = getBreakpoint(); return { tileSpacing: screenSz == 'sm' ? 6 : 10, //px - headerSz: 22, //px - minTileSz: 50, //px - maxTileSz: 200, //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' - rectSensitivity: 0.9, //Between 0 and 1 - sweepMode: 'left', //'left' | 'top' | 'shorter' | 'auto' - sweptNodesPrio: 'sqrt', //'linear' | 'sqrt' | 'pow-2/3' - sweepToParent: 'prefer', //'none' | 'prefer' | 'fallback' + layoutType: 'sweep', // 'sqr' | 'rect' | 'sweep' + rectMode: 'auto first-row', // 'horz' | 'vert' | 'linear' | 'auto' | 'auto first-row' + rectSensitivity: 0.9, // Between 0 and 1 + sweepMode: 'left', // 'left' | 'top' | 'shorter' | 'auto' + sweptNodesPrio: 'sqrt', // 'linear' | 'sqrt' | 'pow-2/3' + sweepToParent: 'prefer', // 'none' | 'prefer' | 'fallback' }; } function getDefaultUiOpts(){ let screenSz = getBreakpoint(); return { // Shared styling - borderRadius: 5, //px + borderRadius: 5, // px shadowNormal: '0 0 2px black', - shadowHighlight: '0 0 1px 2px greenyellow', + shadowHovered: '0 0 1px 2px greenyellow', shadowFocused: '0 0 1px 2px orange', // Styling for App appBgColor: '#292524', - tileAreaOffset: screenSz == 'sm' ? 6 : 10, //px (space between root tile and display boundary) + titleBarBgColor: 'black', + mainTileMargin: screenSz == 'sm' ? 6 : 10, // px // Styling for tiles - headerColor: '#fafaf9', - childThresholds: [[1, 'greenyellow'], [10, 'orange'], [100, 'red']], - infoIconSz: 18, //px - infoIconMargin: 2, //px - leafTilePadding: 4, //px - leafHeaderFontSz: 15, //px - nonleafBgColors: ['#44403c', '#57534e'], //tiles at depth N use the Nth color, repeating from the start as needed - nonleafHeaderFontSz: 15, //px - nonleafHeaderColor: '#fafaf9', + textColor: '#fafaf9', + childQtyColors: [[1, 'greenyellow'], [10, 'orange'], [100, 'red']], + infoIconSz: 18, // px + infoIconMargin: 2, // px + leafPadding: 4, // px + leafHeaderFontSz: 15, // px + nonleafBgColors: ['#44403c', '#57534e'], + nonleafHeaderFontSz: 15, // px nonleafHeaderBgColor: '#1c1917', // Styling for other components - infoModalImgSz: 200, + infoModalImgSz: 200, // px ancestryBarBgColor: '#44403c', - ancestryBarImgSz: 100, //px - ancestryTileMargin: 5, //px (gap between detached-ancestor tiles) - tutorialPaneSz: 200, - tutorialPaneBgColor: '#1c1917', - tutorialPaneTextColor: 'white', + ancestryBarImgSz: 100, // px + ancestryTileGap: 5, // px + tutPaneSz: 200, // px + tutPaneBgColor: '#1c1917', // Timing related - clickHoldDuration: 400, //ms (duration after mousedown when a click-and-hold is recognised) - tileChgDuration: 300, //ms (for tile move/expand/collapse) - autoWaitTime: 500, //ms (time to wait between actions (with their transitions)) + clickHoldDuration: 400, // ms + transitionDuration: 300, // ms + autoActionDelay: 500, // ms // Other useReducedTree: false, searchSuggLimit: 5, @@ -103,6 +97,10 @@ function getDefaultUiOpts(){ }; } +// Initialise tree-of-life data +const initialTolMap: TolMap = new Map(); +initialTolMap.set("", new TolNode()); + export default defineComponent({ data(){ let layoutTree = initLayoutTree(initialTolMap, "", 0); @@ -162,22 +160,22 @@ export default defineComponent({ }, tutPaneContainerStyles(): Record<string,string> { return { - minHeight: (this.tutorialOpen ? this.uiOpts.tutorialPaneSz : 0) + 'px', - maxHeight: (this.tutorialOpen ? this.uiOpts.tutorialPaneSz : 0) + 'px', - transitionDuration: this.uiOpts.tileChgDuration + 'ms', + minHeight: (this.tutorialOpen ? this.uiOpts.tutPaneSz : 0) + 'px', + maxHeight: (this.tutorialOpen ? this.uiOpts.tutPaneSz : 0) + 'px', + transitionDuration: this.uiOpts.transitionDuration + 'ms', transitionProperty: 'max-height, min-height', overflow: 'hidden', }; }, ancestryBarContainerStyles(): Record<string,string> { let ancestryBarBreadth = this.detachedAncestors == null ? 0 : - this.uiOpts.ancestryBarImgSz + this.uiOpts.ancestryTileMargin*2 + this.uiOpts.scrollGap; + this.uiOpts.ancestryBarImgSz + this.uiOpts.ancestryTileGap*2 + this.uiOpts.scrollGap; let styles = { minWidth: 'auto', maxWidth: 'none', minHeight: 'auto', maxHeight: 'none', - transitionDuration: this.uiOpts.tileChgDuration + 'ms', + transitionDuration: this.uiOpts.transitionDuration + 'ms', transitionProperty: '', overflow: 'hidden' }; @@ -295,7 +293,7 @@ export default defineComponent({ let doExpansion = () => { LayoutNode.hideUpward(layoutNode, this.layoutMap); this.activeRoot = layoutNode; - if (this.detachedAncestors.length == 0){ + if (this.detachedAncestors == null){ // Repeatedly relayout tiles during ancestry-bar transition this.ancestryBarInTransition = true; this.relayoutDuringAncestryBarTransition(); @@ -352,7 +350,7 @@ export default defineComponent({ LayoutNode.hideUpward(layoutNode, this.layoutMap); this.activeRoot = layoutNode; // Repeatedly relayout tiles during ancestry-bar transition - if (this.detachedAncestors.length == 0){ + if (this.detachedAncestors == null){ this.ancestryBarInTransition = true; this.relayoutDuringAncestryBarTransition(); } @@ -437,7 +435,7 @@ export default defineComponent({ } if (!this.uiOpts.jumpToSearchedNode){ this.onDetachedAncestorClick(nodeInAncestryBar!); - setTimeout(() => this.expandToNode(name), this.uiOpts.tileChgDuration); + setTimeout(() => this.expandToNode(name), this.uiOpts.transitionDuration); } else{ this.onDetachedAncestorClick(nodeInAncestryBar, true) .then(() => this.expandToNode(name)); @@ -459,18 +457,18 @@ export default defineComponent({ targetNode = this.layoutMap.get(name); this.onLeafClickHeld(targetNode!.parent!); // - setTimeout(() => {this.setLastFocused(targetNode!);}, this.uiOpts.tileChgDuration); + setTimeout(() => {this.setLastFocused(targetNode!);}, this.uiOpts.transitionDuration); this.modeRunning = false; return; } if (this.overflownRoot){ this.onLeafClickHeld(layoutNode); - setTimeout(() => this.expandToNode(name), this.uiOpts.tileChgDuration); + setTimeout(() => this.expandToNode(name), this.uiOpts.transitionDuration); return; } this.onLeafClick(layoutNode).then(success => { if (success){ - setTimeout(() => this.expandToNode(name), this.uiOpts.tileChgDuration); + setTimeout(() => this.expandToNode(name), this.uiOpts.transitionDuration); return; } // Attempt expand-to-view on an ancestor halfway to the active root @@ -486,7 +484,7 @@ export default defineComponent({ } layoutNode = ancestorChain[Math.floor((ancestorChain.length - 1) / 2)] this.onNonleafClickHeld(layoutNode); - setTimeout(() => this.expandToNode(name), this.uiOpts.tileChgDuration); + setTimeout(() => this.expandToNode(name), this.uiOpts.transitionDuration); }); }, // For auto-mode events @@ -510,7 +508,7 @@ export default defineComponent({ layoutNode = layoutNode.children[idx!]; } this.setLastFocused(layoutNode); - setTimeout(this.autoAction, this.uiOpts.autoWaitTime); + setTimeout(this.autoAction, this.uiOpts.autoActionDelay); } else { // Determine available actions let action: AutoAction | null; @@ -597,7 +595,7 @@ export default defineComponent({ this.onDetachedAncestorClick(node.parent!); break; } - setTimeout(this.autoAction, this.uiOpts.tileChgDuration + this.uiOpts.autoWaitTime); + setTimeout(this.autoAction, this.uiOpts.transitionDuration + this.uiOpts.autoActionDelay); this.autoPrevAction = action; } }, @@ -881,7 +879,7 @@ export default defineComponent({ clearTimeout(timerId); console.log('Reached timeout waiting for ancestry-bar transition-end event'); } - }, this.uiOpts.tileChgDuration * 3); + }, this.uiOpts.transitionDuration * 3); }, tutPaneTransitionEnd(){ this.tutPaneInTransition = false; @@ -899,7 +897,7 @@ export default defineComponent({ clearTimeout(timerId); console.log('Reached timeout waiting for tutorial-pane transition-end event'); } - }, this.uiOpts.tileChgDuration * 3); + }, this.uiOpts.transitionDuration * 3); }, }, created(){ @@ -922,7 +920,7 @@ export default defineComponent({ <template> <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 shadow"> + <div class="flex shadow" :style="{backgroundColor: uiOpts.titleBarBgColor}"> <h1 class="text-lime-500 px-4 my-auto text-2xl">Tree of Life</h1> <!-- Icons --> <div v-if="!uiOpts.disabledActions.has('search')" @@ -948,7 +946,7 @@ export default defineComponent({ </div> <div :style="tutPaneContainerStyles"> <!-- Used to slide-in/out the tutorial pane --> <transition name="fade" @after-enter="tutPaneTransitionEnd" @after-leave="tutPaneTransitionEnd"> - <tutorial-pane v-if="tutorialOpen" :height="uiOpts.tutorialPaneSz + 'px'" + <tutorial-pane v-if="tutorialOpen" :height="uiOpts.tutPaneSz + 'px'" :uiOpts="uiOpts" :triggerFlag="tutTriggerFlag" :skipWelcome="!welcomeOpen" @close="onTutorialClose" @skip="onTutorialSkip" @stage-chg="onTutStageChg"/> </transition> @@ -962,7 +960,7 @@ export default defineComponent({ @ancestor-click="onDetachedAncestorClick" @info-click="onInfoClick"/> </transition> </div> - <div class="relative grow" :style="{margin: uiOpts.tileAreaOffset + 'px'}" ref="tileArea"> + <div class="relative grow" :style="{margin: uiOpts.mainTileMargin + 'px'}" ref="tileArea"> <tile :layoutNode="layoutTree" :tolMap="tolMap" :lytOpts="lytOpts" :uiOpts="uiOpts" :overflownDim="overflownRoot ? tileAreaDims[1] : 0" :skipTransition="justInitialised" @leaf-click="onLeafClick" @nonleaf-click="onNonleafClick" diff --git a/src/components/AncestryBar.vue b/src/components/AncestryBar.vue index c973195..a5bb1c0 100644 --- a/src/components/AncestryBar.vue +++ b/src/components/AncestryBar.vue @@ -29,8 +29,8 @@ export default defineComponent({ // For child layout display: 'flex', flexDirection: this.vert ? 'column' : 'row', - gap: this.uiOpts.ancestryTileMargin + 'px', - padding: this.uiOpts.ancestryTileMargin + 'px', + gap: this.uiOpts.ancestryTileGap + 'px', + padding: this.uiOpts.ancestryTileGap + 'px', // Other backgroundColor: this.uiOpts.ancestryBarBgColor, boxShadow: this.uiOpts.shadowNormal, diff --git a/src/components/SettingsModal.vue b/src/components/SettingsModal.vue index 5be06b7..92f4dce 100644 --- a/src/components/SettingsModal.vue +++ b/src/components/SettingsModal.vue @@ -146,7 +146,7 @@ export default defineComponent({ <hr class="border-stone-400"/> <div> <label>Animation Duration <input type="range" min="0" max="3000" class="mx-2 w-[3cm]" - v-model.number="uiOpts.tileChgDuration" @change="onUiOptChg('tileChgDuration')"/></label> + v-model.number="uiOpts.transitionDuration" @change="onUiOptChg('transitionDuration')"/></label> </div> <hr class="border-stone-400"/> <div> diff --git a/src/components/Tile.vue b/src/components/Tile.vue index e03f13e..5e4bccf 100644 --- a/src/components/Tile.vue +++ b/src/components/Tile.vue @@ -95,7 +95,7 @@ export default defineComponent({ }, boxShadow(): string { if (this.highlight){ - return this.uiOpts.shadowHighlight; + return this.uiOpts.shadowHovered; } else if (this.layoutNode.hasFocus && !this.inTransition){ return this.uiOpts.shadowFocused; } else { @@ -113,7 +113,7 @@ export default defineComponent({ boxShadow: this.boxShadow, borderRadius: this.uiOpts.borderRadius + 'px', // Transition related - transitionDuration: (this.skipTransition ? 0 : this.uiOpts.tileChgDuration) + 'ms', + transitionDuration: (this.skipTransition ? 0 : this.uiOpts.transitionDuration) + 'ms', transitionProperty: 'left, top, width, height, visibility', transitionTimingFunction: 'ease-out', zIndex: this.inTransition && this.wasClicked ? '1' : '0', @@ -174,20 +174,20 @@ export default defineComponent({ }, leafHeaderStyles(): Record<string,string> { let numChildren = this.tolNode.children.length; - let headerColor = this.uiOpts.headerColor; - for (let [threshold, color] of this.uiOpts.childThresholds){ + let textColor = this.uiOpts.textColor; + for (let [threshold, color] of this.uiOpts.childQtyColors){ if (numChildren >= threshold){ - headerColor = color; + textColor = color; } else { break; } } return { - height: (this.uiOpts.leafHeaderFontSz + this.uiOpts.leafTilePadding * 2) + 'px', - padding: this.uiOpts.leafTilePadding + 'px', + height: (this.uiOpts.leafHeaderFontSz + this.uiOpts.leafPadding * 2) + 'px', + padding: this.uiOpts.leafPadding + 'px', lineHeight: this.uiOpts.leafHeaderFontSz + 'px', fontSize: this.uiOpts.leafHeaderFontSz + 'px', - color: headerColor, + color: textColor, // For ellipsis overflow: 'hidden', textOverflow: 'ellipsis', @@ -240,7 +240,7 @@ export default defineComponent({ lineHeight: this.lytOpts.headerSz + 'px', fontSize: this.uiOpts.nonleafHeaderFontSz + 'px', textAlign: 'center', - color: this.uiOpts.nonleafHeaderColor, + color: this.uiOpts.textColor, paddingLeft: '5px', // For ellipsis overflow: 'hidden', @@ -266,7 +266,7 @@ export default defineComponent({ position: 'absolute', backgroundColor: this.nonleafBgColor, boxShadow: this.boxShadow, - transitionDuration: this.uiOpts.tileChgDuration + 'ms', + transitionDuration: this.uiOpts.transitionDuration + 'ms', transitionProperty: 'left, top, width, height, visibility', transitionTimingFunction: 'ease-out', }; @@ -326,7 +326,7 @@ export default defineComponent({ pos: { handler(newVal, oldVal){ let valChanged = newVal[0] != oldVal[0] || newVal[1] != oldVal[1]; - if (valChanged && this.uiOpts.tileChgDuration > 100 && !this.inTransition){ + if (valChanged && this.uiOpts.transitionDuration > 100 && !this.inTransition){ this.inTransition = true; } }, @@ -335,7 +335,7 @@ export default defineComponent({ dims: { handler(newVal, oldVal){ let valChanged = newVal[0] != oldVal[0] || newVal[1] != oldVal[1]; - if (valChanged && this.uiOpts.tileChgDuration > 100 && !this.inTransition){ + if (valChanged && this.uiOpts.transitionDuration > 100 && !this.inTransition){ this.inTransition = true; } }, @@ -358,13 +358,13 @@ export default defineComponent({ hidden(newVal, oldVal){ if (oldVal && !newVal){ this.justUnhidden = true; - setTimeout(() => {this.justUnhidden = false;}, this.uiOpts.tileChgDuration); + setTimeout(() => {this.justUnhidden = false;}, this.uiOpts.transitionDuration); } }, hasFocus(newVal, oldVal){ if (newVal != oldVal && newVal){ this.inFlash = true; - setTimeout(() => {this.inFlash = false;}, this.uiOpts.tileChgDuration); + setTimeout(() => {this.inFlash = false;}, this.uiOpts.transitionDuration); } }, }, diff --git a/src/components/TutorialPane.vue b/src/components/TutorialPane.vue index 6fa7bac..e32fedd 100644 --- a/src/components/TutorialPane.vue +++ b/src/components/TutorialPane.vue @@ -20,8 +20,8 @@ export default defineComponent({ computed: { styles(): Record<string,string> { return { - backgroundColor: this.uiOpts.tutorialPaneBgColor, - color: this.uiOpts.tutorialPaneTextColor, + backgroundColor: this.uiOpts.tutPaneBgColor, + color: this.uiOpts.textColor, height: this.height, }; }, @@ -48,38 +48,37 @@ export type UiOptions = { // Shared styling borderRadius: number, // CSS border-radius value, in px shadowNormal: string, // CSS box-shadow value - shadowHighlight: string, + shadowHovered: string, shadowFocused: string, // Styling for App appBgColor: string, // CSS color - tileAreaOffset: number, // Space between root tile and display boundary, in px + titleBarBgColor: string, // CSS color + mainTileMargin: number, // px // Styling for tiles - headerColor: string, // CSS color - childThresholds: [number, string][], - // Specifies, for an increasing sequence of minimum-child-quantity values, CSS color to use + textColor: string, // CSS color + childQtyColors: [number, string][], + // Specifies, for an increasing sequence of minimum-child-quantity values, CSS colors to use //eg: [[1, 'green'], [10, 'orange'], [100, 'red']] infoIconSz: number, // px infoIconMargin: number, // px - leafTilePadding: number, // px + leafPadding: number, // px leafHeaderFontSz: number, // px nonleafBgColors: string[], // Specifies CSS colors to use at various tree depths // With N strings, tiles at depth M use the color at index M % N nonleafHeaderFontSz: number, // px - nonleafHeaderColor: string, // CSS color nonleafHeaderBgColor: string, // CSS color // Styling for other components infoModalImgSz: number, // px ancestryBarBgColor: string, // CSS color ancestryBarImgSz: number, // px - ancestryTileMargin: number, // px (gap between detached-ancestor tiles) - tutorialPaneSz: number, // px - tutorialPaneBgColor: string, // CSS color - tutorialPaneTextColor: string, // CSS color + ancestryTileGap: number, // Gap between ancestor tiles, in px + tutPaneSz: number, // px + tutPaneBgColor: string, // CSS color // Timing related clickHoldDuration: number, // Time after mousedown when a click-and-hold is recognised, in ms - tileChgDuration: number, // Transition time for tile_move/etc, in ms - autoWaitTime: number, // Time between actions, in ms + transitionDuration: number, // ms + autoActionDelay: number, // Time between auto-mode actions, in ms // Other useReducedTree: boolean, searchSuggLimit: number, // Max number of search suggestions |
