diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/App.vue | 116 | ||||
| -rw-r--r-- | src/components/SettingsModal.vue | 26 | ||||
| -rw-r--r-- | src/components/Tile.vue | 1 | ||||
| -rw-r--r-- | src/lib.ts | 12 |
4 files changed, 95 insertions, 60 deletions
diff --git a/src/App.vue b/src/App.vue index 0c9a96c..66db8c0 100644 --- a/src/App.vue +++ b/src/App.vue @@ -21,7 +21,7 @@ import type {TolMap, Action} from './lib'; import {LayoutNode} from './layout'; import type {LayoutOptions, LayoutTreeChg} from './layout'; // Functions -import {arraySum, randWeightedChoice, getScrollBarWidth} from './lib'; +import {arraySum, randWeightedChoice, getScrollBarWidth, getBreakpoint} from './lib'; import {initLayoutTree, initLayoutMap, tryLayout} from './layout'; // Type representing auto-mode actions @@ -46,56 +46,62 @@ const initialTolMap: TolMap = new Map(); initialTolMap.set("", new TolNode()); // Configurable options -const defaultLytOpts: LayoutOptions = { - tileSpacing: 8, //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: 'sqrt', //'linear' | 'sqrt' | 'pow-2/3' - sweepToParent: 'fallback', //'none' | 'prefer' | 'fallback' -}; -const defaultUiOpts = { - // For tiles - borderRadius: 5, //px - shadowNormal: '0 0 2px black', - shadowHighlight: '0 0 1px 2px greenyellow', - shadowFocused: '0 0 1px 2px orange', - infoIconSz: 18, //px - infoIconMargin: 2, //px - childThresholds: [[1, 'greenyellow'], [10, 'orange'], [100, 'red']], - headerColor: '#fafaf9', - // For leaf tiles - leafTilePadding: 4, //px - leafHeaderFontSz: 15, //px - // 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) - scrollGap: getScrollBarWidth(), - ancestryBarImgSz: defaultLytOpts.minTileSz * 2, //px - ancestryBarBgColor: '#44403c', - ancestryTileMargin: 5, //px (gap between detached-ancestor tiles) - infoModalImgSz: 200, - searchSuggLimit: 5, - autoWaitTime: 500, //ms (time to wait between actions (with their transitions)) - tutorialPaneSz: 200, - tutorialPaneBgColor: '#1c1917', - tutorialPaneTextColor: 'white', - // Timing related - tileChgDuration: 300, //ms (for tile move/expand/collapse) - clickHoldDuration: 400, //ms (duration after mousedown when a click-and-hold is recognised) - // Other - useReducedTree: false, - jumpToSearchedNode: false, -}; +function getDefaultLytOpts(): LayoutOptions { + let screenSz = getBreakpoint(); + return { + tileSpacing: screenSz == 'sm' ? 6 : 10, //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: 'sqrt', //'linear' | 'sqrt' | 'pow-2/3' + sweepToParent: 'fallback', //'none' | 'prefer' | 'fallback' + }; +} +function getDefaultUiOpts(){ + let screenSz = getBreakpoint(); + return { + // For tiles + borderRadius: 5, //px + shadowNormal: '0 0 2px black', + shadowHighlight: '0 0 1px 2px greenyellow', + shadowFocused: '0 0 1px 2px orange', + infoIconSz: 18, //px + infoIconMargin: 2, //px + childThresholds: [[1, 'greenyellow'], [10, 'orange'], [100, 'red']], + headerColor: '#fafaf9', + // For leaf tiles + leafTilePadding: 4, //px + leafHeaderFontSz: 15, //px + // 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) + scrollGap: getScrollBarWidth(), + ancestryBarImgSz: 100, //px + ancestryBarBgColor: '#44403c', + ancestryTileMargin: 5, //px (gap between detached-ancestor tiles) + infoModalImgSz: 200, + searchSuggLimit: 5, + autoWaitTime: 500, //ms (time to wait between actions (with their transitions)) + tutorialPaneSz: 200, + tutorialPaneBgColor: '#1c1917', + tutorialPaneTextColor: 'white', + // Timing related + tileChgDuration: 300, //ms (for tile move/expand/collapse) + clickHoldDuration: 400, //ms (duration after mousedown when a click-and-hold is recognised) + // Other + useReducedTree: false, + jumpToSearchedNode: false, + }; +} export default defineComponent({ data(){ @@ -570,6 +576,8 @@ export default defineComponent({ this.initTreeFromServer(); }, onResetSettings(){ + let defaultLytOpts = getDefaultLytOpts(); + let defaultUiOpts = getDefaultUiOpts(); if (this.uiOpts.useReducedTree != defaultUiOpts.useReducedTree){ this.onTreeChange(); } @@ -671,7 +679,7 @@ export default defineComponent({ // Helper methods initTreeFromServer(){ let urlPath = '/data/node'; - urlPath += this.uiOpts.useReducedTree ? '&tree=reduced' : ''; + urlPath += this.uiOpts.useReducedTree ? '?tree=reduced' : ''; fetch(urlPath) .then(response => response.json()) .then(obj => { @@ -704,7 +712,7 @@ export default defineComponent({ }); }, getLytOpts(){ - let opts: {[x: string]: boolean|number|string} = {...defaultLytOpts}; + let opts: {[x: string]: boolean|number|string} = getDefaultLytOpts(); for (let prop of Object.getOwnPropertyNames(opts)){ let item = localStorage.getItem('lyt ' + prop); if (item != null){ @@ -719,7 +727,7 @@ export default defineComponent({ return opts; }, getUiOpts(){ - let opts: {[x: string]: boolean|number|string|string[]|(string|number)[][]} = {...defaultUiOpts}; + let opts: {[x: string]: boolean|number|string|string[]|(string|number)[][]} = getDefaultUiOpts(); for (let prop of Object.getOwnPropertyNames(opts)){ let item = localStorage.getItem('ui ' + prop); if (item != null){ diff --git a/src/components/SettingsModal.vue b/src/components/SettingsModal.vue index e95dc34..c4f896d 100644 --- a/src/components/SettingsModal.vue +++ b/src/components/SettingsModal.vue @@ -10,15 +10,26 @@ export default defineComponent({ lytOpts: {type: Object as PropType<LayoutOptions>, required: true}, uiOpts: {type: Object, required: true}, }, + data(){ + return { + optChgd: false, + }; + }, methods: { onCloseClick(evt: Event){ if (evt.target == this.$el || (this.$refs.closeIcon as typeof CloseIcon).$el.contains(evt.target)){ - this.saveSettings(); + if (this.optChgd){ + this.saveSettings(); + } this.$emit('close'); } }, + onOptChg(){ + this.optChgd = true; + }, onLytOptChg(){ this.$emit('layout-setting-chg'); + this.onOptChg(); }, onMinTileSzChg(){ let minInput = this.$refs.minTileSzInput as HTMLInputElement; @@ -38,6 +49,7 @@ export default defineComponent({ }, onTreeChg(){ this.$emit('tree-chg'); + this.onOptChg(); }, saveSettings(){ const savedLytOpts = ['tileSpacing', 'minTileSz', 'maxTileSz', 'layoutType', 'sweepMode', 'sweepToParent', ]; @@ -52,6 +64,7 @@ export default defineComponent({ }, onReset(){ localStorage.clear(); + this.optChgd = false; this.$emit('reset'); console.log('Settings reset'); }, @@ -71,21 +84,21 @@ export default defineComponent({ <hr class="border-stone-400"/> <div> <label>Tile Spacing <input type="range" min="0" max="20" class="mx-2 w-[3cm]" - v-model.number="lytOpts.tileSpacing" @input="onLytOptChg"/></label> + v-model.number="lytOpts.tileSpacing" @change="onLytOptChg"/></label> </div> <hr class="border-stone-400"/> <div> <label> <span class="inline-block w-[3cm]">Min Tile Size</span> <input type="range" min="0" max="400" v-model.number="lytOpts.minTileSz" class="w-[3cm]" - @input="onMinTileSzChg" ref="minTileSzInput"/> + @change="onMinTileSzChg" ref="minTileSzInput"/> </label> </div> <div> <label> <span class="inline-block w-[3cm]">Max Tile Size</span> <input type="range" min="0" max="400" v-model.number="lytOpts.maxTileSz" class="w-[3cm]" - @input="onMaxTileSzChg" ref="maxTileSzInput"/> + @change="onMaxTileSzChg" ref="maxTileSzInput"/> </label> </div> <hr class="border-stone-400"/> @@ -149,11 +162,12 @@ 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"/></label> + v-model.number="uiOpts.tileChgDuration" @change="onOptChg"/></label> </div> <hr class="border-stone-400"/> <div> - <label> <input type="checkbox" v-model="uiOpts.jumpToSearchedNode"/> Jump to search result</label> + <label> <input type="checkbox" v-model="uiOpts.jumpToSearchedNode" @change="onOptChg"/> + Jump to search result</label> </div> <hr class="border-stone-400"/> <div> diff --git a/src/components/Tile.vue b/src/components/Tile.vue index 7b412b2..3054d46 100644 --- a/src/components/Tile.vue +++ b/src/components/Tile.vue @@ -242,6 +242,7 @@ export default defineComponent({ fontSize: this.uiOpts.nonleafHeaderFontSz + 'px', textAlign: 'center', color: this.uiOpts.nonleafHeaderColor, + paddingLeft: '5px', // For ellipsis overflow: 'hidden', textOverflow: 'ellipsis', @@ -47,6 +47,18 @@ export type Action = * General utility functions */ +export type Breakpoint = 'sm' | 'md' | 'lg'; // These represent screen sizes +export function getBreakpoint(): Breakpoint { + let w = window.innerWidth; + if (w < 768){ + return 'sm'; + } else if (w < 1024){ + return 'md'; + } else { + return 'lg'; + } +} + // Returns [0 ... len] export function range(len: number): number[] { return [...Array(len).keys()]; |
