diff options
| -rw-r--r-- | src/App.vue | 93 | ||||
| -rw-r--r-- | src/components/AncestryBar.vue | 9 | ||||
| -rw-r--r-- | src/components/IconButton.vue | 6 | ||||
| -rw-r--r-- | src/components/SearchModal.vue | 10 | ||||
| -rw-r--r-- | src/components/SettingsModal.vue | 2 | ||||
| -rw-r--r-- | src/components/Tile.vue | 26 | ||||
| -rw-r--r-- | src/components/TileInfoModal.vue | 6 | ||||
| -rw-r--r-- | src/lib.ts | 26 |
8 files changed, 87 insertions, 91 deletions
diff --git a/src/App.vue b/src/App.vue index 063f5ab..9abd5ac 100644 --- a/src/App.vue +++ b/src/App.vue @@ -55,39 +55,39 @@ function getDefaultLytOpts(): LayoutOptions { sweepToParent: 'prefer', // 'none' | 'prefer' | 'fallback' }; } -function getDefaultUiOpts(){ +function getDefaultUiOpts(lytOpts: LayoutOptions){ let screenSz = getBreakpoint(); - let bgColorLight = '#44403c', altColor = '#a3e623'; + // Reused option values + let textColor = '#fafaf9'; + let bgColor = '#292524', bgColorLight = '#44403c', bgColorDark = '#1c1917', + bgColorLight2 = '#57534e', bgColorDark2 = '#0e0c0b'; + let altColor = '#a3e623', altColorDark = '#65a30d'; + let accentColor = '#f59e0b'; + let scrollGap = getScrollBarWidth(); + // return { - // Shared styling - textColor: '#fafaf9', - bgColor: '#292524', + // Shared coloring/sizing + textColor, + bgColor, bgColorLight, - bgColorDark: '#1c1917', - bgColorLight2: '#57534e', - bgColorDark2: '#0e0c0b', + bgColorDark, + bgColorLight2, + bgColorDark2, altColor, - altColorDark: '#65a30d', + altColorDark, borderRadius: 5, // px shadowNormal: '0 0 2px black', - shadowHovered: '0 0 1px 2px' + altColor, - shadowFocused: '0 0 1px 2px orange', - // Styling for App - mainTileMargin: screenSz == 'sm' ? 6 : 10, // px - // Styling for tiles - childQtyColors: [[1, altColor], [10, 'orange'], [100, 'red']], - infoIconSz: 18, // px - infoIconMargin: 2, // px - leafPadding: 4, // px - leafHeaderFontSz: 15, // px - nonleafBgColors: ['#44403c', '#57534e'], - nonleafHeaderFontSz: 15, // px - // Styling for other components - infoModalImgSz: 200, // px + shadowHovered: '0 0 1px 2px ' + altColor, + shadowFocused: '0 0 1px 2px ' + accentColor, + // Component coloring + childQtyColors: [[1, 'greenyellow'], [10, 'orange'], [100, 'red']], + nonleafBgColors: [bgColorLight, bgColorLight2], + nonleafHeaderColor: bgColorDark, ancestryBarBgColor: bgColorLight, - ancestryBarImgSz: 100, // px - ancestryTileGap: 5, // px + // Component sizing + ancestryBarBreadth: lytOpts.maxTileSz / 2 + lytOpts.tileSpacing*2 + scrollGap, // px tutPaneSz: 200, // px + scrollGap, // Timing related clickHoldDuration: 400, // ms transitionDuration: 300, // ms @@ -95,10 +95,9 @@ function getDefaultUiOpts(){ // Other useReducedTree: false, searchSuggLimit: 5, - jumpToSearchedNode: false, + searchJumpMode: false, tutorialSkip: false, disabledActions: new Set() as Set<Action>, - scrollGap: getScrollBarWidth(), }; } @@ -163,7 +162,7 @@ export default defineComponent({ } return ancestors.reverse(); }, - iconButtonStyles(): Record<string,string> { + buttonStyles(): Record<string,string> { return { color: this.uiOpts.textColor, backgroundColor: this.uiOpts.altColorDark, @@ -179,8 +178,7 @@ export default defineComponent({ }; }, ancestryBarContainerStyles(): Record<string,string> { - let ancestryBarBreadth = this.detachedAncestors == null ? 0 : - this.uiOpts.ancestryBarImgSz + this.uiOpts.ancestryTileGap*2 + this.uiOpts.scrollGap; + let ancestryBarBreadth = this.detachedAncestors == null ? 0 : this.uiOpts.ancestryBarBreadth; let styles = { minWidth: 'auto', maxWidth: 'none', @@ -441,7 +439,7 @@ export default defineComponent({ while (!this.detachedAncestors!.includes(nodeInAncestryBar)){ nodeInAncestryBar = nodeInAncestryBar.parent!; } - if (!this.uiOpts.jumpToSearchedNode){ + if (!this.uiOpts.searchJumpMode){ this.onDetachedAncestorClick(nodeInAncestryBar!); setTimeout(() => this.expandToNode(name), this.uiOpts.transitionDuration); } else{ @@ -451,7 +449,7 @@ export default defineComponent({ return; } // Attempt tile-expand - if (this.uiOpts.jumpToSearchedNode){ + if (this.uiOpts.searchJumpMode){ // Extend layout tree let tolNode = this.tolMap.get(name)!; let nodesToAdd = [name] as string[]; @@ -639,7 +637,7 @@ export default defineComponent({ onResetSettings(){ localStorage.clear(); let defaultLytOpts = getDefaultLytOpts(); - let defaultUiOpts = getDefaultUiOpts(); + let defaultUiOpts = getDefaultUiOpts(defaultLytOpts); if (this.uiOpts.useReducedTree != defaultUiOpts.useReducedTree){ this.onTreeChange(); } @@ -699,7 +697,7 @@ export default defineComponent({ let handleResize = () => { // Update unmodified layout/ui options with defaults let lytOpts = getDefaultLytOpts(); - let uiOpts = getDefaultUiOpts(); + let uiOpts = getDefaultUiOpts(lytOpts); let changedTree = false; for (let prop of Object.getOwnPropertyNames(lytOpts)){ let item = localStorage.getItem('lyt ' + prop); @@ -710,6 +708,7 @@ export default defineComponent({ for (let prop of Object.getOwnPropertyNames(uiOpts)){ let item = localStorage.getItem('lyt ' + prop); if (item == null && this.uiOpts[prop] != uiOpts[prop as keyof typeof uiOpts]){ + console.log("Loaded UI prop " + prop) this.uiOpts[prop] = uiOpts[prop as keyof typeof uiOpts]; if (prop == 'useReducedTree'){ changedTree = true; @@ -757,8 +756,8 @@ export default defineComponent({ } else if (evt.key == 'F' && evt.ctrlKey){ // If search bar is open, swap search mode if (this.searchOpen){ - this.uiOpts.jumpToSearchedNode = !this.uiOpts.jumpToSearchedNode; - this.onSettingsChg([], ['jumpToSearchedNode']); + this.uiOpts.searchJumpMode = !this.uiOpts.searchJumpMode; + this.onSettingsChg([], ['searchJumpMode']); } } }, @@ -813,7 +812,8 @@ export default defineComponent({ return opts; }, getUiOpts(){ - let opts: {[x: string]: boolean|number|string|string[]|(string|number)[][]|Set<Action>} = getDefaultUiOpts(); + let opts: {[x: string]: boolean|number|string|string[]|(string|number)[][]|Set<Action>} = + getDefaultUiOpts(getDefaultLytOpts()); for (let prop of Object.getOwnPropertyNames(opts)){ let item = localStorage.getItem('ui ' + prop); if (item != null){ @@ -928,23 +928,20 @@ export default defineComponent({ <template> <div class="absolute left-0 top-0 w-screen h-screen overflow-hidden flex flex-col" :style="{backgroundColor: uiOpts.bgColor}"> - <div class="flex shadow" :style="{backgroundColor: uiOpts.bgColorDark2}"> - <h1 class="px-4 my-auto text-2xl" :style="{color: uiOpts.altColor}">Tree of Life</h1> + <div class="flex shadow gap-2 p-2" :style="{backgroundColor: uiOpts.bgColorDark2}"> + <h1 class="my-auto text-2xl" :style="{color: uiOpts.altColor}">Tol Explorer</h1> <!-- Icons --> - <icon-button v-if="!uiOpts.disabledActions.has('search')" - class="ml-auto mr-2 my-2" :style="iconButtonStyles" @click="onSearchIconClick"> + <div class="mx-auto"/> <!-- Spacer --> + <icon-button v-if="!uiOpts.disabledActions.has('search')" :style="buttonStyles" @click="onSearchIconClick"> <search-icon/> </icon-button> - <icon-button v-if="!uiOpts.disabledActions.has('autoMode')" - class="mr-2 my-2" :style="iconButtonStyles" @click="onPlayIconClick"> + <icon-button v-if="!uiOpts.disabledActions.has('autoMode')" :style="buttonStyles" @click="onPlayIconClick"> <play-icon/> </icon-button> - <icon-button v-if="!uiOpts.disabledActions.has('settings')" - class="mr-2 my-2" :style="iconButtonStyles" @click="onSettingsIconClick"> + <icon-button v-if="!uiOpts.disabledActions.has('settings')" :style="buttonStyles" @click="onSettingsIconClick"> <settings-icon/> </icon-button> - <icon-button v-if="!uiOpts.disabledActions.has('help')" - class="mr-2 my-2" :style="iconButtonStyles" @click="onHelpIconClick"> + <icon-button v-if="!uiOpts.disabledActions.has('help')" :style="buttonStyles" @click="onHelpIconClick"> <help-icon/> </icon-button> </div> @@ -964,7 +961,7 @@ export default defineComponent({ @ancestor-click="onDetachedAncestorClick" @info-click="onInfoClick"/> </transition> </div> - <div class="relative grow" :style="{margin: uiOpts.mainTileMargin + 'px'}" ref="tileArea"> + <div class="relative grow" :style="{margin: lytOpts.tileSpacing + '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 a5bb1c0..0f95d00 100644 --- a/src/components/AncestryBar.vue +++ b/src/components/AncestryBar.vue @@ -15,10 +15,13 @@ export default defineComponent({ uiOpts: {type: Object as PropType<UiOptions>, required: true}, }, computed: { + imgSz(){ + return this.uiOpts.ancestryBarBreadth - this.lytOpts.tileSpacing*2 - this.uiOpts.scrollGap; + }, usedNodes(){ // Childless versions of 'nodes' used to parameterise <tile> return this.nodes.map(n => { let newNode = new LayoutNode(n.name, []); - newNode.dims = [this.uiOpts.ancestryBarImgSz, this.uiOpts.ancestryBarImgSz]; + newNode.dims = [this.imgSz, this.imgSz]; return newNode; }); }, @@ -29,8 +32,8 @@ export default defineComponent({ // For child layout display: 'flex', flexDirection: this.vert ? 'column' : 'row', - gap: this.uiOpts.ancestryTileGap + 'px', - padding: this.uiOpts.ancestryTileGap + 'px', + gap: this.lytOpts.tileSpacing + 'px', + padding: this.lytOpts.tileSpacing + 'px', // Other backgroundColor: this.uiOpts.ancestryBarBgColor, boxShadow: this.uiOpts.shadowNormal, diff --git a/src/components/IconButton.vue b/src/components/IconButton.vue index 7ce5da4..71dddc3 100644 --- a/src/components/IconButton.vue +++ b/src/components/IconButton.vue @@ -3,14 +3,16 @@ import {defineComponent, PropType} from 'vue'; export default defineComponent({ props: { + size: {type: Number, default: 36}, disabled: {type: Boolean, default: false}, }, }); </script> <template> -<div class="w-9 h-9 p-2 rounded-full hover:cursor-pointer" - :class="{'hover:brightness-125': !disabled, 'brightness-75': disabled}"> +<div class="p-2 rounded-full hover:cursor-pointer" + :class="{'hover:brightness-125': !disabled, 'brightness-75': disabled}" + :style="{width: size + 'px', height: size + 'px', padding: (size / 5) + 'px'}"> <slot class="w-full h-full">?</slot> </div> </template> diff --git a/src/components/SearchModal.vue b/src/components/SearchModal.vue index 1a3ef27..a3fbd14 100644 --- a/src/components/SearchModal.vue +++ b/src/components/SearchModal.vue @@ -24,13 +24,13 @@ export default defineComponent({ }, computed: { infoIconStyles(): Record<string,string> { - let size = this.uiOpts.infoIconSz + 'px'; + let size = '18px'; return { width: size, height: size, minWidth: size, minHeight: size, - margin: this.uiOpts.infoIconMargin + 'px', + margin: '2px', }; }, suggDisplayStrings(): [string, string, string][] { @@ -71,8 +71,8 @@ export default defineComponent({ } }, onSearchModeChg(){ - this.uiOpts.jumpToSearchedNode = !this.uiOpts.jumpToSearchedNode; - this.$emit('settings-chg', [], ['jumpToSearchedNode']); + this.uiOpts.searchJumpMode = !this.uiOpts.searchJumpMode; + this.$emit('settings-chg', [], ['searchJumpMode']); }, resolveSearch(tolNodeName: string){ if (tolNodeName == ''){ @@ -223,7 +223,7 @@ export default defineComponent({ </div> <div class="my-auto mx-2 hover:cursor-pointer hover:brightness-75 rounded"> <log-in-icon @click.stop="onSearchModeChg" class="block w-8 h-8" - :class="uiOpts.jumpToSearchedNode ? 'text-stone-500' : 'text-stone-300'"/> + :class="uiOpts.searchJumpMode ? 'text-stone-500' : 'text-stone-300'"/> </div> </div> </div> diff --git a/src/components/SettingsModal.vue b/src/components/SettingsModal.vue index 92f4dce..8cebf7c 100644 --- a/src/components/SettingsModal.vue +++ b/src/components/SettingsModal.vue @@ -151,7 +151,7 @@ export default defineComponent({ <hr class="border-stone-400"/> <div> <label> - <input type="checkbox" v-model="uiOpts.jumpToSearchedNode" @change="onUiOptChg('jumpToSearchedNode')"/> + <input type="checkbox" v-model="uiOpts.searchJumpMode" @change="onUiOptChg('searchJumpMode')"/> Jump to search result </label> </div> diff --git a/src/components/Tile.vue b/src/components/Tile.vue index df281f5..1559f06 100644 --- a/src/components/Tile.vue +++ b/src/components/Tile.vue @@ -102,6 +102,9 @@ export default defineComponent({ return this.uiOpts.shadowNormal; } }, + fontSz(): number { + return 0.7 * this.lytOpts.headerSz; + }, styles(): Record<string,string> { let layoutStyles = { position: 'absolute', @@ -151,7 +154,7 @@ export default defineComponent({ return { // Image (and scrims) backgroundImage: this.tolNode.imgName != null ? - 'linear-gradient(to bottom, rgba(0,0,0,0.4), #0000 40%, #0000 60%, rgba(0,0,0,0.4) 100%),' + + 'linear-gradient(to bottom, rgba(0,0,0,0.4), #0000 40%),' + 'url(\'/img/' + (this.tolNode.imgName as string).replaceAll('\'', '\\\'') + '\')' : 'none', backgroundColor: '#1c1917', @@ -183,10 +186,10 @@ export default defineComponent({ } } return { - height: (this.uiOpts.leafHeaderFontSz + this.uiOpts.leafPadding * 2) + 'px', - padding: this.uiOpts.leafPadding + 'px', - lineHeight: this.uiOpts.leafHeaderFontSz + 'px', - fontSize: this.uiOpts.leafHeaderFontSz + 'px', + height: (this.fontSz * 1.3) + 'px', + padding: this.fontSz * 0.3 + 'px', + lineHeight: this.fontSz + 'px', + fontSize: this.fontSz + 'px', color: textColor, // For ellipsis overflow: 'hidden', @@ -220,7 +223,7 @@ export default defineComponent({ height: this.lytOpts.headerSz + 'px', borderTopLeftRadius: 'inherit', borderTopRightRadius: 'inherit', - backgroundColor: this.uiOpts.bgColorDark, + backgroundColor: this.uiOpts.nonleafHeaderColor, }; if (this.isOverflownRoot){ styles = { @@ -238,7 +241,7 @@ export default defineComponent({ nonleafHeaderTextStyles(): Record<string,string> { return { lineHeight: this.lytOpts.headerSz + 'px', - fontSize: this.uiOpts.nonleafHeaderFontSz + 'px', + fontSize: this.fontSz + 'px', textAlign: 'center', color: this.uiOpts.textColor, paddingLeft: '5px', @@ -249,15 +252,14 @@ export default defineComponent({ }; }, infoIconStyles(): Record<string,string> { - let size = this.uiOpts.infoIconSz + 'px'; + let size = (this.lytOpts.headerSz * 0.8) + 'px'; + let marginSz = (this.lytOpts.headerSz * 0.2) + 'px'; return { width: size, height: size, minWidth: size, minHeight: size, - margin: this.uiOpts.infoIconMargin + 'px', - marginTop: 'auto', - marginLeft: 'auto', + margin: this.isLeaf ? `auto ${marginSz} ${marginSz} auto` : `auto ${marginSz}`, }; }, sepSweptAreaStyles(): Record<string,string> { @@ -446,7 +448,7 @@ export default defineComponent({ height: '100%', // Image (and scrims) backgroundImage: (this.tolNode.imgName![idx]! != null) ? - 'linear-gradient(to bottom, rgba(0,0,0,0.4), #0000 40%, #0000 60%, rgba(0,0,0,0.4) 100%),' + + 'linear-gradient(to bottom, rgba(0,0,0,0.4), #0000 40%),' + 'url(\'/img/' + this.tolNode.imgName![idx]!.replaceAll('\'', '\\\'') + '\')' : 'none', backgroundColor: '#1c1917', diff --git a/src/components/TileInfoModal.vue b/src/components/TileInfoModal.vue index 423a2f5..90ad9a7 100644 --- a/src/components/TileInfoModal.vue +++ b/src/components/TileInfoModal.vue @@ -50,7 +50,7 @@ export default defineComponent({ }, dummyNode(): LayoutNode { let newNode = new LayoutNode(this.nodeName, []); - newNode.dims = [this.uiOpts.infoModalImgSz, this.uiOpts.infoModalImgSz]; + newNode.dims = [this.lytOpts.maxTileSz, this.lytOpts.maxTileSz]; return newNode; }, }, @@ -69,8 +69,8 @@ export default defineComponent({ 'none', backgroundColor: '#1c1917', backgroundSize: 'cover', - width: this.uiOpts.infoModalImgSz + 'px', - height: this.uiOpts.infoModalImgSz + 'px', + width: this.lytOpts.maxTileSz + 'px', + height: this.lytOpts.maxTileSz + 'px', }; }, licenseToUrl(license: string){ @@ -45,7 +45,7 @@ export type Action = 'tileInfo' | 'search' | 'autoMode' | 'settings' | 'help'; // export type UiOptions = { - // Shared styling + // Shared coloring/sizing textColor: string, // CSS color bgColor: string, bgColorLight: string, @@ -58,26 +58,19 @@ export type UiOptions = { shadowNormal: string, // CSS box-shadow value shadowHovered: string, shadowFocused: string, - // Styling for App - mainTileMargin: number, // px - // Styling for tiles + // Component coloring 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 - 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 - // Styling for other components - infoModalImgSz: number, // px - ancestryBarBgColor: string, // CSS color - ancestryBarImgSz: number, // px - ancestryTileGap: number, // Gap between ancestor tiles, in px - tutPaneSz: number, // px + nonleafHeaderColor: string, // CSS color + ancestryBarBgColor: string, + // Component sizing + ancestryBarBreadth: number, // px (fixed value needed for transitions) + tutPaneSz: number, // px (fixed value needed for transitions) + scrollGap: number, // Size of scroll bar, in px // Timing related clickHoldDuration: number, // Time after mousedown when a click-and-hold is recognised, in ms transitionDuration: number, // ms @@ -85,8 +78,7 @@ export type UiOptions = { // Other useReducedTree: boolean, searchSuggLimit: number, // Max number of search suggestions - jumpToSearchedNode: boolean, + searchJumpMode: boolean, tutorialSkip: boolean, disabledActions: Set<Action>, - scrollGap: number, // Size of scroll bar, in px }; |
