aboutsummaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/AncestryBar.vue34
-rw-r--r--src/components/HelpModal.vue29
-rw-r--r--src/components/LoadingModal.vue17
-rw-r--r--src/components/SButton.vue2
-rw-r--r--src/components/SearchModal.vue40
-rw-r--r--src/components/SettingsModal.vue192
-rw-r--r--src/components/TileInfoModal.vue43
-rw-r--r--src/components/TolTile.vue89
-rw-r--r--src/components/TutorialPane.vue21
9 files changed, 222 insertions, 245 deletions
diff --git a/src/components/AncestryBar.vue b/src/components/AncestryBar.vue
index 1b4ee81..8eabf22 100644
--- a/src/components/AncestryBar.vue
+++ b/src/components/AncestryBar.vue
@@ -1,7 +1,7 @@
<template>
<div :style="styles" @wheel.stop="onWheelEvt" ref="rootRef">
<tol-tile v-for="(node, idx) in dummyNodes" :key="node.name" class="shrink-0"
- :layoutNode="node" :tolMap="tolMap" :nonAbsPos="true" :lytOpts="lytOpts" :uiOpts="uiOpts"
+ :layoutNode="node" :tolMap="tolMap" :nonAbsPos="true"
@leaf-click="onTileClick(nodes[idx])" @info-click="onInfoIconClick"/>
</div>
</template>
@@ -10,27 +10,27 @@
import {ref, computed, watch, onMounted, nextTick, PropType} from 'vue';
import TolTile from './TolTile.vue';
import {TolMap} from '../tol';
-import {LayoutNode, LayoutOptions} from '../layout';
-import {UiOptions} from '../lib';
+import {LayoutNode} from '../layout';
+import {useStore} from '../store';
// Refs
const rootRef = ref(null as HTMLDivElement | null);
+// Global store
+const store = useStore();
+
// Props + events
const props = defineProps({
nodes: {type: Array as PropType<LayoutNode[]>, required: true},
vert: {type: Boolean, default: false},
breadth: {type: Number, required: true},
- //
- lytOpts: {type: Object as PropType<LayoutOptions>, required: true},
- uiOpts: {type: Object as PropType<UiOptions>, required: true},
tolMap: {type: Object as PropType<TolMap>, required: true},
});
const emit = defineEmits(['ancestor-click', 'info-click']);
// Computed prop data for display
const imgSz = computed(() =>
- props.breadth - props.lytOpts.tileSpacing - props.uiOpts.scrollGap
+ props.breadth - store.lytOpts.tileSpacing - store.scrollGap
// Intentionally omitting extra tileSpacing, to allow for scrollGap with less image shrinkage
);
const dummyNodes = computed(() => props.nodes.map(n => {
@@ -54,11 +54,13 @@ function onWheelEvt(evt: WheelEvent){ // For converting vertical scrolling to ho
}
}
function scrollToEnd(){
- let el = rootRef.value!;
- if (props.vert){
- el.scrollTop = el.scrollHeight;
- } else {
- el.scrollLeft = el.scrollWidth;
+ let el = rootRef.value;
+ if (el != null){
+ if (props.vert){
+ el.scrollTop = el.scrollHeight;
+ } else {
+ el.scrollLeft = el.scrollWidth;
+ }
}
}
watch(props.nodes, () => {
@@ -75,12 +77,12 @@ const styles = computed(() => ({
display: 'flex',
flexDirection: props.vert ? 'column' : 'row',
alignItems: 'center',
- gap: props.lytOpts.tileSpacing + 'px',
- padding: props.lytOpts.tileSpacing + 'px',
+ gap: store.lytOpts.tileSpacing + 'px',
+ padding: store.lytOpts.tileSpacing + 'px',
overflowX: props.vert ? 'hidden' : 'auto',
overflowY: props.vert ? 'auto' : 'hidden',
// Other
- backgroundColor: props.uiOpts.ancestryBarBgColor,
- boxShadow: props.uiOpts.shadowNormal,
+ backgroundColor: store.ancestryBarBgColor,
+ boxShadow: store.shadowNormal,
}));
</script>
diff --git a/src/components/HelpModal.vue b/src/components/HelpModal.vue
index c403e53..d2576d5 100644
--- a/src/components/HelpModal.vue
+++ b/src/components/HelpModal.vue
@@ -385,13 +385,6 @@
two species of grass being described like a third closely-related species.
</p>
<br/>
- <h1 class="text-lg font-bold">Why did searching for 'goat' send me to the moths?</h1>
- <p>
- When you search for a name, then press enter, the first result is used.
- Currently, search suggestions are not ordered by well-known the taxons are,
- so the first result might mean 'Goat Moth' instead of 'Domestic Goat'.
- </p>
- <br/>
<h1 class="text-lg font-bold">Why do a lot of fish have their heads clipped out?</h1>
<p>
Cropping images into squares was done semi-automatically, and sometimes this
@@ -418,7 +411,7 @@
</template>
</s-collapsible>
</div>
- <s-button class="mx-auto mb-2" :style="{color: uiOpts.textColor, backgroundColor: uiOpts.bgColor}"
+ <s-button class="mx-auto mb-2" :style="{color: store.color.text, backgroundColor: store.color.bg}"
:disabled="tutOpen" @click.stop="onStartTutorial">
Start Tutorial
</s-button>
@@ -427,23 +420,25 @@
</template>
<script setup lang="ts">
-import {ref, computed, PropType} from 'vue';
+import {ref, computed} from 'vue';
import SButton from './SButton.vue';
import SCollapsible from './SCollapsible.vue';
import CloseIcon from './icon/CloseIcon.vue';
import DownIcon from './icon/DownIcon.vue';
-import {UiOptions} from '../lib';
+import {useStore} from '../store';
// Refs
const rootRef = ref(null as HTMLDivElement | null)
const closeRef = ref(null as typeof CloseIcon | null);
+// Global store
+const store = useStore();
+
// Props + events
-const props = defineProps({
+defineProps({
tutOpen: {type: Boolean, default: false},
- uiOpts: {type: Object as PropType<UiOptions>, required: true},
});
-const touchDevice = computed(() => props.uiOpts.touchDevice)
+const touchDevice = computed(() => store.touchDevice)
const emit = defineEmits(['close', 'start-tutorial']);
// Event handlers
@@ -459,12 +454,12 @@ function onStartTutorial(){
// Styles
const styles = computed(() => ({
- backgroundColor: props.uiOpts.bgColorAlt,
- borderRadius: props.uiOpts.borderRadius + 'px',
- boxShadow: props.uiOpts.shadowNormal,
+ backgroundColor: store.color.bgAlt,
+ borderRadius: store.borderRadius + 'px',
+ boxShadow: store.shadowNormal,
}));
const aStyles = computed(() => ({
- color: props.uiOpts.altColorDark,
+ color: store.color.altDark,
}));
// Classes
diff --git a/src/components/LoadingModal.vue b/src/components/LoadingModal.vue
index abd405c..3f941f2 100644
--- a/src/components/LoadingModal.vue
+++ b/src/components/LoadingModal.vue
@@ -9,19 +9,20 @@
</template>
<script setup lang="ts">
-import {computed, PropType} from 'vue';
+import {computed} from 'vue';
import LoaderIcon from './icon/LoaderIcon.vue';
-import {UiOptions} from '../lib';
+import {useStore} from '../store';
-const props = defineProps({
+const store = useStore();
+
+defineProps({
msg: {type: String, required: true},
- uiOpts: {type: Object as PropType<UiOptions>, required: true},
});
const styles = computed(() => ({
- color: props.uiOpts.textColor,
- backgroundColor: props.uiOpts.bgColorDark2,
- borderRadius: props.uiOpts.borderRadius + 'px',
- boxShadow: props.uiOpts.shadowNormal,
+ color: store.color.text,
+ backgroundColor: store.color.bgDark2,
+ borderRadius: store.borderRadius + 'px',
+ boxShadow: store.shadowNormal,
}));
</script>
diff --git a/src/components/SButton.vue b/src/components/SButton.vue
index 884fa30..487d6bd 100644
--- a/src/components/SButton.vue
+++ b/src/components/SButton.vue
@@ -6,7 +6,7 @@
</template>
<script setup lang="ts">
-const props = defineProps({
+defineProps({
disabled: {type: Boolean, default: false},
});
</script>
diff --git a/src/components/SearchModal.vue b/src/components/SearchModal.vue
index a035cac..1818529 100644
--- a/src/components/SearchModal.vue
+++ b/src/components/SearchModal.vue
@@ -10,7 +10,7 @@
</div>
<div class="absolute top-[100%] w-full overflow-hidden" :style="suggContainerStyles">
<div v-for="(sugg, idx) of searchSuggs" :key="sugg.name + '|' + sugg.canonicalName"
- :style="{backgroundColor: idx == focusedSuggIdx ? uiOpts.bgColorAltDark : uiOpts.bgColorAlt}"
+ :style="{backgroundColor: idx == focusedSuggIdx ? store.color.bgAltDark : store.color.bgAlt}"
class="border-b p-1 px-2 hover:underline hover:cursor-pointer flex"
@click="resolveSearch(sugg.canonicalName || sugg.name)">
<div class="grow overflow-hidden whitespace-nowrap text-ellipsis">
@@ -25,7 +25,7 @@
<div v-if="searchHadMoreSuggs" class="text-center">&#x2022; &#x2022; &#x2022;</div>
</div>
<label :style="animateLabelStyles" class="flex gap-1">
- <input type="checkbox" v-model="uiOpts.searchJumpMode" @change="$emit('setting-chg', 'searchJumpMode')"/>
+ <input type="checkbox" v-model="store.searchJumpMode" @change="emit('setting-chg', 'searchJumpMode')"/>
<div class="text-sm">Jump to result</div>
</label>
</div>
@@ -37,20 +37,22 @@ import {ref, computed, onMounted, onUnmounted, PropType} from 'vue';
import SearchIcon from './icon/SearchIcon.vue';
import InfoIcon from './icon/InfoIcon.vue';
import {TolNode, TolMap} from '../tol';
-import {LayoutNode, LayoutMap, LayoutOptions} from '../layout';
-import {queryServer, SearchSugg, SearchSuggResponse, UiOptions} from '../lib';
+import {LayoutNode, LayoutMap} from '../layout';
+import {queryServer, SearchSugg, SearchSuggResponse} from '../lib';
+import {useStore} from '../store';
// Refs
const rootRef = ref(null as HTMLDivElement | null);
const inputRef = ref(null as HTMLInputElement | null);
+// Global store
+const store = useStore();
+
// Props + events
const props = defineProps({
lytMap: {type: Object as PropType<LayoutMap>, required: true}, // Used to check if a searched-for node exists
activeRoot: {type: Object as PropType<LayoutNode>, required: true}, // Sent to server to reduce response size
tolMap: {type: Object as PropType<TolMap>, required: true}, // Upon a search response, gets new nodes added
- lytOpts: {type: Object as PropType<LayoutOptions>, required: true},
- uiOpts: {type: Object as PropType<UiOptions>, required: true},
});
const emit = defineEmits(['search', 'close', 'info-click', 'setting-chg', 'net-wait', 'net-get']);
@@ -101,8 +103,8 @@ async function onInput(){
let urlParams = new URLSearchParams({
type: 'sugg',
name: input.value,
- limit: String(props.uiOpts.searchSuggLimit),
- tree: props.uiOpts.tree,
+ limit: String(store.searchSuggLimit),
+ tree: store.tree,
});
// Query server, delaying/skipping if a request was recently sent
pendingSuggReqParams.value = urlParams;
@@ -168,7 +170,7 @@ async function resolveSearch(tolNodeName: string){
name: tolNodeName,
toroot: '1',
excl: props.activeRoot.name,
- tree: props.uiOpts.tree,
+ tree: store.tree,
});
emit('net-wait'); // Allows the parent component to show a loading-indicator
let responseObj: {[x: string]: TolNode} = await queryServer(urlParams);
@@ -216,7 +218,7 @@ function onInfoIconClick(nodeName: string){
// For keyboard shortcuts
function onKeyDown(evt: KeyboardEvent){
- if (props.uiOpts.disableShortcuts){
+ if (store.disableShortcuts){
return;
}
if (evt.key == 'f' && evt.ctrlKey){
@@ -232,26 +234,26 @@ onMounted(() => inputRef.value!.focus())
// Styles
const styles = computed((): Record<string,string> => {
- let br = props.uiOpts.borderRadius;
+ let br = store.borderRadius;
return {
- backgroundColor: props.uiOpts.bgColorAlt,
+ backgroundColor: store.color.bgAlt,
borderRadius: (searchSuggs.value.length == 0) ? `${br}px` : `${br}px ${br}px 0 0`,
- boxShadow: props.uiOpts.shadowNormal,
+ boxShadow: store.shadowNormal,
};
});
const suggContainerStyles = computed((): Record<string,string> => {
- let br = props.uiOpts.borderRadius;
+ let br = store.borderRadius;
return {
- backgroundColor: props.uiOpts.bgColorAlt,
- color: props.uiOpts.textColorAlt,
+ backgroundColor: store.color.bgAlt,
+ color: store.color.textAlt,
borderRadius: `0 0 ${br}px ${br}px`,
};
});
const animateLabelStyles = computed(() => ({
position: 'absolute',
- top: -props.lytOpts.headerSz - 2 + 'px',
+ top: -store.lytOpts.headerSz - 2 + 'px',
right: '0',
- height: props.lytOpts.headerSz + 'px',
- color: props.uiOpts.textColor,
+ height: store.lytOpts.headerSz + 'px',
+ color: store.color.text,
}));
</script>
diff --git a/src/components/SettingsModal.vue b/src/components/SettingsModal.vue
index df8444f..a55dc41 100644
--- a/src/components/SettingsModal.vue
+++ b/src/components/SettingsModal.vue
@@ -9,19 +9,19 @@
<h2 class="font-bold md:text-xl text-center pt-1 md:pt-2 md:pb-1">Timing</h2>
<div class="grid grid-cols-[130px_minmax(0,1fr)_65px] gap-1 px-2 md:px-3">
<!-- Row 1 -->
- <label for="animTimeInput" @click="onReset('UI', 'transitionDuration')" :class="rLabelClasses">
+ <label for="animTimeInput" @click="onResetOne('transitionDuration')" :class="rLabelClasses">
Animation Time
</label>
- <input type="range" min="0" max="1000" v-model.number="uiOpts.transitionDuration"
- @change="onSettingChg('UI', 'transitionDuration')" class="my-auto" name="animTimeInput"/>
- <div class="my-auto text-right">{{uiOpts.transitionDuration}} ms</div>
+ <input type="range" min="0" max="1000" v-model.number="store.transitionDuration"
+ @change="onSettingChg('transitionDuration')" class="my-auto" name="animTimeInput"/>
+ <div class="my-auto text-right">{{store.transitionDuration}} ms</div>
<!-- Row 2 -->
- <label for="autoDelayInput" @click="onReset('UI', 'autoActionDelay')" :class="rLabelClasses">
+ <label for="autoDelayInput" @click="onResetOne('autoActionDelay')" :class="rLabelClasses">
Auto-mode Delay
</label>
- <input type="range" min="100" max="1000" v-model.number="uiOpts.autoActionDelay"
- @change="onSettingChg('UI', 'autoActionDelay')" class="my-auto" name="autoDelayInput"/>
- <div class="my-auto text-right">{{uiOpts.autoActionDelay}} ms</div>
+ <input type="range" min="300" max="2000" v-model.number="store.autoActionDelay"
+ @change="onSettingChg('autoActionDelay')" class="my-auto" name="autoDelayInput"/>
+ <div class="my-auto text-right">{{store.autoActionDelay}} ms</div>
</div>
</div>
<div class="pb-2" :class="borderBClasses">
@@ -31,49 +31,49 @@
<div>Sweep leaves left</div>
<ul>
<li> <label> <input type="radio" v-model="sweepLeaves" :value="true"
- @change="onSettingChg('LYT', 'layoutType')"/> Yes </label> </li>
+ @change="onSettingChg('lytOpts.layoutType')"/> Yes </label> </li>
<li> <label> <input type="radio" v-model="sweepLeaves" :value="false"
- @change="onSettingChg('LYT', 'layoutType')"/> No </label> </li>
+ @change="onSettingChg('lytOpts.layoutType')"/> No </label> </li>
</ul>
</div>
<div>
<div>Sweep into parent</div>
<ul>
- <li> <label> <input type="radio" :disabled="!sweepLeaves" v-model="lytOpts.sweepToParent"
- value="none" @change="onSettingChg('LYT', 'sweepToParent')"/> Never </label> </li>
- <li> <label> <input type="radio" :disabled="!sweepLeaves" v-model="lytOpts.sweepToParent"
- value="prefer" @change="onSettingChg('LYT', 'sweepToParent')"/> Always </label> </li>
- <li> <label> <input type="radio" :disabled="!sweepLeaves" v-model="lytOpts.sweepToParent"
- value="fallback" @change="onSettingChg('LYT', 'sweepToParent')"/> If needed </label> </li>
+ <li> <label> <input type="radio" :disabled="!sweepLeaves" v-model="store.lytOpts.sweepToParent"
+ value="none" @change="onSettingChg('lytOpts.sweepToParent')"/> Never </label> </li>
+ <li> <label> <input type="radio" :disabled="!sweepLeaves" v-model="store.lytOpts.sweepToParent"
+ value="prefer" @change="onSettingChg('lytOpts.sweepToParent')"/> Always </label> </li>
+ <li> <label> <input type="radio" :disabled="!sweepLeaves" v-model="store.lytOpts.sweepToParent"
+ value="fallback" @change="onSettingChg('lytOpts.sweepToParent')"/> If needed </label> </li>
</ul>
</div>
</div>
<div class="grid grid-cols-[100px_minmax(0,1fr)_65px] gap-1 w-fit mx-auto px-2 md:px-3">
<!-- Row 1 -->
- <label for="minTileSizeInput" @click="onReset('LYT', 'minTileSz')" :class="rLabelClasses">
+ <label for="minTileSizeInput" @click="onResetOne('lytOpts.minTileSz')" :class="rLabelClasses">
Min Tile Size
</label>
<input type="range"
- min="15" :max="uiOpts.breakpoint == 'sm' ? 150 : 200" v-model.number="lytOpts.minTileSz"
- @input="onSettingChgThrottled('LYT', 'minTileSz')" @change="onSettingChg('LYT', 'minTileSz')"
+ min="15" :max="store.breakpoint == 'sm' ? 150 : 200" v-model.number="store.lytOpts.minTileSz"
+ @input="onSettingChgThrottled('lytOpts.minTileSz')" @change="onSettingChg('lytOpts.minTileSz')"
name="minTileSizeInput" ref="minTileSzRef"/>
- <div class="my-auto text-right">{{lytOpts.minTileSz}} px</div>
+ <div class="my-auto text-right">{{store.lytOpts.minTileSz}} px</div>
<!-- Row 2 -->
- <label for="maxTileSizeInput" @click="onReset('LYT', 'maxTileSz')" :class="rLabelClasses">
+ <label for="maxTileSizeInput" @click="onResetOne('lytOpts.maxTileSz')" :class="rLabelClasses">
Max Tile Size
</label>
- <input type="range" min="15" max="400" v-model.number="lytOpts.maxTileSz"
- @input="onSettingChgThrottled('LYT', 'maxTileSz')" @change="onSettingChg('LYT', 'maxTileSz')"
+ <input type="range" min="15" max="400" v-model.number="store.lytOpts.maxTileSz"
+ @input="onSettingChgThrottled('lytOpts.maxTileSz')" @change="onSettingChg('lytOpts.maxTileSz')"
name="maxTileSizeInput" ref="maxTileSzRef"/>
- <div class="my-auto text-right">{{lytOpts.maxTileSz}} px</div>
+ <div class="my-auto text-right">{{store.lytOpts.maxTileSz}} px</div>
<!-- Row 3 -->
- <label for="tileSpacingInput" @click="onReset('LYT', 'tileSpacing')" :class="rLabelClasses">
+ <label for="tileSpacingInput" @click="onResetOne('lytOpts.tileSpacing')" :class="rLabelClasses">
Tile Spacing
</label>
- <input type="range" min="0" max="20" v-model.number="lytOpts.tileSpacing"
- @input="onSettingChgThrottled('LYT', 'tileSpacing')" @change="onSettingChg('LYT', 'tileSpacing')"
+ <input type="range" min="0" max="20" v-model.number="store.lytOpts.tileSpacing"
+ @input="onSettingChgThrottled('lytOpts.tileSpacing')" @change="onSettingChg('lytOpts.tileSpacing')"
name="tileSpacingInput"/>
- <div class="my-auto text-right">{{lytOpts.tileSpacing}} px</div>
+ <div class="my-auto text-right">{{store.lytOpts.tileSpacing}} px</div>
</div>
</div>
<div class="pb-2 px-2 md:px-3" :class="borderBClasses">
@@ -81,29 +81,29 @@
<div>
Tree to use
<ul class="flex justify-evenly">
- <li> <label> <input type="radio" v-model="uiOpts.tree" value="trimmed"
- @change="onSettingChg('UI', 'tree')"/> Complex </label> </li>
- <li> <label> <input type="radio" v-model="uiOpts.tree" value="images"
- @change="onSettingChg('UI', 'tree')"/> Visual </label> </li>
- <li> <label> <input type="radio" v-model="uiOpts.tree" value="picked"
- @change="onSettingChg('UI', 'tree')"/> Minimal </label> </li>
+ <li> <label> <input type="radio" v-model="store.tree" value="trimmed"
+ @change="onSettingChg('tree')"/> Complex </label> </li>
+ <li> <label> <input type="radio" v-model="store.tree" value="images"
+ @change="onSettingChg('tree')"/> Visual </label> </li>
+ <li> <label> <input type="radio" v-model="store.tree" value="picked"
+ @change="onSettingChg('tree')"/> Minimal </label> </li>
</ul>
</div>
<div>
- <label> <input type="checkbox" v-model="uiOpts.searchJumpMode"
- @change="onSettingChg('UI', 'searchJumpMode')"/> Skip search animation </label>
+ <label> <input type="checkbox" v-model="store.searchJumpMode"
+ @change="onSettingChg('searchJumpMode')"/> Skip search animation </label>
</div>
<div>
- <label> <input type="checkbox" v-model="uiOpts.autoHide"
- @change="onSettingChg('UI', 'autoHide')"/> Auto-hide ancestors </label>
+ <label> <input type="checkbox" v-model="store.autoHide"
+ @change="onSettingChg('autoHide')"/> Auto-hide ancestors </label>
</div>
- <div v-if="uiOpts.touchDevice == false">
- <label> <input type="checkbox" v-model="uiOpts.disableShortcuts"
- @change="onSettingChg('UI', 'disableShortcuts')"/> Disable keyboard shortcuts </label>
+ <div v-if="store.touchDevice == false">
+ <label> <input type="checkbox" v-model="store.disableShortcuts"
+ @change="onSettingChg('disableShortcuts')"/> Disable keyboard shortcuts </label>
</div>
</div>
- <s-button class="mx-auto my-2" :style="{color: uiOpts.textColor, backgroundColor: uiOpts.bgColor}"
- @click="onResetAll">
+ <s-button class="mx-auto my-2" :style="{color: store.color.text, backgroundColor: store.color.bg}"
+ @click="onReset">
Reset
</s-button>
<transition name="fade">
@@ -114,11 +114,10 @@
</template>
<script setup lang="ts">
-import {ref, computed, watch, PropType} from 'vue';
+import {ref, computed, watch} from 'vue';
import SButton from './SButton.vue';
import CloseIcon from './icon/CloseIcon.vue';
-import {UiOptions, OptionType, getDefaultLytOpts, getDefaultUiOpts} from '../lib';
-import {LayoutOptions} from '../layout';
+import {useStore, StoreState} from '../store';
// Refs
const rootRef = ref(null as HTMLDivElement | null);
@@ -127,37 +126,36 @@ const minTileSzRef = ref(null as HTMLInputElement | null);
const maxTileSzRef = ref(null as HTMLInputElement | null);
const saveIndRef = ref(null as HTMLDivElement | null);
-// Props + events
-const props = defineProps({
- lytOpts: {type: Object as PropType<LayoutOptions>, required: true},
- uiOpts: {type: Object as PropType<UiOptions>, required: true},
-});
-const emit = defineEmits(['close', 'setting-chg', 'reset', ]);
+// Global store
+const store = useStore();
-// For settings
-const sweepLeaves = ref(props.lytOpts.layoutType == 'sweep');
- // For making only two of 'layoutType's values available for user selection)
-watch(sweepLeaves, (newVal) => {props.lytOpts.layoutType = newVal ? 'sweep' : 'rect'})
+// Events
+const emit = defineEmits(['close', 'setting-chg', 'reset']);
+
+// For making only two of 'layoutType's values available for user selection)
+const sweepLeaves = ref(store.lytOpts.layoutType == 'sweep');
+watch(sweepLeaves, (newVal) => {store.lytOpts.layoutType = newVal ? 'sweep' : 'rect'})
// Settings change handling
const saved = ref(false); // Set to true after a setting is saved
-const settingChgTimeout = ref(0); // Used to throttle some setting-change handling
-function onSettingChg(optionType: OptionType, option: string){
+let settingChgTimeout = 0; // Used to throttle some setting-change handling
+function onSettingChg(option: string){
// Maintain min/max-tile-size consistency
- if (optionType == 'LYT' && (option == 'minTileSz' || option == 'maxTileSz')){
+ if (option == 'lytOpts.minTileSz' || option == 'lytOpts.maxTileSz'){
let minInput = minTileSzRef.value!;
let maxInput = maxTileSzRef.value!;
- if (option == 'minTileSz' && Number(minInput.value) > Number(maxInput.value)){
- props.lytOpts.maxTileSz = props.lytOpts.minTileSz;
- emit('setting-chg', 'LYT', 'maxTileSz');
- } else if (option == 'maxTileSz' && Number(maxInput.value) < Number(minInput.value)){
- props.lytOpts.minTileSz = props.lytOpts.maxTileSz;
- emit('setting-chg', 'LYT', 'minTileSz');
+ if (Number(minInput.value) > Number(maxInput.value)){
+ if (option == 'lytOpts.minTileSz'){
+ store.lytOpts.maxTileSz = store.lytOpts.minTileSz;
+ emit('setting-chg', 'lytOpts.maxTileSz');
+ } else {
+ store.lytOpts.minTileSz = store.lytOpts.maxTileSz;
+ emit('setting-chg', 'lytOpts.minTileSz');
+ }
}
}
- // Notify parent component
- emit('setting-chg', optionType, option,
- {relayout: optionType == 'LYT', reinit: optionType == 'UI' && option == 'tree'});
+ // Notify parent (might need to relayout)
+ emit('setting-chg', option);
// Possibly make saved-indicator appear/animate
if (!saved.value){
saved.value = true;
@@ -168,48 +166,24 @@ function onSettingChg(optionType: OptionType, option: string){
el.classList.add('animate-flash-green');
}
}
-function onSettingChgThrottled(optionType: OptionType, option: string){
- if (settingChgTimeout.value == 0){
- settingChgTimeout.value = setTimeout(() => {
- settingChgTimeout.value = 0;
- onSettingChg(optionType, option);
- }, props.uiOpts.animationDelay);
+function onSettingChgThrottled(option: string){
+ if (settingChgTimeout == 0){
+ settingChgTimeout = setTimeout(() => {
+ settingChgTimeout = 0;
+ onSettingChg(option);
+ }, store.animationDelay);
}
}
-function onReset(optionType: OptionType, option: string){
- // Restore the setting's default
- let defaultLytOpts = getDefaultLytOpts();
- let defaultUiOpts = getDefaultUiOpts(defaultLytOpts);
- if (optionType == 'LYT'){
- let lytOpt = option as keyof LayoutOptions;
- if (props.lytOpts[lytOpt] == defaultLytOpts[lytOpt]){
- return;
- }
- (props.lytOpts[lytOpt] as any) = defaultLytOpts[lytOpt];
- if (option == 'layoutType'){
- sweepLeaves.value = props.lytOpts.layoutType == 'sweep';
- }
- } else {
- let uiOpt = option as keyof UiOptions;
- if (props.uiOpts[uiOpt] == defaultUiOpts[uiOpt]){
- return;
- }
- (props.uiOpts[uiOpt] as any) = defaultUiOpts[uiOpt];
+function onResetOne(option: string){
+ store.resetOne(option);
+ if (option == 'lytOpts.layoutType'){
+ sweepLeaves.value = (store.lytOpts.layoutType == 'sweep');
}
- // Notify parent component
- onSettingChg(optionType, option);
+ onSettingChg(option);
}
-function onResetAll(){
- // Restore default options
- let defaultLytOpts = getDefaultLytOpts();
- let defaultUiOpts = getDefaultUiOpts(defaultLytOpts);
- let needReinit = props.uiOpts.tree != defaultUiOpts.tree;
- Object.assign(props.lytOpts, defaultLytOpts);
- Object.assign(props.uiOpts, defaultUiOpts);
- // Notify parent component
- emit('reset', needReinit);
- // Clear saved-indicator
- saved.value = false;
+function onReset(){
+ emit('reset'); // Notify parent (might need to relayout)
+ saved.value = false; // Clear saved-indicator
}
// Close handling
@@ -221,9 +195,9 @@ function onClose(evt: Event){
// Styles and classes
const styles = computed(() => ({
- backgroundColor: props.uiOpts.bgColorAlt,
- borderRadius: props.uiOpts.borderRadius + 'px',
- boxShadow: props.uiOpts.shadowNormal,
+ backgroundColor: store.color.bgAlt,
+ borderRadius: store.borderRadius + 'px',
+ boxShadow: store.shadowNormal,
}));
const borderBClasses = 'border-b border-stone-400';
const rLabelClasses = "w-fit hover:cursor-pointer hover:text-lime-600"; // For reset-upon-click labels
diff --git a/src/components/TileInfoModal.vue b/src/components/TileInfoModal.vue
index fc09d86..2d0e354 100644
--- a/src/components/TileInfoModal.vue
+++ b/src/components/TileInfoModal.vue
@@ -58,35 +58,35 @@
</template>
<template v-slot:content>
<ul class="rounded shadow overflow-x-auto p-1"
- :style="{backgroundColor: uiOpts.bgColor, color: uiOpts.textColor}">
+ :style="{backgroundColor: store.color.bg, color: store.color.text}">
<li v-if="imgInfos[idx]!.url != ''">
- <span :style="{color: uiOpts.altColor}">Source: </span>
+ <span :style="{color: store.color.alt}">Source: </span>
<a :href="imgInfos[idx]!.url" target="_blank">Link</a>
<external-link-icon class="inline-block w-3 h-3 ml-1"/>
</li>
<li v-if="imgInfos[idx]!.artist != ''" class="whitespace-nowrap">
- <span :style="{color: uiOpts.altColor}">Artist: </span>
+ <span :style="{color: store.color.alt}">Artist: </span>
{{imgInfos[idx]!.artist}}
</li>
<li v-if="imgInfos[idx]!.credit != ''" class="whitespace-nowrap">
- <span :style="{color: uiOpts.altColor}">Credits: </span>
+ <span :style="{color: store.color.alt}">Credits: </span>
{{imgInfos[idx]!.credit}}
</li>
<li>
- <span :style="{color: uiOpts.altColor}">License: </span>
+ <span :style="{color: store.color.alt}">License: </span>
<a :href="licenseToUrl(imgInfos[idx]!.license)" target="_blank">
{{imgInfos[idx]!.license}}
</a>
<external-link-icon class="inline-block w-3 h-3 ml-1"/>
</li>
<li v-if="imgInfos[idx]!.src != 'picked'">
- <span :style="{color: uiOpts.altColor}">Obtained via: </span>
+ <span :style="{color: store.color.alt}">Obtained via: </span>
<a v-if="imgInfos[idx]!.src == 'eol'" href="https://eol.org/">EoL</a>
<a v-else href="https://www.wikipedia.org/">Wikipedia</a>
<external-link-icon class="inline-block w-3 h-3 ml-1"/>
</li>
<li>
- <span :style="{color: uiOpts.altColor}">Changes: </span>
+ <span :style="{color: store.color.alt}">Changes: </span>
Cropped and resized
</li>
</ul>
@@ -119,22 +119,21 @@ import ExternalLinkIcon from './icon/ExternalLinkIcon.vue';
import DownIcon from './icon/DownIcon.vue';
import LinkIcon from './icon/LinkIcon.vue';
import {TolNode} from '../tol';
-import {LayoutOptions} from '../layout';
-import {getImagePath, DescInfo, ImgInfo, InfoResponse, UiOptions} from '../lib';
+import {getImagePath, DescInfo, ImgInfo, InfoResponse} from '../lib';
import {capitalizeWords} from '../util';
+import {useStore} from '../store';
// Refs
const rootRef = ref(null as HTMLDivElement | null);
const closeRef = ref(null as typeof CloseIcon | null);
+// Global store
+const store = useStore();
+
// Props + events
const props = defineProps({
- // Node data to display
nodeName: {type: String, required: true},
infoResponse: {type: Object as PropType<InfoResponse>, required: true},
- // Options
- lytOpts: {type: Object as PropType<LayoutOptions>, required: true},
- uiOpts: {type: Object as PropType<UiOptions>, required: true},
});
const emit = defineEmits(['close']);
@@ -239,9 +238,9 @@ function onLinkIconClick(){
// Styles
const styles = computed(() => ({
- backgroundColor: props.uiOpts.bgColorAlt,
- borderRadius: props.uiOpts.borderRadius + 'px',
- boxShadow: props.uiOpts.shadowNormal,
+ backgroundColor: store.color.bgAlt,
+ borderRadius: store.borderRadius + 'px',
+ boxShadow: store.shadowNormal,
overflow: 'visible auto',
}));
function getImgStyles(tolNode: TolNode | null): Record<string,string> {
@@ -255,10 +254,10 @@ function getImgStyles(tolNode: TolNode | null): Record<string,string> {
backgroundImage: imgName != null ?
`url('${getImagePath(imgName as string)}')` :
'none',
- backgroundColor: props.uiOpts.bgColorDark,
+ backgroundColor: store.color.bgDark,
backgroundSize: 'cover',
- borderRadius: props.uiOpts.borderRadius + 'px',
- boxShadow: props.uiOpts.shadowNormal,
+ borderRadius: store.borderRadius + 'px',
+ boxShadow: store.shadowNormal,
};
}
function iucnStyles(iucn: string): Record<string,string>{
@@ -277,8 +276,8 @@ function iucnStyles(iucn: string): Record<string,string>{
};
}
const linkCopyLabelStyles = computed(() => ({
- color: props.uiOpts.textColor,
- backgroundColor: props.uiOpts.bgColor,
- borderRadius: props.uiOpts.borderRadius + 'px',
+ color: store.color.text,
+ backgroundColor: store.color.bg,
+ borderRadius: store.borderRadius + 'px',
}));
</script>
diff --git a/src/components/TolTile.vue b/src/components/TolTile.vue
index 810c5b7..a30c6d9 100644
--- a/src/components/TolTile.vue
+++ b/src/components/TolTile.vue
@@ -37,7 +37,7 @@
</transition>
</div>
<tol-tile v-for="child in visibleChildren" :key="child.name"
- :layoutNode="child" :tolMap="tolMap" :lytOpts="lytOpts" :uiOpts="uiOpts" :overflownDim="overflownDim"
+ :layoutNode="child" :tolMap="tolMap" :overflownDim="overflownDim"
@leaf-click="onInnerLeafClick" @nonleaf-click="onInnerNonleafClick"
@leaf-click-held="onInnerLeafClickHeld" @nonleaf-click-held="onInnerNonleafClickHeld"
@info-click="onInnerInfoIconClick"/>
@@ -52,22 +52,23 @@
import {ref, computed, watch, PropType} from 'vue';
import InfoIcon from './icon/InfoIcon.vue';
import {TolMap} from '../tol';
-import {LayoutNode, LayoutOptions} from '../layout';
-import {getImagePath, UiOptions} from '../lib';
+import {LayoutNode} from '../layout';
+import {getImagePath} from '../lib';
import {capitalizeWords} from '../util';
+import {useStore} from '../store';
const SCRIM_GRADIENT = 'linear-gradient(to bottom, rgba(0,0,0,0.4), #0000 40%, #0000 60%, rgba(0,0,0,0.2) 100%)';
// Refs
const rootRef = ref(null as HTMLDivElement | null);
+// Global store
+const store = useStore();
+
// Props + events
const props = defineProps({
layoutNode: {type: Object as PropType<LayoutNode>, required: true},
tolMap: {type: Object as PropType<TolMap>, required: true},
- // Options
- lytOpts: {type: Object as PropType<LayoutOptions>, required: true},
- uiOpts: {type: Object as PropType<UiOptions>, required: true},
// Other
skipTransition: {type: Boolean, default: false},
nonAbsPos: {type: Boolean, default: false},
@@ -120,26 +121,26 @@ const isOverflownRoot = computed(() =>
props.overflownDim > 0 && !props.layoutNode.hidden && props.layoutNode.children.length > 0
);
const hasFocusedChild = computed(() => props.layoutNode.children.some(n => n.hasFocus));
-const infoIconDisabled = computed(() => !props.uiOpts.disabledActions.has('tileInfo'));
+const infoIconDisabled = computed(() => !store.disabledActions.has('tileInfo'));
// Click/hold handling
const clickHoldTimer = ref(0); // Used to recognise click-and-hold events
function onMouseDown(): void {
highlight.value = false;
- if (!props.uiOpts.touchDevice){
+ if (!store.touchDevice){
// Wait for a mouseup or click-hold
clearTimeout(clickHoldTimer.value);
clickHoldTimer.value = setTimeout(() => {
clickHoldTimer.value = 0;
onClickHold();
- }, props.uiOpts.clickHoldDuration);
+ }, store.clickHoldDuration);
} else {
// Wait for or recognise a double-click
if (clickHoldTimer.value == 0){
clickHoldTimer.value = setTimeout(() => {
clickHoldTimer.value = 0;
onClick();
- }, props.uiOpts.clickHoldDuration);
+ }, store.clickHoldDuration);
} else {
clearTimeout(clickHoldTimer.value)
clickHoldTimer.value = 0;
@@ -148,7 +149,7 @@ function onMouseDown(): void {
}
}
function onMouseUp(): void {
- if (!props.uiOpts.touchDevice){
+ if (!store.touchDevice){
if (clickHoldTimer.value > 0){
clearTimeout(clickHoldTimer.value);
clickHoldTimer.value = 0;
@@ -226,14 +227,14 @@ function onScroll(): void {
pendingScrollHdlr.value = setTimeout(() => {
scrollOffset.value = rootRef.value!.scrollTop;
pendingScrollHdlr.value = 0;
- }, props.uiOpts.animationDelay);
+ }, store.animationDelay);
}
}
// Scroll to focused child if overflownRoot
watch(hasFocusedChild, (newVal: boolean) => {
if (newVal && isOverflownRoot.value){
let focusedChild = props.layoutNode.children.find(n => n.hasFocus)!
- let bottomY = focusedChild.pos[1] + focusedChild.dims[1] + props.lytOpts.tileSpacing;
+ let bottomY = focusedChild.pos[1] + focusedChild.dims[1] + store.lytOpts.tileSpacing;
let scrollTop = Math.max(0, bottomY - (props.overflownDim / 2)); // No need to manually cap at max
rootRef.value!.scrollTop = scrollTop;
}
@@ -253,16 +254,16 @@ function onTransitionEnd(){
// For setting transition state (allows external triggering, like via search and auto-mode)
watch(() => props.layoutNode.pos, (newVal: [number, number], oldVal: [number, number]) => {
let valChanged = newVal[0] != oldVal[0] || newVal[1] != oldVal[1];
- if (valChanged && props.uiOpts.transitionDuration > 100 && !inTransition.value){
+ if (valChanged && store.transitionDuration > 100 && !inTransition.value){
inTransition.value = true;
- setTimeout(onTransitionEnd, props.uiOpts.transitionDuration);
+ setTimeout(onTransitionEnd, store.transitionDuration);
}
});
watch(() => props.layoutNode.dims, (newVal: [number, number], oldVal: [number, number]) => {
let valChanged = newVal[0] != oldVal[0] || newVal[1] != oldVal[1];
- if (valChanged && props.uiOpts.transitionDuration > 100 && !inTransition.value){
+ if (valChanged && store.transitionDuration > 100 && !inTransition.value){
inTransition.value = true;
- setTimeout(onTransitionEnd, props.uiOpts.transitionDuration);
+ setTimeout(onTransitionEnd, store.transitionDuration);
}
});
@@ -285,7 +286,7 @@ const inFlash = ref(false); // Used to 'flash' the tile when focused
watch(() => props.layoutNode.hasFocus, (newVal: boolean, oldVal: boolean) => {
if (newVal != oldVal && newVal){
inFlash.value = true;
- setTimeout(() => {inFlash.value = false;}, props.uiOpts.transitionDuration);
+ setTimeout(() => {inFlash.value = false;}, 300);
}
});
@@ -294,32 +295,32 @@ const justUnhidden = ref(false); // Used to allow overflow temporarily after bei
watch(() => props.layoutNode.hidden, (newVal: boolean, oldVal: boolean) => {
if (oldVal && !newVal){
justUnhidden.value = true;
- setTimeout(() => {justUnhidden.value = false}, props.uiOpts.transitionDuration + 100);
+ setTimeout(() => {justUnhidden.value = false}, store.transitionDuration + 100);
}
});
// Styles + classes
const nonleafBgColor = computed(() => {
- let colorArray = props.uiOpts.nonleafBgColors;
+ let colorArray = store.nonleafBgColors;
return colorArray[props.layoutNode.depth % colorArray.length];
});
const boxShadow = computed((): string => {
if (highlight.value){
- return props.uiOpts.shadowHovered;
+ return store.shadowHovered;
} else if (props.layoutNode.hasFocus && !inTransition.value){
- return props.uiOpts.shadowFocused;
+ return store.shadowFocused;
} else {
- return props.uiOpts.shadowNormal;
+ return store.shadowNormal;
}
});
const fontSz = computed((): number => {
// These values are a compromise between dynamic font size and code simplicity
if (props.layoutNode.dims[0] >= 150){
- return props.lytOpts.headerSz * 0.8;
+ return store.lytOpts.headerSz * 0.8;
} else if (props.layoutNode.dims[0] >= 80){
- return props.lytOpts.headerSz * 0.7;
+ return store.lytOpts.headerSz * 0.7;
} else {
- return props.lytOpts.headerSz * 0.6;
+ return store.lytOpts.headerSz * 0.6;
}
});
//
@@ -330,11 +331,11 @@ const styles = computed((): Record<string,string> => {
top: props.layoutNode.pos[1] + 'px',
width: props.layoutNode.dims[0] + 'px',
height: props.layoutNode.dims[1] + 'px',
- borderRadius: props.uiOpts.borderRadius + 'px',
+ borderRadius: store.borderRadius + 'px',
boxShadow: boxShadow.value,
visibility: 'visible',
// Transition related
- transitionDuration: (props.skipTransition ? 0 : props.uiOpts.transitionDuration) + 'ms',
+ transitionDuration: (props.skipTransition ? 0 : store.transitionDuration) + 'ms',
transitionProperty: 'left, top, width, height, visibility',
transitionTimingFunction: 'ease-out',
zIndex: inTransition.value && wasClicked.value ? '1' : '0',
@@ -342,10 +343,10 @@ const styles = computed((): Record<string,string> => {
'hidden' : 'visible',
// CSS variables
'--nonleafBgColor': nonleafBgColor.value,
- '--tileSpacing': props.lytOpts.tileSpacing + 'px',
+ '--tileSpacing': store.lytOpts.tileSpacing + 'px',
};
if (!isLeaf.value){
- let borderR = props.uiOpts.borderRadius + 'px';
+ let borderR = store.borderRadius + 'px';
if (props.layoutNode.sepSweptArea != null){
borderR = props.layoutNode.sepSweptArea.sweptLeft ?
`${borderR} ${borderR} ${borderR} 0` :
@@ -354,7 +355,7 @@ const styles = computed((): Record<string,string> => {
layoutStyles.borderRadius = borderR;
}
if (isOverflownRoot.value){
- layoutStyles.width = (props.layoutNode.dims[0] + props.uiOpts.scrollGap) + 'px';
+ layoutStyles.width = (props.layoutNode.dims[0] + store.scrollGap) + 'px';
layoutStyles.height = props.overflownDim + 'px';
layoutStyles.overflow = 'hidden scroll';
}
@@ -377,7 +378,7 @@ const leafStyles = computed((): Record<string,string> => {
backgroundImage: tolNode.value.imgName != null ?
`${SCRIM_GRADIENT},url('${getImagePath(tolNode.value.imgName as string)}')` :
'none',
- backgroundColor: props.uiOpts.bgColorDark,
+ backgroundColor: store.color.bgDark,
backgroundSize: 'cover',
};
}
@@ -385,8 +386,8 @@ const leafStyles = computed((): Record<string,string> => {
});
const leafHeaderStyles = computed((): Record<string,string> => {
let numChildren = tolNode.value.children.length;
- let textColor = props.uiOpts.textColor;
- for (let [threshold, color] of props.uiOpts.childQtyColors){
+ let textColor = store.color.text;
+ for (let [threshold, color] of store.childQtyColors){
if (numChildren >= threshold){
textColor = color;
} else {
@@ -413,7 +414,7 @@ function leafSubImgStyles(idx: number): Record<string,string> {
backgroundImage: (tolNode.value.imgName![idx]! != null) ?
`${SCRIM_GRADIENT},url('${getImagePath(tolNode.value.imgName![idx]! as string)}')` :
'none',
- backgroundColor: props.uiOpts.bgColorDark,
+ backgroundColor: store.color.bgDark,
backgroundSize: '125%',
borderRadius: 'inherit',
clipPath: (idx == 0) ? 'polygon(0 0, 100% 0, 0 100%)' : 'polygon(100% 0, 0 100%, 100% 100%)',
@@ -438,10 +439,10 @@ const nonleafStyles = computed((): Record<string,string> => {
const nonleafHeaderStyles = computed((): Record<string,string> => {
let styles: Record<string,string> = {
position: 'static',
- height: props.lytOpts.headerSz + 'px',
+ height: store.lytOpts.headerSz + 'px',
borderTopLeftRadius: 'inherit',
borderTopRightRadius: 'inherit',
- backgroundColor: props.uiOpts.nonleafHeaderColor,
+ backgroundColor: store.nonleafHeaderColor,
};
if (isOverflownRoot.value){
styles = {
@@ -451,7 +452,7 @@ const nonleafHeaderStyles = computed((): Record<string,string> => {
left: '0',
borderTopRightRadius: '0',
zIndex: '1',
- boxShadow: props.uiOpts.shadowNormal,
+ boxShadow: store.shadowNormal,
};
}
return styles;
@@ -461,19 +462,19 @@ const nonleafHeaderTextStyles = computed(() => ({
fontSize: fontSz.value + 'px',
paddingLeft: (fontSz.value * 0.2) + 'px',
textAlign: 'center',
- color: props.uiOpts.textColor,
+ color: store.color.text,
// For ellipsis
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
}));
const sepSweptAreaStyles = computed((): Record<string,string> => {
- let borderR = props.uiOpts.borderRadius + 'px';
+ let borderR = store.borderRadius + 'px';
let styles = {
position: 'absolute',
backgroundColor: nonleafBgColor.value,
boxShadow: boxShadow.value,
- transitionDuration: props.uiOpts.transitionDuration + 'ms',
+ transitionDuration: store.transitionDuration + 'ms',
transitionProperty: 'left, top, width, height, visibility',
transitionTimingFunction: 'ease-out',
};
@@ -495,7 +496,7 @@ const sepSweptAreaStyles = computed((): Record<string,string> => {
...styles,
visibility: 'hidden',
left: '0',
- top: props.lytOpts.headerSz + 'px',
+ top: store.lytOpts.headerSz + 'px',
width: '0',
height: '0',
borderRadius: borderR,
@@ -512,8 +513,8 @@ const sepSweptAreaHideEdgeClass = computed((): string => {
}
});
const infoIconStyles = computed((): Record<string,string> => {
- let size = (props.lytOpts.headerSz * 0.85);
- let marginSz = (props.lytOpts.headerSz - size);
+ let size = (store.lytOpts.headerSz * 0.85);
+ let marginSz = (store.lytOpts.headerSz - size);
return {
width: size + 'px',
height: size + 'px',
diff --git a/src/components/TutorialPane.vue b/src/components/TutorialPane.vue
index 4c24bae..3ccbc46 100644
--- a/src/components/TutorialPane.vue
+++ b/src/components/TutorialPane.vue
@@ -70,7 +70,11 @@
import {ref, computed, watch, onMounted, PropType} from 'vue';
import SButton from './SButton.vue';
import CloseIcon from './icon/CloseIcon.vue';
-import {Action, UiOptions} from '../lib';
+import {Action} from '../lib';
+import {useStore} from '../store';
+
+// Global store
+const store = useStore();
// Props + events
const props = defineProps({
@@ -79,9 +83,8 @@ const props = defineProps({
triggerFlag: {type: Boolean, required: true},
// Used to indicate that a tutorial-requested 'trigger' action has been done
skipWelcome: {type: Boolean, default: false},
- uiOpts: {type: Object as PropType<UiOptions>, required: true},
});
-const touchDevice = computed(() => props.uiOpts.touchDevice);
+const touchDevice = computed(() => store.touchDevice);
const emit = defineEmits(['close', 'stage-chg', 'skip']);
// For tutorial stage
@@ -118,13 +121,13 @@ function onStageChange(){
if (stage.value == 1 && !disabledOnce){
for (let action of STAGE_ACTIONS){
if (action != null && !props.actionsDone.has(action)){
- props.uiOpts.disabledActions.add(action);
+ store.disabledActions.add(action);
}
}
disabledOnce = true;
}
// Enable action for this stage
- props.uiOpts.disabledActions.delete(STAGE_ACTIONS[stage.value - 1]);
+ store.disabledActions.delete(STAGE_ACTIONS[stage.value - 1]);
// Notify of new trigger-action
emit('stage-chg', STAGE_ACTIONS[stage.value - 1]);
// After stage 1, show prev/next buttons
@@ -148,8 +151,8 @@ watch(() => props.triggerFlag, () => {
// Styles
const styles = computed(() => ({
- backgroundColor: props.uiOpts.bgColorDark,
- color: props.uiOpts.textColor,
+ backgroundColor: store.color.bgDark,
+ color: store.color.text,
}));
const contentStyles = {
padding: '0 0.5cm',
@@ -157,7 +160,7 @@ const contentStyles = {
textAlign: 'center',
};
const buttonStyles = computed(() => ({
- color: props.uiOpts.textColor,
- backgroundColor: props.uiOpts.bgColor,
+ color: store.color.text,
+ backgroundColor: store.color.bg,
}));
</script>