Skip to content

Commit

Permalink
Merge pull request #36 from zazuko/share_feature
Browse files Browse the repository at this point in the history
add share feature
  • Loading branch information
BenjaminHofstetter authored Dec 3, 2024
2 parents bfc0f3b + 333d58d commit c7cff87
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 14 deletions.
23 changes: 18 additions & 5 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ import type { Dataset, Term } from '@rdfjs/types';
import { prefixMap } from './rdf/prefix-map';
import SPOSearch from './components/SPOSearch.vue';
import ShareButton from './components/share-button/ShareButton.vue';
import { useVueFlow } from '@vue-flow/core';
const { fitView, nodeLookup} = useVueFlow()
const selectedFormat = ref<RdfFormat>(rdfFormats.find(f => f.type === RdfSerializationType.Turtle) ?? rdfFormats[0]);
const rdfFormatOptions = ref<RdfFormat[]>(rdfFormats);
const rdfText = ref<string>('');
const currentSerialization = computed(() => selectedFormat.value.type);
const dataset = ref<Dataset>(rdfEnvironment.dataset() as unknown as Dataset);
const hideEditorSplitterPanel = ref(false);
Expand All @@ -33,6 +36,7 @@ const showAboutDialog = ref(false);
function onQuadsChanged(rdfData: RdfData) {
const newDataset = rdfEnvironment.dataset(rdfData.quads) as unknown as Dataset;
prefixMap.update(rdfData.prefix);
rdfText.value = rdfData.rdfText;
dataset.value = newDataset;
}
function makeEditorSmall() {
Expand All @@ -52,8 +56,6 @@ function onFormatChange(rdfSerializationType: RdfSerializationType) {
}
const env = rdfEnvironment;
function onNdeSelected(term: Term) {
if (!(term.termType === 'NamedNode' || term.termType === 'BlankNode')) {
return
Expand All @@ -70,6 +72,7 @@ function onNdeSelected(term: Term) {
}
</script>

<template>
Expand All @@ -79,31 +82,38 @@ function onNdeSelected(term: Term) {
<Button v-if="!hideEditorSplitterPanel" icon="pi pi-file-edit" class="mr-2" severity="secondary" @click="makeEditorSmall" text ></Button>
<Button v-if="hideEditorSplitterPanel" icon="pi pi-file-edit" class="mr-2" severity="secondary" @click="makeEditorBig" text ></Button>
<Select v-model="selectedFormat" :options="rdfFormatOptions" optionLabel="name" placeholder="Select RDF Serialization" checkmark :highlightOnSelect="false"></Select>

</template>

<template #center>
<span>Sketch</span>
</template>

<template #end>

<Button icon="pi pi-search" class="mr-2" severity="secondary" @click="toggleSearch" text></Button>
<ShareButton :format="currentSerialization" :rdf-text="rdfText"></ShareButton>
<Button icon="pi pi-lightbulb" class="mr-2" severity="secondary" @click="showAboutDialog = !showAboutDialog" text></Button>
<Button as="a" icon="pi pi-github" class="mr-2" severity="secondary" href="https://github.com/zazuko/rdf-sketch"
target="_blank"></Button>
target="_blank" text></Button>


</template>
</Toolbar>

<Splitter style="height: calc(100vh - (67.5px + ( 2 * 8px) + 8px) ); margin-top: 8px" class="mb-8">

<SplitterPanel :style="{ display: hideEditorSplitterPanel ? 'none' : 'flex' }" class="flex items-center justify-center">
<RdfEditor :format="currentSerialization" @change="onQuadsChanged" @format-change="onFormatChange"/>
</SplitterPanel>

<SplitterPanel class="flex items-center justify-center">
<GraphView :dataset="dataset" :env="env" />
<GraphView :dataset="dataset" />
</SplitterPanel>

<SplitterPanel v-if="!hideSearchPanel">
<SPOSearch :dataset="dataset" @selected="onNdeSelected"/>
</SplitterPanel>

</Splitter>


Expand Down Expand Up @@ -135,3 +145,6 @@ Sketch is a simple yet powerful tool for visualizing RDF graphs. It allows you t

</template>




9 changes: 4 additions & 5 deletions src/components/GraphView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<FloatingEdge v-bind="customEdgeProps" />
</template>
</VueFlow>

</template>

<script setup lang="ts">
Expand All @@ -21,7 +22,7 @@ import type { Dataset } from '@rdfjs/types';
import { linksFromResources, resourcesFromDataset } from '../resources-utils';
import FloatingEdge from './graph/floating-edge/FloatingEdge.vue';
import { VueFlow, useVueFlow, type Node, type Edge, type NodeDragEvent, MarkerType} from '@vue-flow/core';
import { VueFlow, useVueFlow, type Node, type Edge, type NodeDragEvent, MarkerType, type EdgeMouseEvent} from '@vue-flow/core';
import { useLayout } from '../layout/use-layout';
import ResourceNode from './graph/resource-node/ResourceNode.vue'
Expand All @@ -34,7 +35,6 @@ export type CustomEdge = Edge<any, any, CustomEdgeTypes>
interface GraphViewProps {
dataset: Dataset,
env: any
}
const props = defineProps<GraphViewProps>()
Expand All @@ -60,8 +60,7 @@ watch(resources, async (newResources) => {
type: 'custom',
position: { x: 0, y: 0 },
data: {
resource,
env: props.env,
resource
},
}));
Expand Down Expand Up @@ -153,7 +152,7 @@ function onNodeDrag(nodeDragEvent: NodeDragEvent) {
edges.value = [...edges.value];
}
function zoomToNode(e: any) {
function zoomToNode(e: EdgeMouseEvent) {
fitView({
nodes: [e.edge.sourceNode.id],
duration: 1000, // use this if you want a smooth transition to the node
Expand Down
104 changes: 104 additions & 0 deletions src/components/share-button/ShareButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<template>
<div class="card flex justify-center">
<Button icon="pi pi-share-alt" @click="toggle" severity="secondary" text ></Button>
<Popover ref="popoverElement">
<div class="content">
<div class="group">
<div style="display: flex; flex-direction: row; gap:1">
<div class="limit">{{ shareUrl }}</div>
<div v-if="!shareUrl.startsWith('https://s.zazuko.com/')" >...</div>
</div>


<Button icon="pi pi-clipboard" @click="copyURL(shareUrl)" variant="text" rounded aria-label="Copy to Clipboard" />

</div>

<Button :disabled="shareUrl.startsWith('https://s.zazuko.com/')" type="button" severity="secondary" @click="useLinkShortener(shareUrl)" label="Shorten" />
</div>
</Popover>
</div>
</template>

<script setup lang="ts">
import { ref } from "vue";
import Button from 'primevue/button';
import Popover from 'primevue/popover';
import type { RdfSerializationType } from "@/constant/rdf-format";
interface ShareButtonPropsProps {
format: RdfSerializationType;
rdfText: string;
}
const props = defineProps<ShareButtonPropsProps>()
const popoverElement = ref<InstanceType<typeof Popover> | null>(null);
const appUrl = new URL(window.location.href.split('#')[0]);
const shareUrl = ref<string>('');
async function toggle(event: MouseEvent): Promise<void> {
if(popoverElement.value) {
shareUrl.value = `${appUrl}#rdf=${encodeURIComponent(props.rdfText)}&format=${props.format}`;
if(shareUrl.value.length > 2000) {
useLinkShortener(shareUrl.value);
}
popoverElement.value.toggle(event);
}
}
async function useLinkShortener(url: string): Promise<void> {
const body = new URLSearchParams({ url })
const response = await fetch('https://s.zazuko.com/api/v1/shorten/', {
method: 'POST',
body
})
const res = await response
if (res.status<200 || res.status >= 300) {
console.error('shortener error', res.status)
} else {
shareUrl.value = await response.text()
}
}
function copyURL(url: string): void {
navigator.clipboard.writeText(url).then(() => {
if(popoverElement.value) {
popoverElement.value.hide();
}
}).catch((err) => {
console.error('copy failed', err);
if(popoverElement.value) {
popoverElement.value.hide();
}
});
}
</script>

<style scoped>
.limit {
display: inline-block;
white-space: nowrap;
width: 253px;
overflow: hidden;
}
.content {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 1rem;
}
.group {
display: flex;
flex-direction: row;
gap: 1rem;
align-items: center;
}
</style>
31 changes: 27 additions & 4 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import './assets/main.css'

import { createApp } from 'vue'
import { createApp } from 'vue';

import PrimeVue from 'primevue/config'
import PrimeVue from 'primevue/config';

import Aura from '@primevue/themes/aura'
import App from './App.vue'
import Aura from '@primevue/themes/aura';
import App from './App.vue';

import { useUrlSearchParams } from '@vueuse/core';
import { rdfFormats, RdfSerializationType } from './constant/rdf-format';
import { localStorageKeyFormat, localStorageKeyText } from './constant/local-storage-keys';

const params = useUrlSearchParams('hash-params')

const app = createApp(App);

Expand All @@ -19,5 +25,22 @@ app.use(PrimeVue, {
}
});

writeRdfFromUrlToLocalStorage();

app.mount('#app')


/**
* This is used for "Share" functionality.
* It reads the RDF and format from the URL and writes it to the local storage.
*/
function writeRdfFromUrlToLocalStorage(): void {
if (params.rdf) {
const rdfText = (Array.isArray(params.rdf) ? params.rdf[0] : params.rdf) ?? '';
const rdfFormat = ((Array.isArray(params.format) ? params.format[0] : params.format) ?? RdfSerializationType.Turtle).replace(' ', '+');
if (rdfText.length > 0 && rdfFormats.find(f => f.type === rdfFormat)) {
localStorage.setItem(localStorageKeyText, rdfText);
localStorage.setItem(localStorageKeyFormat, rdfFormat);
}
}
}

0 comments on commit c7cff87

Please sign in to comment.