Skip to content

Commit

Permalink
Merge pull request #131 from RDFLib/feature/internal-links
Browse files Browse the repository at this point in the history
Objects that have prez:links now render as internal RouterLinks
  • Loading branch information
jamiefeiss authored Jan 25, 2024
2 parents ef1bfea + 8699de1 commit b0d59e9
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 13 deletions.
98 changes: 98 additions & 0 deletions src/components/InternalLink.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<script lang="ts" setup>
import { computed } from "vue";
import { RouterLink } from "vue-router";
import type { PropTableObject } from "@/types";
import ToolTip from "@/components/ToolTip.vue";
import Popover from "@/components/Popover.vue";
const props = defineProps<PropTableObject>();
// dumb hack to guarantee vocab link is first if exists
const links = computed(() => {
return props.links?.reverse();
});
</script>

<template>
<div class="link">
<component :is="!!props.description ? ToolTip : 'slot'">
<RouterLink v-if="links && links.length === 1" :to="links[0]">
<template v-if="!!props.label">{{ props.label }}</template>
<template v-else-if="!!props.qname">{{ props.qname }}</template>
<template v-else>{{ props.value }}</template>
</RouterLink>
<span v-else-if="links && links.length > 1">
<template v-if="!!props.label">{{ props.label }}</template>
<template v-else-if="!!props.qname">{{ props.qname }}</template>
<template v-else>{{ props.value }}</template>
</span>
<a
v-else
:href="props.value"
target="_blank"
rel="noopener noreferrer"
>
<template v-if="!!props.label">{{ props.label }}</template>
<template v-else-if="!!props.qname">{{ props.qname }}</template>
<template v-else>{{ props.value }}</template>
</a>
<template v-if="!!props.description" #text>{{ props.description }}</template>
</component>
<Popover v-if="links && links.length > 1" class="links-btn" title="Internal links">
<i class="fa-solid fa-link"></i>
<template #content>
<RouterLink v-for="link in links" :to="link" class="link-item">{{ link }}</RouterLink>
</template>
</Popover>
<!-- <span v-if="links && links.length > 1" class="multi-links">
<template v-for="(link, index) in links.slice(1)">
<template v-if="index === 0">(</template>
<RouterLink :to="link" class="btn sm outline alt-internal-link"><i class="fa-solid fa-link" title="Alternative internal link"></i></RouterLink>
<template v-if="index < links.length - 2">,&nbsp;</template>
<template v-if="index === links.length - 2">)</template>
</template>
</span> -->
<a
v-if="links"
:href="props.value"
target="_blank"
rel="noopener noreferrer"
class="btn sm outline ext-link"
>
<i class="fa-solid fa-arrow-up-right-from-square" title="External link"></i>
</a>
</div>
</template>

<style lang="scss" scoped>
.link {
.multi-links {
margin: 0px 4px;
.alt-internal-link {
padding: 4px;
}
}
i {
font-size: 0.8rem;
}
.links-btn {
margin-left: 4px;
}
.link-item {
padding: 6px;
transition: background-color 0.2s ease-in-out;
&:hover {
background-color: #f3f3f3;
}
}
.ext-link {
margin-left: 4px;
}
}
</style>
54 changes: 54 additions & 0 deletions src/components/Popover.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<script lang="ts" setup>
import { ref, onMounted, onBeforeUnmount } from "vue";
const open = ref(false);
const popoverRef = ref<HTMLElement | null>(null);
function handleDocumentClick(e: MouseEvent) {
if (open.value && popoverRef.value && !popoverRef.value.contains(e.target as Node)) {
open.value = false;
}
}
onMounted(() => {
document.body.addEventListener("click", handleDocumentClick);
});
onBeforeUnmount(() => {
document.body.removeEventListener("click", handleDocumentClick);
});
</script>

<template>
<span class="popover" ref="popoverRef">
<button class="popover-btn btn outline sm" @click="open = !open">
<slot></slot>
<i :class="`fa-solid fa-chevron-${open ? 'up' : 'down'}`"></i>
</button>
<span v-show="open" class="popover-content">
<slot name="content"></slot>
</span>
</span>
</template>

<style lang="scss" scoped>
@import "@/assets/sass/_mixins.scss";
.popover {
position: relative;
display: inline-block;
.popover-content {
box-shadow: 0px 0px 5px 2px rgba(0, 0, 0, 0.10);
font-size: 14px;
padding: 6px;
background-color: white;
border-radius: 6px;
z-index: 999;
position: absolute;
top: 100%;
display: flex;
flex-direction: column;
}
}
</style>
15 changes: 2 additions & 13 deletions src/components/proptable/ObjectCell.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import type { PropTableObject } from "@/types";
import { copyToClipboard } from "@/util/helpers";
import PropRow from "@/components/proptable/PropRow.vue";
import ToolTip from "@/components/ToolTip.vue";
import InternalLink from "@/components/InternalLink.vue";
const props = defineProps<PropTableObject>();
Expand All @@ -20,18 +20,7 @@ const MAX_GEOM_LENGTH = 100; // max character length for geometry strings
<table v-if="props.termType === 'BlankNode'">
<PropRow v-for="row in props.rows" v-bind="row" />
</table>
<component v-else-if="props.termType === 'NamedNode'" :is="!!props.description ? ToolTip : 'slot'">
<a
:href="props.value"
target="_blank"
rel="noopener noreferrer"
>
<template v-if="!!props.label">{{ props.label }}</template>
<template v-else-if="!!props.qname">{{ props.qname }}</template>
<template v-else>{{ props.value }}</template>
</a>
<template #text>{{ props.description }}</template>
</component>
<InternalLink v-else-if="props.termType === 'NamedNode'" v-bind="props" />
<template v-else>
<template v-if="props.predicateIri === 'https://schema.org/color'">{{ props.value }}<span v-if="!!props.value" :style="{color: props.value, marginLeft: '4px'}" class="fa-solid fa-circle fa-2xs"></span></template>
<template v-else-if="props.value.startsWith('http')">
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ export interface AnnotatedTerm {
label?: string;
description?: string;
provenance?: string;
links?: string[];
};

export interface AnnotatedPredicate extends Omit<AnnotatedTerm, "language" | "datatype"> {
Expand Down
3 changes: 3 additions & 0 deletions src/util/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,9 @@ export function createAnnotatedTerm<T extends AnnotatedTerm>(term: Quad_Predicat
annoTerm.label = getLabel(term.value, store);
annoTerm.description = getDescription(term.value, store);
annoTerm.provenance = getProvenance(term.value, store);

const links = store.getObjects(namedNode(term.value), namedNode(defaultQnameToIri("prez:link")), null);
annoTerm.links = links.length > 0 ? links.map(l => l.value) : undefined;
} else if (term.termType === "Literal") {
annoTerm.language = term.language;
annoTerm.datatype = {
Expand Down

0 comments on commit b0d59e9

Please sign in to comment.