Skip to content

Commit

Permalink
Feat/iiif viewer (#16)
Browse files Browse the repository at this point in the history
integrate the mirador iiif-viewer, so images in high resolution can be
displayed & their annotations.

---------

Co-authored-by: Mocca101 <[email protected]>
  • Loading branch information
oliviareichl and Mocca101 authored Jun 3, 2024
1 parent d896d38 commit f42085d
Show file tree
Hide file tree
Showing 10 changed files with 1,469 additions and 19 deletions.
80 changes: 63 additions & 17 deletions components/entity-images.vue
Original file line number Diff line number Diff line change
@@ -1,24 +1,70 @@
<script lang="ts" setup>
import type { CarouselApi } from "@/components/ui/carousel";
import { Toggle } from "@/components/ui/toggle";
const props = defineProps<{
images: Array<{ license: string; url: string }>;
images: Array<{
IIIFManifest: string | undefined;
license: string | undefined;
mimetype?: string | undefined;
title?: string | undefined;
url?: string | undefined;
}>;
}>();
const api = ref<CarouselApi>();
const current = ref(0);
const t = useTranslations();
let show = ref(false);
function toggleIIIF() {
show.value = !show.value;
}
function setApi(val: CarouselApi) {
api.value = val;
api.value.scrollTo(current.value, true);
}
watch(api, (api) => {
if (!api) return;
current.value = api.selectedScrollSnap();
api.on("select", () => {
current.value = api.selectedScrollSnap();
});
});
const currentImage = computed(() => {
return props.images[current.value];
});
onMounted(() => {});
</script>

<template>
<Carousel class="mx-14">
<CarouselPrevious v-if="props.images.length > 2" />
<CarouselContent>
<CarouselItem v-for="(image, index) of props.images" :key="index" class="h-full">
<Card class="pb-3">
<figure class="grid h-96 grid-rows-[1fr_auto] gap-y-1.5 overflow-hidden">
<div class="relative">
<img alt="" class="absolute size-full object-contain" :src="image.url" />
</div>
<figcaption class="justify-self-center">{{ image.license }}</figcaption>
</figure>
</Card>
</CarouselItem>
</CarouselContent>
<CarouselNext v-if="props.images.length > 2" />
</Carousel>
<div class="relative">
<Carousel v-if="!show" @init-api="setApi">
<CarouselPrevious v-if="props.images.length > 2" />
<CarouselContent>
<CarouselItem v-for="(image, index) of props.images" :key="index" class="h-full">
<Card class="pb-3">
<figure class="grid h-96 grid-rows-[1fr_auto] gap-y-1.5 overflow-hidden">
<div class="relative">
<img alt="" class="absolute size-full object-contain" :src="image.url" />
</div>
<figcaption class="justify-self-center">{{ image.license }}</figcaption>
</figure>
</Card>
</CarouselItem>
</CarouselContent>
<CarouselNext v-if="props.images.length > 2" />
</Carousel>
<EntityMiradorViewer v-if="show && currentImage" :images="[currentImage]" />
<Toggle variant="iiif" class="absolute bottom-0 right-0 z-10 m-4" @click="toggleIIIF">
{{ t("EntityPage.iiif") }}
</Toggle>
</div>
</template>
30 changes: 30 additions & 0 deletions components/entity-mirador-viewer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<script lang="ts" setup>
import { miradorConfig } from "@/config/mirador.config";
const props = defineProps<{
images: Array<{
IIIFManifest: string | undefined;
license: string | undefined;
mimetype?: string | undefined;
title?: string | undefined;
url?: string | undefined;
}>;
}>();
const imageUrls: Array<string> = [];
props.images.forEach((url) => {
if (url.IIIFManifest != null) imageUrls.push(url.IIIFManifest);
});
console.log(imageUrls);
</script>

<template>
<Card class="h-96 overflow-hidden">
<VisualisationContainer v-slot="{ height, width }">
<MiradorViewer v-if="height && width" :config="miradorConfig" :images="imageUrls" />
</VisualisationContainer>
</Card>
</template>
45 changes: 45 additions & 0 deletions components/mirador-viewer.client.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<script lang="ts" setup>
import Mirador from "mirador";
import { defineProps, onMounted } from "vue";
// Props
const props = defineProps({
config: {
type: Object,
required: true,
},
images: Array<string>,
});
// Refs
// TODO: At the moment I assume there is a race condition where the div element isn't available yet,
// at time of mounting.
// Methods
const initMirador = () => {
Mirador.viewer({
id: mirador,
...props.config,
windows: props.images?.map((url) => {
return { manifestId: url };
}),
});
};
const delayedInitMirador = () => {
setTimeout(initMirador, 100);
};
const mirador = "mirador";
// Lifecycle
onMounted(() => {
// TODO: We shouldn't rely on timeouts here, and find a more reliable solution
delayedInitMirador();
});
</script>

<template>
<div :id="mirador" class="relative"></div>
</template>
35 changes: 35 additions & 0 deletions components/ui/toggle/Toggle.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { Toggle, type ToggleEmits, type ToggleProps, useForwardPropsEmits } from 'radix-vue'
import { type ToggleVariants, toggleVariants } from '.'
import { cn } from '@/utils/styles'
const props = withDefaults(defineProps<ToggleProps & {
class?: HTMLAttributes['class']
variant?: ToggleVariants['variant']
size?: ToggleVariants['size']
}>(), {
variant: 'default',
size: 'default',
disabled: false,
})
const emits = defineEmits<ToggleEmits>()
const delegatedProps = computed(() => {
const { class: _, size, variant, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>

<template>
<Toggle
v-bind="forwarded"
:class="cn(toggleVariants({ variant, size }), props.class)"
>
<slot />
</Toggle>
</template>
28 changes: 28 additions & 0 deletions components/ui/toggle/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { cva, type VariantProps } from "class-variance-authority";

export { default as Toggle } from "./Toggle.vue";

export const toggleVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-primary data-[state=on]:text-primary-foreground dark:bg-white dark:text-black dark:hover:bg-black dark:hover:text-white dark:data-[state=on]:bg-black dark:data-[state=on]:text-white",
{
variants: {
variant: {
default: "bg-transparent",
outline:
"border border-input bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground",
iiif: "bg-muted text-primary shadow hover:bg-primary hover:text-primary-foreground",
},
size: {
default: "h-9 px-3",
sm: "h-8 px-2",
lg: "h-10 px-3",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
);

export type ToggleVariants = VariantProps<typeof toggleVariants>;
30 changes: 30 additions & 0 deletions config/mirador.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Configuration for Mirador
export const miradorConfig = ref({
layout: "1x1",
mainMenuSettings: {
show: false,
},
workspaceControlPanel: {
enabled: false,
},
window: {
allowClose: false,
defaultSideBarPanel: "attribution",
sideBarOpenByDefault: false,
forceDrawAnnotations: true,
highlightAllAnnotations: true,
},
// workspace: {
// type: "mosaic"
// },
language: "en",
availableLanguages: {
de: "Deutsch",
en: "English",
},
windows: [
{
thumbnailNavigationPosition: "off",
},
],
});
1 change: 1 addition & 0 deletions messages/de/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
},
"EntityPage": {
"details": "Details",
"iiif": "IIIF-Ansicht",
"images": "Bilder ({count})",
"map": "Karte",
"network": "Netzwerk-Visualisierung",
Expand Down
1 change: 1 addition & 0 deletions messages/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
},
"EntityPage": {
"details": "Details",
"iiif": "IIIF-Viewer",
"images": "Images ({count})",
"map": "Map",
"network": "Network Visualization",
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"@vee-validate/zod": "^4.12.6",
"@vueuse/core": "^10.9.0",
"@vueuse/nuxt": "^10.9.0",
"class-variance-authority": "^0.7.0",
"colorjs.io": "^0.5.0",
"cva": "^1.0.0-beta.1",
"dotenv": "^16.4.5",
Expand All @@ -71,13 +72,15 @@
"is-ci": "^3.0.1",
"lucide-vue-next": "^0.358.0",
"maplibre-gl": "^4.1.1",
"mirador": "4.0.0-alpha.2",
"npm-run-all2": "^6.1.2",
"nuxt": "^3.11.0",
"openapi-typescript": "^7.0.0-next.8",
"pino-http": "^9.0.0",
"radix-vue": "^1.5.2",
"sigma": "3.0.0-beta.17",
"tailwind-merge": "^2.2.2",
"tify": "^0.30.2",
"tsx": "^4.7.1",
"vee-validate": "^4.12.6",
"vue": "^3.4.21",
Expand Down
Loading

0 comments on commit f42085d

Please sign in to comment.