Commit 8b8f2dcd authored by schneider210's avatar schneider210
Browse files

Merge branch 'feature/#35-optimize-toggle-linking' into 'develop'

Feature/#35 optimize toggle linking

See merge request subugoe/emo/Qviewer!30
parents 58aa1248 6309d403
Pipeline #145392 passed with stages
in 2 minutes and 41 seconds
......@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.0.23] - 2020-07-20
### Added
- hide image toggle and panel if no imageurl is provided
- panelnames are configurable. togglenames and captions are updated accordingly
### Fixed
- order of toggles corresponds to panels
## [0.0.22] - 2020-07-13
### Added
......
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "viewer",
"version": "0.0.1",
"version": "0.0.22",
"description": "Viewer for the modular framework to present digital editions",
"productName": "EMo Viewer",
"cordovaId": "de.uni-goettingen.sub.emo",
......@@ -10,7 +10,8 @@
"Frank Schneider <frank.schneider@sub.uni-goettingen.de>"
],
"contributors": [
"Nils Windisch"
"Nils Windisch",
"Manikanth Dindigala"
],
"private": true,
"scripts": {
......@@ -19,27 +20,26 @@
"lint": "eslint --ext .js,.vue src"
},
"dependencies": {
"@quasar/extras": "^1.7.0",
"@quasar/extras": "^1.8.2",
"openseadragon": "^2.4.2",
"quasar": "^1.12.4"
"quasar": "^1.12.11"
},
"devDependencies": {
"@quasar/app": "^1.9.6",
"autoprefixer": "^9.7.6",
"autoprefixer": "9.7.6",
"babel-eslint": "^10.0.1",
"eslint": "^6.8.0",
"eslint-config-airbnb-base": "^14.2.0",
"eslint-loader": "^3.0.3",
"eslint-plugin-import": "^2.21.2",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-vue": "^6.1.2",
"node-sass": "^4.14.1",
"sass": "^1.26.8",
"sass-loader": "^8.0.2"
"sass": "^1.26.10",
"sass-loader": "8.0.2"
},
"engines": {
"node": ">= 10.18.1",
"npm": ">= 6.13.4",
"yarn": ">= 1.21.1"
"npm": ">= 6.13.4"
},
"browserslist": [
"last 1 version, not dead, ie >= 11"
......
......@@ -4,10 +4,10 @@
<Header v-if="config.headers.all"
:collectiontitle="collectiontitle"
:config="config"
:imageurl="imageurl"
:itemlabel="itemlabel"
:itemurls="itemurls"
:manifests="manifests"
:status="status"
/>
<q-page-container>
......@@ -21,7 +21,6 @@
:language="itemlanguage"
:manifests="manifests"
:request="request"
:status="status"
:tree="tree"
/>
</q-page-container>
......@@ -49,13 +48,13 @@ export default {
config: {},
fontsize: 14,
imageurl: '',
isCollection: false,
itemlabel: '',
itemlanguage: '',
itemurl: '',
itemurls: [],
label: '',
manifests: [],
status: {},
tree: [],
};
},
......@@ -67,6 +66,8 @@ export default {
return data;
},
getCollection(url) {
this.isCollection = true;
this.request(url)
.then((data) => {
this.collection = data;
......@@ -91,10 +92,6 @@ export default {
},
getConfig() {
this.config = JSON.parse(document.getElementById('emo-config').text);
if (Object.keys(this.config.panels).length) {
this.status = this.config.panels;
}
},
getItemData(url) {
this.request(url)
......@@ -104,7 +101,8 @@ export default {
this.contenturl = data.content;
this.imageurl = data.image && data.image.id ? data.image.id : '';
this.itemlabel = data.n ? data.n : 'No itemlabel :(';
this.itemlanguage = data.language;
// eslint-disable-next-line prefer-destructuring
this.itemlanguage = data['x-langString'].split(',')[0];
});
},
getItemIndex(nodelabel) {
......@@ -148,6 +146,11 @@ export default {
getManifest(url) {
this.request(url)
.then((data) => {
// if the entrypoint points to a single manifest, initialize the tree
if (this.isCollection === false) {
this.tree.push({ label: '', 'label-key': this.config.labels.manifest, children: [] });
}
if (!Array.isArray(data.sequence)) {
data.sequence = [data.sequence];
}
......@@ -204,21 +207,18 @@ export default {
this.itemurls.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
},
mounted() {
this.$root.$on('update-fontsize', (fontsize) => {
this.fontsize = fontsize;
});
this.$root.$on('update-item', (url) => {
this.itemurl = url;
this.$router.push({ query: { itemurl: url } });
// NOTE: Set imageurl to an empty string. Otherwise, if there is no corresponding image,
// the "preceding" image according to the "preceding" itemurl will be shown.
// the "preceding" image according to the "preceding" item will be shown.
this.imageurl = '';
this.getItemData(url);
});
this.$root.$on('update-panel-status', (status) => {
this.status = status;
});
this.$root.$on('change-fontsize', (fontsize) => {
this.fontsize = fontsize;
});
},
};
</script>
......@@ -66,14 +66,14 @@ export default {
let textsize = this.fontsize;
textsize -= textsize > min ? 1 : 0;
this.$root.$emit('change-fontsize', textsize);
this.$root.$emit('update-fontsize', textsize);
},
increase() {
const max = 32;
let textsize = this.fontsize;
textsize += textsize < max ? 1 : 0;
this.$root.$emit('change-fontsize', textsize);
this.$root.$emit('update-fontsize', textsize);
},
getSupport(obj) {
if (obj.type === 'css') {
......
......@@ -15,23 +15,6 @@
{{ captionprev }}
</q-btn>
<!-- <q-input
color="teal"
class="q-mb-md"
dense
min="1"
standout
type="number"
:placeholder="config.labels.item"
>
<template v-slot:append>
<q-icon
:name="fasCheck"
size="20px"
/>
</template>
</q-input> -->
<q-btn
unelevated
class="q-mb-md"
......@@ -61,19 +44,6 @@ export default {
this.fasArrowLeft = fasArrowLeft;
this.fasCheck = fasCheck;
},
methods: {
toggleSheet(itemIndex) {
const link = this.itemurls[itemIndex];
const tree = document.getElementsByClassName('view-tree')[0];
window.location.hash = `selectedItem-${link}`;
tree.scrollBy(0, -80);
this.sequenceindex = this.computedsequenceindex;
this.updateItem(this.itemurls[itemIndex]);
this.updateSequenceIndex(this.sequenceindex);
},
},
};
</script>
......
......@@ -33,7 +33,8 @@
content-sm-center
justify-sm-evenly
row-sm"
:status="status"
:imageurl="imageurl"
:panelstates="config.panels"
/>
</div>
</q-header>
......@@ -54,10 +55,10 @@ export default {
props: {
collectiontitle: String,
config: Object,
imageurl: String,
itemlabel: String,
itemurls: Array,
manifests: Array,
status: Object,
},
};
</script>
......
<template>
<div class="q-pa-md q-gutter-sm">
<!-- <q-input
filled
label="Filter by label ..."
ref="filter"
v-model="filter"
>
<template v-slot:append>
<q-icon v-if="filter !== ''"
class="cursor-pointer"
@click="resetSearch"
:name="fasTimes"
/>
</template>
</q-input>
-->
<q-tree
class="view-tree"
label-key="label-key"
node-key="label"
:expanded.sync="expanded"
:filter="filter"
:filter-method="search"
:nodes="tree"
:selected.sync="selected"
>
......@@ -32,16 +15,11 @@
</div>
</template>
</q-tree>
<!-- ^^ these ones go up here
:filter="filter"
:filter-method="search"
-->
</div>
</template>
<script>
import matIcons from 'quasar/icon-set/material-icons';
// import { fasTimes } from '@quasar/extras/fontawesome-v5';
export default {
name: 'Treeview',
......@@ -52,24 +30,12 @@ export default {
data() {
return {
expanded: [],
// filter: '',
selected: this.manifests[0].sequence[0].id,
sequenceindex: 0,
};
},
// methods: {
// resetSearch() {
// this.filter = '';
// this.$refs.filter.focus();
// },
// search(node, filter) {
// const f = filter.toLowerCase();
// return node.label && node.label.toLowerCase().indexOf(f) > -1;
// },
// },
created() {
this.$q.iconSet.set(matIcons);
// this.fasTimes = fasTimes;
},
mounted() {
this.$root.$on('update-item', (item) => {
......
......@@ -5,14 +5,14 @@
dense
size="md"
class="q-mb-md"
v-for="(name, idx) in panels"
v-for="(name, idx) in togglekeys"
:aria-selected="toggleAria(idx)"
:key="idx"
:title="toggleTitle(idx)"
@click="updateStatus(idx)"
@click="toggleIcon(idx); updateStatus(idx)"
>
<q-icon class="q-pr-xs" size="xs" :name="toggleIcon(idx)" />
{{ name }}
{{ panelstates[name].name }}
</q-btn>
<q-btn
......@@ -40,11 +40,13 @@ import {
export default {
name: 'Togglebar',
props: {
status: Object,
imageurl: String,
panelstates: Object,
},
data() {
return {
panels: [],
togglekeys: [],
states: {},
};
},
filters: {
......@@ -53,28 +55,49 @@ export default {
},
},
methods: {
keyExists(needle = '', haystack = []) {
const index = haystack.indexOf(needle);
return [index > -1, index];
},
resetPanelStatus() {
for (let index = 0; index < this.panels.length; index += 1) {
this.status[this.panels[index]] = true;
// NOTE: just loop over the initial states formerly configured to be shown (e.g. *true*)
// filtered result goes into *togglekeys*.
// leave the initial panel/s configured to be not shown (e.g. *false*) untouched!
for (let idx = 0; idx < this.togglekeys.length; idx += 1) {
this.panelstates[this.togglekeys[idx]].show = true;
}
this.$root.$emit('update-panel-status', this.status);
this.$root.$emit('update-panel-status', this.updateEmitter(this.panelstates));
},
toggleAria(id) {
return !!this.status[this.panels[id]];
return !!this.panelstates[this.togglekeys[id]].show;
},
toggleIcon(id) {
return this.status[this.panels[id]] ? fasCheckCircle : fasCircle;
return this.panelstates[this.togglekeys[id]].show ? fasCheckCircle : fasCircle;
},
toggleTitle(id) {
const caption = this.ucfirst(this.panels[id]);
return this.status[this.panels[id]] ? `Hide ${caption} Tab` : `Show ${caption} Tab`;
const caption = this.ucfirst(this.panelstates[this.togglekeys[id]].name);
return this.panelstates[this.togglekeys[id]].show
? `Hide ${caption} Tab`
: `Show ${caption} Tab`;
},
ucfirst(s) {
return s.charAt(0).toUpperCase() + s.slice(1);
},
updateEmitter(status) {
Object.entries(status).forEach(([panel, state]) => {
this.states[panel] = state.show;
});
return this.states;
},
updateStatus(id) {
this.status[this.panels[id]] = !this.status[this.panels[id]];
this.$root.$emit('update-panel-status', this.status);
// NOTE: leave the initial panelstates untouched! Configured by the project only!
// original panelstates needed in resetPanelStatus() to look up the initial states,
// which otherwise would be incidentally overwritten; hence: => *statecopy*
const statecopy = this.panelstates;
statecopy[this.togglekeys[id]].show = !statecopy[this.togglekeys[id]].show;
this.$root.$emit('update-panel-status', this.updateEmitter(statecopy));
},
},
created() {
......@@ -83,9 +106,24 @@ export default {
this.fasCheckCircle = fasCheckCircle;
// just show the toggle buttons needed according to the config
Object.entries(this.status).forEach(([panel, state]) => {
if (state === true) {
this.panels.push(panel);
Object.entries(this.panelstates).forEach(([panel, state]) => {
if (state.show === true) {
this.togglekeys.push(panel);
}
});
},
mounted() {
this.resetPanelStatus();
this.$root.$on('update-item', () => {
if (this.panelstates.image.show) {
const [haskey, index] = this.keyExists('image', this.togglekeys);
// check, if image key hasn't been deleted yet
if (haskey && !this.imageurl) {
this.togglekeys.splice(index, 1);
} else if (!haskey && this.imageurl) {
this.togglekeys.push('image');
}
}
});
},
......
......@@ -10,5 +10,11 @@
position: sticky;
top: 0;
width: 100%;
z-index: 999999;
}
}
.openseadragon-canvas {
&:focus {
outline: none;
}
}
......@@ -36,10 +36,26 @@
"manifest": "Manuscript"
},
"panels": {
"image": true,
"text": true,
"metadata": true,
"treeview": true
"tree": {
"name": "Contents",
"position": 1,
"show": true
},
"text": {
"name": "Text",
"position": 2,
"show": true
},
"image": {
"name": "Image",
"position": 3,
"show": true
},
"metadata": {
"name": "Metadata",
"position": 4,
"show": true
}
},
"standalone": true
}
......
......@@ -11,6 +11,17 @@ export default {
};
},
methods: {
toggleSheet(itemIndex) {
const link = this.itemurls[itemIndex];
const tree = document.getElementsByClassName('view-tree')[0];
window.location.hash = `selectedItem-${link}`;
tree.scrollBy(0, -80);
this.sequenceindex = this.computedsequenceindex;
this.updateItem(this.itemurls[itemIndex]);
this.updateSequenceIndex(this.sequenceindex);
},
updateItem() {
this.$root.$emit('update-item', this.itemurls[this.itemindex]);
},
......
......@@ -2,8 +2,8 @@
<q-page>
<q-splitter v-model="splitterone" :limits="[0, 100]">
<template v-show="panels.treeview" v-slot:before>
<Toolbar heading="Treeview" />
<template v-show="config.panels.tree.show && states.tree" v-slot:before>
<Toolbar :heading="config.panels.tree.name" />
<q-separator />
<Treeview v-if="tree.length && manifests.length"
......@@ -16,8 +16,8 @@
<template v-slot:after>
<q-splitter v-model="splittertwo" :limits="[0, 100]">
<template v-show="panels.text" v-slot:before>
<Toolbar heading="Text" />
<template v-show="config.panels.text.show && states.text" v-slot:before>
<Toolbar :heading="config.panels.text.name" />
<q-separator />
<div class="q-pa-md q-gutter-sm">
......@@ -40,8 +40,8 @@
<template v-slot:after>
<q-splitter v-model="splitterthree" :limits="[0, 100]">
<template v-show="panels.image && imageurl" v-slot:before>
<Toolbar heading="Image" />
<template v-show="config.panels.image.show && states.image && imageurl" v-slot:before>
<Toolbar :heading="config.panels.image.name" />
<q-separator />
<div class="q-pa-md q-gutter-sm" style="overflow:hidden">
......@@ -55,8 +55,8 @@
</div>
</template>
<template v-show="panels.metadata" v-slot:after>
<Toolbar heading="Metadata" />
<template v-show="config.panels.metadata.show && states.metadata" v-slot:after>
<Toolbar :heading="config.panels.metadata.name" />
<q-separator />
<div class="scrollPanel">
......@@ -106,34 +106,33 @@ export default {
language: String,
manifests: Array,
request: Function,
status: Object,
tree: Array,
},
data() {
return {
// status: image, text, metadata, treeview
// status: tree, text, image, metadata
matrix: [
{ state: [1, 1, 1, 1], ratio: [25, 33, 50] },
{ state: [0, 0, 0, 0], ratio: [0, 0, 0] },
{ state: [1, 1, 1, 0], ratio: [0, 33, 50] },
{ state: [1, 1, 0, 1], ratio: [33, 50, 100] },
{ state: [1, 1, 1, 0], ratio: [33, 50, 100] },
{ state: [1, 1, 0, 1], ratio: [33, 50, 0] },
{ state: [1, 0, 1, 1], ratio: [33, 0, 50] },
{ state: [1, 0, 1, 0], ratio: [0, 0, 50] },
{ state: [1, 0, 0, 1], ratio: [50, 0, 100] },
{ state: [1, 1, 0, 0], ratio: [0, 50, 100] },
{ state: [1, 0, 0, 0], ratio: [0, 0, 100] },
{ state: [0, 1, 1, 1], ratio: [33, 50, 0] },
{ state: [0, 1, 1, 0], ratio: [0, 50, 0] },
{ state: [0, 1, 0, 1], ratio: [50, 100, 0] },
{ state: [1, 0, 1, 0], ratio: [33, 0, 100] },
{ state: [1, 0, 0, 1], ratio: [50, 0, 0] },
{ state: [1, 1, 0, 0], ratio: [50, 100, 0] },
{ state: [1, 0, 0, 0], ratio: [100, 0, 0] },
{ state: [0, 1, 1, 1], ratio: [0, 33, 50] },
{ state: [0, 1, 1, 0], ratio: [0, 50, 100] },
{ state: [0, 1, 0, 1], ratio: [0, 50, 0] },
{ state: [0, 1, 0, 0], ratio: [0, 100, 0] },
{ state: [0, 0, 1, 1], ratio: [50, 0, 0] },
{ state: [0, 0, 1, 0], ratio: [0, 0, 0.1] },
{ state: [0, 0, 0, 1], ratio: [100, 0, 0] },
{ state: [0, 0, 1, 1], ratio: [0, 0, 50] },
{ state: [0, 0, 1, 0], ratio: [0, 0, 100] },
{ state: [0, 0, 0, 1], ratio: [0, 0, 0] },
],
panels: {},
splitterone: 25,
splittertwo: 33,
splitterthree: 50,
states: {},
};
},
methods: {
......@@ -146,27 +145,37 @@ export default {
return activePanels;
},
// hide image panel if the actual item doesn't contain an imageurl