Commit 26cfc40d authored by dindigala's avatar dindigala
Browse files

Merge branch 'feature/#54-Userconfigurable' into 'develop'

Feature/#54 User Configurable

See merge request subugoe/emo/Qviewer!48
parents f321804d 7c85392c
Pipeline #150363 passed with stages
in 2 minutes and 47 seconds
......@@ -5,6 +5,13 @@ 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).
## [1.4.0] - 2020-09-11
### Added
- Refactored drag and drop component in order to make it user configurable.
- Moved panel data structure into a separate component where panels can be configured.
## [1.3.3] - 2020-09-10
### Changed
......
......@@ -158,8 +158,6 @@ See [Configuring quasar.conf.js](https://quasar.dev/quasar-cli/quasar-conf-js).
## Configure the Viewer
As a rule of thumb, every key with a boolean value (e.g. *true* or *false*) defaults to `true` and denotes to show the appropriate component. If you intend to hide a component, just toggle it's corresponding key-value to `false`.
Locate the `index.template.html` file inside the root of your project dir and find the script section:
**Note**:
......@@ -167,7 +165,7 @@ Locate the `index.template.html` file inside the root of your project dir and fi
It's a json object. So if you are going to make any changes and you have to quote these, use double quotes but single ones.
```html
<script id="emo-config" type="application/json">
<script id="emo-config" type="application/json">
{
"entrypoint": "https://{server}{/prefix}/{collection}/collection.json",
"headers": {
......@@ -182,65 +180,48 @@ It's a json object. So if you are going to make any changes and you have to quot
},
"standalone": true
}
</script>
</script>
```
The data structure of the panels are inside app.vue component. You can work on this data structure to make any required changes.
## Configure Panels
In order to configure the panels, locate the `panels.js` file inside the `src/components/config` of your project dir and find the panels section:
**Note**:
As a rule of thumb, every key with a boolean value (e.g. *true* or *false*) defaults to `true` and denotes to show the appropriate component. If you intend to hide a component, just toggle it's corresponding key-value to `false`.
It's an array structure. So if you are going to make any changes and you have to quote these, use double quotes but single ones.
```html
data() {
return {
.
.
.
panels: [
{
component: null,
name: 'tabs',
show: true,
tabs: {
children: [
{
component: Treeviewtab,
label: 'Contents',
name: 'content',
},
{
component: Metadatatab,
label: 'Metadata',
name: 'meta',
},
],
model: 'content',
},
toolbar: 'Tabs',
},
{
component: OpenSeadragon,
name: 'image',
show: true,
tabs: [],
toolbar: 'Image',
},
{
component: Content,
name: 'text',
show: true,
tabs: [],
toolbar: 'Content',
},
{
component: null,
name: 'annotations',
show: true,
tabs: [],
toolbar: 'Annotations',
},
],
}
}
const panels = [
{
id: uuidv4(),
connector: [1, 2],
panel_label: 'Tabs',
show: true,
tab_model: null,
},
{
id: uuidv4(),
connector: [3],
panel_label: 'Image',
show: true,
},
{
id: uuidv4(),
connector: [4],
panel_label: 'Text',
show: true,
},
{
id: uuidv4(),
connector: [5],
panel_label: 'Annotations',
show: true,
},
];
```
### The keys in detail
- **entrypoint**
......@@ -284,42 +265,44 @@ The data structure of the panels are inside app.vue component. You can work on t
Defaults to `Manuscript`
- **panels**
- **standalone**
denotes if the Viewer will be used as a single page application on it's own or if it will be embedded into an existing page. If you want to use it in the latter case, please toggle the value to "false". That way the language toggle in the footer section will not show up.
Defaults to **true**
- **Panels Configure**
- **panels**
It's keys correspond to the panelnames, e.g. "contents", "text", "image" and so on.
<br />
**Note:** Pls **leave these keys UNTOUCHED** since these are for internal use only!
<br /><br />
Each object inside panels consists of keys: `component`, `name`, `show`, `tab` and `toolbar`.
Change either name-key according to your liking and set either show-key to **false** if you don't want the Viewer to show the appropriate panel/s.
Each object inside panels consists of keys: `id`, `connector`, `pane_label`, `show` and `tab_model`.
Change either panel_label-key according to your liking and set either show-key to **false** if you don't want the Viewer to show the appropriate panel/s.
Example given:
```json
{
panels: {
panels: [
{
component: toC,
name: 'text',
show: false,
tabs: false,
toolbar: 'toC',
id: uuidv4(),
connector: [1, 2],
panel_label: 'Tabs',
show: true,
tab_model: null,
},
}
]
}
```
- **Component** Refers to the required component that we are integrating.
- **name** Refers to the caption in each panel's toolbar.
- **id** Provides unique id always instead of hard coded id's.
- **connector** Groups together the panels as tabs (works on user configure as well).
- **panel_label** Refers to the caption in each panel's toolbar
- **show** toggles (show or rather hide) the appropriate panel respectively.
- **tabs** denotes, if the panel will be grouped together with others inside a single panel.
- **toolbar** displays the related component on toolbar and can able to toggle panels.
- **standalone**
denotes if the Viewer will be used as a single page application on it's own or if it will be embedded into an existing page. If you want to use it in the latter case, please toggle the value to "false". That way the language toggle in the footer section will not show up.
Defaults to **true**
- **tab_model** Represents to displays panels in a tab when loading for the first time.
## Dockerfile
......
{
"name": "viewer",
"version": "1.3.0",
"version": "1.3.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
......@@ -3885,6 +3885,16 @@
"integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==",
"dev": true
},
"bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"dev": true,
"optional": true,
"requires": {
"file-uri-to-path": "1.0.0"
}
},
"bl": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz",
......@@ -7218,6 +7228,13 @@
}
}
},
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"dev": true,
"optional": true
},
"filesize": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz",
......@@ -15004,6 +15021,7 @@
"dev": true,
"optional": true,
"requires": {
"bindings": "^1.5.0",
"nan": "^2.12.1"
}
},
......@@ -15655,6 +15673,7 @@
"dev": true,
"optional": true,
"requires": {
"bindings": "^1.5.0",
"nan": "^2.12.1"
}
},
......
......@@ -35,12 +35,9 @@
</template>
<script>
import Content from '@/components/content.vue';
import Footer from '@/components/footer.vue';
import Header from '@/components/header.vue';
import Metadatatab from '@/components/tab-panels/metadatatab.vue';
import OpenSeadragon from '@/components/openseadragon.vue';
import Treeviewtab from '@/components/tab-panels/treeviewtab.vue';
import PanelsMixin from '@/config/panels.js';
export default {
name: 'Viewer',
......@@ -48,6 +45,7 @@ export default {
Header,
Footer,
},
mixins: [PanelsMixin],
data() {
return {
annotations: {},
......@@ -65,50 +63,6 @@ export default {
label: '',
manifests: [],
tree: [],
panels: [
{
component: null,
name: 'tabs',
show: true,
tabs: {
children: [
{
component: Treeviewtab,
label: 'Contents',
name: 'content',
},
{
component: Metadatatab,
label: 'Metadata',
name: 'meta',
},
],
model: 'content',
},
toolbar: 'Tabs',
},
{
component: OpenSeadragon,
name: 'image',
show: true,
tabs: [],
toolbar: 'Image',
},
{
component: Content,
name: 'text',
show: true,
tabs: [],
toolbar: 'Content',
},
{
component: null,
name: 'annotations',
show: true,
tabs: [],
toolbar: 'Annotations',
},
],
};
},
methods: {
......
......@@ -3,13 +3,13 @@
<q-btn class="btn-panel" :icon="panelicon" @click="status = true" label="Configure" flat />
<q-dialog v-model="status" persistent transition-show="scale" transition-hide="scale">
<q-card class="bg-white text-black" style="width: 450px">
<q-card class="bg-white text-black" style="width: 600px">
<q-card-section>
<div class="text-h6 text-capitalize">Customize Panels</div>
</q-card-section>
<q-card-section class="q-pt-none">
<Dragboxes :boxs="panelboxes" @updated="(b) => $root.$emit('panels-position', b)" />
<Dragpanelboxes :data="panelboxes" />
</q-card-section>
<q-card-actions align="right" class="bg-white text-black">
......@@ -21,12 +21,12 @@
</template>
<script>
import Dragboxes from '@/components/util/dragboxes';
import Dragpanelboxes from '@/components/util/panelboxes.vue';
import { fasSolarPanel } from '@quasar/extras/fontawesome-v5';
export default {
components: {
Dragboxes,
Dragpanelboxes,
},
props: {
panelboxes: Array,
......
......@@ -10,7 +10,7 @@
@click="() => handleStatusPanel(i)"
>
<q-icon class="q-pr-xs" size="xs" :name="renderCheckIcon(i)" />
{{ p.toolbar.toUpperCase() }}
{{ p.panel_label.toUpperCase() }}
</q-item>
<q-item
......@@ -20,13 +20,13 @@
v-close-popup
@click="()=> handleStatusPanel(-1, true)"
>
<q-icon class="q-pr-xs text-capitalize" size="xs" :name="fasUndo" />
<q-icon class="q-pr-xs text-capitalize" size="xs" :name="fasUndo" />
{{ 'RESET' }}
</q-item>
</q-list>
</ToggleFilter>
<PanelsPosition :panelboxes="panels"/>
<PanelsPosition :panelboxes="panels" />
</div>
</template>
......@@ -65,10 +65,12 @@ export default {
},
// display toggle title when hovering
handleToggleTitle(idx) {
const titleName = this.panels[idx].name;
const titleName = this.panels[idx].panel_label;
const titleUpper = `${titleName[0].toUpperCase()}${titleName.slice(1)}`;
return this.panels[idx].show ? `Hide ${titleUpper} Tab` : `Show ${titleUpper} Tab`;
return this.panels[idx].show
? `Hide ${titleUpper} Panel`
: `Show ${titleUpper} Panel`;
},
},
created() {
......@@ -86,8 +88,4 @@ export default {
margin-right: 8px
button:last-of-type
margin-right: 0
.toggle-list
> *
align-items: center
</style>
<template>
<section>
<p>Drag and drop the buttons to reorder the panels</p>
<Draggable v-model="results" @change="$emit('updated', results)">
<transition-group class="panel-container">
<div v-for="(box, i) in results" :key="`box${i}`"
class="panel-box unselect-text bg-grey"
v-html="box.toolbar"
/>
</transition-group>
</Draggable>
</section>
</template>
<script>
import Draggable from 'vuedraggable';
export default {
components: {
Draggable,
},
props: {
boxs: {
type: Array,
required: true,
},
},
data: () => ({
results: [],
}),
mounted() {
this.results = this.boxs;
},
};
</script>
<style lang="sass" scoped>
.panel-container
display: flex
flex-wrap: wrap
.panel-box
background-color: white
border-radius: 10px
color: black
cursor: pointer
margin: 5px
padding: 10px
text-transform: uppercase
</style>
<template>
<section class="panel-boxs">
<div class="panels">
<Panelsdraggable
handle=".only-bedrag"
v-model="panels"
@change="$root.$emit('panels-position', panels)"
>
<div class="p-c" v-for="(panel, idx) in panels" v-show="panel.show" :key="`pa${idx}`">
<div
class="effected"
@dragleave.prevent="dragHighlightComponent($event, false)"
@dragover.prevent="dragHighlightComponent($event)"
@drop="receivingComponent($event)"
:unique-index="idx"
>
<div>
<header>
<input
class="hidden-txtinput"
type="text"
:value="panel.panel_label"
@input="(e) => handlePanelLabel(e, idx)"
/>
</header>
<q-separator />
<div class="components-list">
<template v-if="panel.connector.length">
<div v-for="(comp,i) in panel.connector" :key="`pi${i}`"
draggable="true"
v-text="comp.label"
@dragstart="dragged = comp.id; draggedPanelIdx = idx"
@dragend="dragged = null; draggedPanelIdx = null"
/>
</template>
</div>
<div class="actions">
<q-btn
class="only-bedrag"
color="blue"
round
size="xs"
title="Drag and drop the panels to reorder."
:icon="fasArrowsAlt"
/>
</div>
</div>
</div>
</div>
<q-btn v-if="panels.length < 4"
color="secondary"
flat
size="large"
slot="footer"
:icon="fasPlus"
@click="addPanel"
/>
</Panelsdraggable>
</div>
</section>
</template>
<script>
import Panelsdraggable from 'vuedraggable';
import { fasPlus, fasTrash, fasArrowsAlt } from '@quasar/extras/fontawesome-v5';
export default {
components: {
Panelsdraggable,
},
data: () => ({
dragged: null,
draggedPanelIdx: null,
panels: [],
}),
props: { data: Array },
mounted() {
this.setpanels();
},
watch: {
data() {
this.setpanels();
},
},
computed: {
panelsEmptyConnectors() {
let result = false;
this.data.forEach((p) => {
if (p.connector.length) result = true;
});
return result;
},
},
methods: {
addPanel() {
this.$root.$emit('add-panel');
},
dragHighlightComponent(event, isEnter = true) {
const element = event.target.classList.contains('effected');
const outline = isEnter && element ? '1px solid grey' : '';
event.target.style.outline = outline;
},
handlePanelLabel(e, index) {
this.$root.$emit('update-panellabel', { v: e.target.value, index });
},
receivingComponent(e) {
const element = e.target;
const targetElID = element.getAttribute('unique-index');
this.dragHighlightComponent(e, false);
if (!this.dragged) return;
if (!element.classList.contains('effected')) return;
this.$root.$emit('handle-connector', {
from: this.draggedPanelIdx,
idC: this.dragged,
to: targetElID,
});
},
setpanels() {
this.panels = this.data;
},
},
created() {
this.fasPlus = fasPlus;
this.fasTrash = fasTrash;
this.fasArrowsAlt = fasArrowsAlt;
},
};
</script>
<style lang="sass" scoped>
.panels
> *
column-gap: 20px
display: grid
grid-template-columns: 1fr 1fr 1fr 1fr
div.p-c
position: relative
> *
background-color: #eee
height: 300px
padding: 15px 10px
header
display: flex
.components-list
> *
background-color: white
border-radius: 5px
cursor: move
margin: 10px 0
padding: 10px
&:active, &:focus
outline: 1px solid blue
.actions
left: -10px
position: absolute
top: -10px
</style>
import Content from '@/components/content.vue';
import Metadatatab from '@/components/tab-panels/metadatatab.vue';