Commit 21a4f3b9 authored by dindigala's avatar dindigala
Browse files

chore: merge branch 'develop' into feat/#318-text-scroll-into-view

parents fa686eb7 9eef247e
Pipeline #222005 passed with stages
in 3 minutes and 9 seconds
...@@ -212,6 +212,8 @@ export default { ...@@ -212,6 +212,8 @@ export default {
this.currentTab = key; this.currentTab = key;
this.highlightActiveContent(this.filteredAnnotations); this.highlightActiveContent(this.filteredAnnotations);
this.handleTooltip();
}, },
addAnnotation(annotation) { addAnnotation(annotation) {
...@@ -239,6 +241,17 @@ export default { ...@@ -239,6 +241,17 @@ export default {
addIcon(element, annotation) { addIcon(element, annotation) {
const contentType = annotation.body['x-content-type']; const contentType = annotation.body['x-content-type'];
let foundSvg = false;
[...element.children].forEach((el) => {
if (el.nodeName === 'svg' && el.getAttribute('data-annotation-icon')) {
foundSvg = true;
}
});
if (foundSvg) {
return;
}
try { try {
const svg = this.createSVG(this.getIconName(contentType)); const svg = this.createSVG(this.getIconName(contentType));
svg.setAttribute( svg.setAttribute(
...@@ -272,16 +285,6 @@ export default { ...@@ -272,16 +285,6 @@ export default {
return svg; return svg;
}, },
onContentUpdate(ids) {
try {
this.currentTab = this.annotationTabs[0].key;
this.contentIds = ids;
this.highlightActiveContent(this.filteredAnnotations);
} catch (err) {
setTimeout(() => this.onContentUpdate(ids), 100);
}
},
getIcon(contentType) { getIcon(contentType) {
return Icons[this.getIconName(contentType)]; return Icons[this.getIconName(contentType)];
}, },
...@@ -292,6 +295,77 @@ export default { ...@@ -292,6 +295,77 @@ export default {
)[0].icon; )[0].icon;
}, },
getTooltipId(el) {
return `${el.className.split(' ').join('')}annotation-tooltip`;
},
handleTooltip() {
const annotationIds = this.filteredAnnotations.reduce((prev, curr) => {
let id = this.stripTargetId(curr, false);
if (id.startsWith('.')) {
id = id.replace('.', '');
}
prev[id] = {
value: curr.body.value,
contentType: curr.body['x-content-type'],
};
return prev;
}, {});
document.querySelectorAll('[data-annotation]').forEach((el) => {
const childOtherNodes = [...el.childNodes].filter((x) => x.nodeName !== '#text').length;
if (!childOtherNodes) {
const classNames = [];
el = this.backTrackNestedAnnotations(el, classNames);
const annotationClasses = [];
// checks for duplicate class names.
classNames
.join(' ')
.split(' ')
.map((x) => annotationIds[x])
.filter((x) => x)
.reduce((prev, curr) => {
if (!prev[curr.value]) {
prev[curr.value] = true;
annotationClasses.push(curr);
}
return prev;
}, {});
if (annotationClasses.length) {
el.addEventListener('mouseenter', () => this.onMouseHover(el, annotationClasses), false);
el.addEventListener('mouseout', () => this.onMouseOut(), false);
}
}
});
},
backTrackNestedAnnotations(el, classNames = []) {
let current = el;
while (current.parentElement.getAttribute('data-annotation') && current.parentElement.childNodes.length === 1) {
classNames.push(current.className);
current = current.parentElement;
}
return el;
},
onContentUpdate(ids) {
try {
this.currentTab = this.annotationTabs[0].key;
this.contentIds = ids;
this.highlightActiveContent(this.filteredAnnotations);
this.handleTooltip();
} catch (err) {
setTimeout(() => this.onContentUpdate(ids), 100);
}
},
onHighlightAll() { onHighlightAll() {
this.filteredAnnotations.forEach( this.filteredAnnotations.forEach(
(annotation) => !this.activeAnnotation[annotation.targetId] (annotation) => !this.activeAnnotation[annotation.targetId]
...@@ -306,6 +380,70 @@ export default { ...@@ -306,6 +380,70 @@ export default {
); );
}, },
onMouseHover(el, annotationClasses) {
const queue = [];
// this logic checks the child spans and classes.
queue.push(el);
let matched = false;
while (queue.length) {
const popped = queue.pop();
if (parseInt(popped.getAttribute('data-annotation-level'), 10) > 0) {
matched = true;
} else {
[...popped.children].forEach((child) => {
queue.push(child);
});
}
}
if (!el || !matched) {
return;
}
const tooltipEl = document.createElement('span');
tooltipEl.setAttribute('data-annotation-classes', `${el.className}`);
tooltipEl.setAttribute('class', 'annotation-tooltip');
const isMultiple = annotationClasses.length > 1;
let annotationLists = '';
annotationClasses.forEach((annotationList) => {
annotationLists += `<div class="referenced-annotation">
${this.createSVG(this.getIconName(annotationList.contentType)).outerHTML}
<span>
${annotationList.value}
</span>
</div>`;
});
const text = `<span class="text-body1">
${!isMultiple ? `${this.$t('toolTip_Reference')}` : `${this.$t('toolTip_References')}`}:
</span>
<br>
<div class="text-body2">
${annotationLists}
</div>`;
tooltipEl.innerHTML = text;
tooltipEl.setAttribute('id', this.getTooltipId(el));
window.top.el = el;
const r = el.getBoundingClientRect();
tooltipEl.style.top = `${r.y + r.height}px`;
tooltipEl.style.left = `${r.x}px`;
document.querySelector('body').append(tooltipEl);
setTimeout(() => tooltipEl.classList.add('annotation-animated-tooltip'), 10);
},
onMouseOut() {
[...document.querySelectorAll('.annotation-tooltip')].forEach((x) => x.remove());
},
removeIcon(annotation) { removeIcon(annotation) {
const stripeId = this.stripTargetId(annotation); const stripeId = this.stripTargetId(annotation);
const el = document const el = document
...@@ -392,6 +530,44 @@ export default { ...@@ -392,6 +530,44 @@ export default {
background-color: $blue-5; background-color: $blue-5;
border-bottom: 1px solid #000; border-bottom: 1px solid #000;
} }
.annotation-tooltip {
background-color: $grey-2 !important;
box-shadow: $shadow-1;
color: #000 !important;
font-size: 14px;
left: 0;
opacity: 0;
padding: 8px;
position: absolute;
text-decoration: none !important;
top: 0;
transition: opacity 0.5s;
width: 240px;
z-index: 10000;
}
.annotation-tooltip {
-webkit-touch-callout: none;
}
.annotation-animated-tooltip {
opacity: 1;
}
.referenced-annotation {
display: block;
margin-bottom: 4px;
}
.referenced-annotation:first-of-type {
padding-top: 4px;
}
.referenced-annotation:last-of-type {
margin-bottom: 0;
}
</style> </style>
<style lang="scss" scoped> <style lang="scss" scoped>
......
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
> >
<a <a
:class="$q.dark.isActive ? 'text-dark' : 'text-white'" :class="$q.dark.isActive ? 'text-dark' : 'text-white'"
:href="`${redirectUrl}?searchTerm=${searchTerm}`" :href="`${redirectUrl}search.html?searchTerm=${searchTerm}`"
class="header-links" class="header-links"
> >
<q-icon <q-icon
......
...@@ -138,7 +138,7 @@ export default { ...@@ -138,7 +138,7 @@ export default {
let dom = parser.parseFromString(data, 'text/html'); let dom = parser.parseFromString(data, 'text/html');
if (!annotationPanelHidden) { if (!annotationPanelHidden) {
const spans = [ const spans = [
...dom.querySelectorAll('span[data-target]:not([value=""])'), ...dom.querySelectorAll('[data-target]:not([value=""])'),
]; ];
const spanIds = [ const spanIds = [
......
...@@ -5,10 +5,11 @@ ...@@ -5,10 +5,11 @@
:class="$q.dark.isActive ? 'bg-white' : 'bg-accent'" :class="$q.dark.isActive ? 'bg-white' : 'bg-accent'"
> >
<BreadCrumbNavigation <BreadCrumbNavigation
v-if="$route.query.source==='external'" v-if="$route.query.source === 'external'"
:config="config" :config="config"
/> />
</div> </div>
<div class="header__wrap"> <div class="header__wrap">
<q-toolbar v-if="config['header_section'].titles"> <q-toolbar v-if="config['header_section'].titles">
<TitleBar <TitleBar
...@@ -30,10 +31,10 @@ ...@@ -30,10 +31,10 @@
<q-toolbar class="q-pb-sm"> <q-toolbar class="q-pb-sm">
<Navbar <Navbar
v-if="config['header_section'].navigation" v-if="config['header_section'].navigation"
:default-view="defaultView"
:itemurls="itemurls" :itemurls="itemurls"
:labels="config.labels" :labels="config.labels"
:manifests="manifests" :manifests="manifests"
:default-view="defaultView"
/> />
<q-space /> <q-space />
...@@ -59,8 +60,8 @@ ...@@ -59,8 +60,8 @@
</template> </template>
<script> <script>
import Navbar from '@/components/navbar.vue';
import BreadCrumbNavigation from '@/components/breadcrumbnavigation.vue'; import BreadCrumbNavigation from '@/components/breadcrumbnavigation.vue';
import Navbar from '@/components/navbar.vue';
import TitleBar from '@/components/titlebar.vue'; import TitleBar from '@/components/titlebar.vue';
import TogglePanels from '@/components/togglebar/togglePanels.vue'; import TogglePanels from '@/components/togglebar/togglePanels.vue';
import Tools from '@/components/tools.vue'; import Tools from '@/components/tools.vue';
...@@ -68,17 +69,13 @@ import Tools from '@/components/tools.vue'; ...@@ -68,17 +69,13 @@ import Tools from '@/components/tools.vue';
export default { export default {
name: 'Header', name: 'Header',
components: { components: {
BreadCrumbNavigation,
Navbar, Navbar,
TitleBar, TitleBar,
TogglePanels, TogglePanels,
BreadCrumbNavigation,
Tools, Tools,
}, },
props: { props: {
defaultView: {
type: Function,
default: () => {},
},
collectiontitle: { collectiontitle: {
type: String, type: String,
default: () => '', default: () => '',
...@@ -87,6 +84,10 @@ export default { ...@@ -87,6 +84,10 @@ export default {
type: Object, type: Object,
default: () => {}, default: () => {},
}, },
defaultView: {
type: Function,
default: () => {},
},
imageurl: { imageurl: {
type: String, type: String,
default: () => '', default: () => '',
......
<template> <template>
<div> <div>
<h1 class="text-h5 text-bold text-uppercase q-mb-none q-mt-xs"> <h1
{{ ahiqar ? $t('cTitle') : collectiontitle }} v-if="collectiontitle"
class="text-h5 text-bold text-uppercase q-mb-none q-mt-xs"
>
{{ collectiontitle }}
</h1> </h1>
<h2 class="text-h6 text-bold text-uppercase q-mt-none q-mb-none"> <h2 class="text-h6 text-bold text-uppercase q-mt-none q-mb-none">
<span>{{ manifesttitle }}</span> <span>{{ manifesttitle }}</span>
<q-icon <q-icon
class="q-pb-xs q-pl-sm q-pr-sm" class="q-pb-xs q-pl-sm q-pr-sm"
size="xs" size="xs"
:color="$q.dark.isActive ? 'white' : 'accent'" :color="$q.dark.isActive ? 'white' : 'accent'"
:name="fasChevronRight" :name="fasChevronRight"
/> />
<span>{{ $t('Sheet') }} {{ item.n }}</span> <span>{{ $t('Sheet') }} {{ item.n }}</span>
</h2> </h2>
</div> </div>
...@@ -37,7 +43,6 @@ export default { ...@@ -37,7 +43,6 @@ export default {
}, },
data() { data() {
return { return {
ahiqar: true,
sequenceindex: 0, sequenceindex: 0,
}; };
}, },
......
...@@ -7,7 +7,6 @@ export default { ...@@ -7,7 +7,6 @@ export default {
colorScheme: 'Farbschema ändern', colorScheme: 'Farbschema ändern',
Contents: 'Inhalt', Contents: 'Inhalt',
contentsMetadata: 'Inhalt & Metadaten', contentsMetadata: 'Inhalt & Metadaten',
cTitle: 'Die Geschichte und Redewendungen von Ahikar dem Weisen',
'Current location': 'Aktueller Standort', 'Current location': 'Aktueller Standort',
'Date of creation': 'Erstelldatum', 'Date of creation': 'Erstelldatum',
Decrease: 'Verkleinern', Decrease: 'Verkleinern',
...@@ -58,6 +57,8 @@ export default { ...@@ -58,6 +57,8 @@ export default {
Title: 'Titel', Title: 'Titel',
title_homepage: 'Die syrischen und arabischen Ahiqar-Texte', title_homepage: 'Die syrischen und arabischen Ahiqar-Texte',
title_viewer: 'Edition anzeigen', title_viewer: 'Edition anzeigen',
toolTip_Reference: 'Referenzierte Annotation',
toolTip_References: 'Referenzierte Annotationen',
transcription: 'Transkription', transcription: 'Transkription',
transliteration: 'Transliteration', transliteration: 'Transliteration',
Year: 'Erstellungsjahr', Year: 'Erstellungsjahr',
......
...@@ -7,7 +7,6 @@ export default { ...@@ -7,7 +7,6 @@ export default {
colorScheme: 'Change color', colorScheme: 'Change color',
Contents: 'Contents', Contents: 'Contents',
contentsMetadata: 'Contents & Metadata', contentsMetadata: 'Contents & Metadata',
cTitle: 'The Story and Proverbs of Ahiqar the Wise',
'Current location': 'Current location', 'Current location': 'Current location',
'Date of creation': 'Date of creation', 'Date of creation': 'Date of creation',
Decrease: 'Decrease', Decrease: 'Decrease',
...@@ -58,6 +57,8 @@ export default { ...@@ -58,6 +57,8 @@ export default {
Title: 'Title', Title: 'Title',
title_homepage: 'The Syriac, Arabic, and Karshuni Ahiqar Texts', title_homepage: 'The Syriac, Arabic, and Karshuni Ahiqar Texts',
title_viewer: 'Edition Viewer', title_viewer: 'Edition Viewer',
toolTip_Reference: 'Referenced Annotation',
toolTip_References: 'Referenced Annotations',
transcription: 'Transcription', transcription: 'Transcription',
transliteration: 'Transliteration', transliteration: 'Transliteration',
Year: 'Year of creation', Year: 'Year of creation',
......
...@@ -3,17 +3,23 @@ export default { ...@@ -3,17 +3,23 @@ export default {
getAllElementsFromSelector(selector, arr = []) { getAllElementsFromSelector(selector, arr = []) {
const el = document.getElementById(selector); const el = document.getElementById(selector);
if (el) { if (el) {
arr.push(el); // https://www.geeksforgeeks.org/queue-data-structure/
const queue = [];
[...el.children].forEach((child) => { queue.push(el);
arr.push(child);
}); while (queue.length) {
const popped = queue.pop();
arr.push(popped);
[...popped.children].forEach((child) => {
queue.push(child);
});
}
return arr; return arr;
} }
return [...document.querySelectorAll(`.${selector}`)]; return [...document.querySelectorAll(`.${selector}`)];
}, },
getElementById(id) { getElementById(id) {
if (!id) { if (!id) {
return null; return null;
...@@ -81,8 +87,8 @@ export default { ...@@ -81,8 +87,8 @@ export default {
}, },
replaceSelectorWithSpan(selector, root) { replaceSelectorWithSpan(selector, root) {
const start = root.querySelector(`span[data-target="${selector}_start"]`); const start = root.querySelector(`[data-target="${selector}_start"]`);
const end = root.querySelector(`span[data-target="${selector}_end"]`); const end = root.querySelector(`[data-target="${selector}_end"]`);
let started = false; let started = false;
let ended = false; let ended = false;
...@@ -184,8 +190,9 @@ export default { ...@@ -184,8 +190,9 @@ export default {
stripSelector(annotation) { stripSelector(annotation) {
return `.${annotation.target.selector.startSelector.value return `.${annotation.target.selector.startSelector.value
.replace("span[data-target='", '') .replace("span[data-target='", '')
.replace("_start']", '') .replace("'", '')
.replace("_end']", '')}`; .replace('_start]', '')
.replace('_end]', '')}`;
}, },
stripTargetId(annotation, removeDot = true) { stripTargetId(annotation, removeDot = true) {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment