aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/App.vue18
-rw-r--r--src/components/TileInfoModal.vue35
-rw-r--r--src/components/icon/LinkIcon.vue12
-rw-r--r--src/index.css13
4 files changed, 73 insertions, 5 deletions
diff --git a/src/App.vue b/src/App.vue
index df2b9b7..cd38621 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -889,8 +889,13 @@ export default defineComponent({
},
// For initialisation
async initTreeFromServer(firstInit = true){
+ // Get possible target node from URL
+ let nodeName = (new URL(window.location.href)).searchParams.get('node');
// Query server
let urlParams = 'type=node';
+ if (nodeName != null){
+ urlParams += '&name=' + encodeURIComponent(nodeName) + '&toroot=true';
+ }
urlParams += '&tree=' + this.uiOpts.tree;
let responseObj: {[x: string]: TolNode} = await this.loadFromServer(urlParams);
if (responseObj == null){
@@ -915,14 +920,21 @@ export default defineComponent({
this.layoutTree = initLayoutTree(this.tolMap, rootName, 0);
this.activeRoot = this.layoutTree;
this.layoutMap = initLayoutMap(this.layoutTree);
- // Relayout
- await this.updateAreaDims();
- this.relayoutWithCollapse(false);
// Skip initial transition
if (firstInit){
this.justInitialised = true;
setTimeout(() => {this.justInitialised = false}, this.uiOpts.transitionDuration);
}
+ // Relayout
+ await this.updateAreaDims();
+ this.relayoutWithCollapse(false);
+ // Possibly jump to a target node
+ if (nodeName != null){
+ let oldSetting = this.uiOpts.searchJumpMode;
+ this.uiOpts.searchJumpMode = true;
+ await this.onSearch(nodeName);
+ this.uiOpts.searchJumpMode = oldSetting;
+ }
},
async reInit(){
if (this.activeRoot != this.layoutTree){
diff --git a/src/components/TileInfoModal.vue b/src/components/TileInfoModal.vue
index 92d19de..9e0f2c3 100644
--- a/src/components/TileInfoModal.vue
+++ b/src/components/TileInfoModal.vue
@@ -5,7 +5,16 @@
:style="styles">
<div class="pb-1 md:pb-2">
<close-icon @click.stop="onClose" ref="closeIcon"
- class="absolute top-1 right-1 md:m-2 w-8 h-8 hover:cursor-pointer"/>
+ class="absolute top-1 right-1 md:top-2 md:right-2 w-8 h-8 hover:cursor-pointer"/>
+ <div class="absolute top-1 left-1 md:top-2 md:left-2 flex items-center">
+ <a :href="'/?node=' + encodeURIComponent(nodeName)" class="block w-8 h-8 p-[2px] hover:cursor-pointer"
+ @click.prevent="onLinkIconClick">
+ <link-icon/>
+ </a>
+ <transition name="fadeslow">
+ <div v-if="linkCopied" class="text-sm p-1 ml-2" :style="linkCopyLabelStyles">Link Copied</div>
+ </transition>
+ </div>
<h1 class="text-center text-xl font-bold pt-2 pb-1 mx-10 md:text-2xl md:pt-3 md:pb-1">
{{getDisplayName(nodeName, tolNode)}}
</h1>
@@ -102,6 +111,7 @@ import SCollapsible from './SCollapsible.vue';
import CloseIcon from './icon/CloseIcon.vue';
import ExternalLinkIcon from './icon/ExternalLinkIcon.vue';
import DownIcon from './icon/DownIcon.vue';
+import LinkIcon from './icon/LinkIcon.vue';
import {TolNode, TolMap} from '../tol';
import {LayoutNode, LayoutOptions} from '../layout';
import {getImagePath, DescInfo, ImgInfo, NodeInfo, InfoResponse, UiOptions} from '../lib';
@@ -116,6 +126,11 @@ export default defineComponent({
lytOpts: {type: Object as PropType<LayoutOptions>, required: true},
uiOpts: {type: Object as PropType<UiOptions>, required: true},
},
+ data(){
+ return {
+ linkCopied: false, // Used to temporarily show a 'link copied' label
+ };
+ },
computed: {
tolNode(): TolNode {
return this.infoResponse.nodeInfo.tolNode;
@@ -154,6 +169,13 @@ export default defineComponent({
overflow: 'visible auto',
};
},
+ linkCopyLabelStyles(): Record<string,string> {
+ return {
+ color: this.uiOpts.textColor,
+ backgroundColor: this.uiOpts.bgColor,
+ borderRadius: this.uiOpts.borderRadius + 'px',
+ };
+ },
},
methods: {
getDisplayName(name: string, tolNode: TolNode | null): string {
@@ -215,8 +237,17 @@ export default defineComponent({
this.$emit('close');
}
},
+ onLinkIconClick(evt: Event){
+ // Copy link to clipboard
+ let url = new URL(window.location.href);
+ url.search = 'node=' + encodeURIComponent(this.nodeName);
+ navigator.clipboard.writeText(url.toString());
+ // Show visual indicator
+ this.linkCopied = true;
+ setTimeout(() => {this.linkCopied = false}, 1500);
+ },
},
- components: {SCollapsible, CloseIcon, ExternalLinkIcon, DownIcon, },
+ components: {SCollapsible, CloseIcon, ExternalLinkIcon, DownIcon, LinkIcon, },
emits: ['close', ],
});
</script>
diff --git a/src/components/icon/LinkIcon.vue b/src/components/icon/LinkIcon.vue
new file mode 100644
index 0000000..49996b7
--- /dev/null
+++ b/src/components/icon/LinkIcon.vue
@@ -0,0 +1,12 @@
+<template>
+<svg viewBox="0 0 24 24" fill="none"
+ stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+ <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path>
+ <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path>
+</svg>
+</template>
+
+<script lang="ts">
+import {defineComponent, PropType} from 'vue';
+export default defineComponent({});
+</script>
diff --git a/src/index.css b/src/index.css
index 5987443..7c01490 100644
--- a/src/index.css
+++ b/src/index.css
@@ -23,6 +23,19 @@ a {
transition-duration: 300ms;
transition-timing-function: ease-out;
}
+.fadeslow-enter-from, .fadeslow-leave-to {
+ opacity: 0;
+}
+.fadeslow-enter-active {
+ transition-property: opacity;
+ transition-duration: 300ms;
+ transition-timing-function: ease-out;
+}
+.fadeslow-leave-active {
+ transition-property: opacity;
+ transition-duration: 1000ms;
+ transition-timing-function: linear;
+}
.fadein-leave-to {
opacity: 0;
}