Commit 43a2ce1b authored by Anton's avatar Anton

Update mosys grid editor and cells for new data model

parent ece4fedb
<template lang="pug">
.cell-item-inner(:class="{'display-preview': preview, 'display-full': display}")
template(v-if="cellTypeName")
template(v-if="cell")
component(
:is="cellTypeName",
:is="cell.component",
:cell="cell",
:class="cellClasses",
:display="display",
......@@ -21,21 +21,19 @@
import cellTypes from './cells'
export default {
props: ['cell', 'display', 'preview', 'annotation'],
props: ['annotation', 'display', 'preview'],
data () {
return {
cell: undefined,
selected: false,
inViewPort: false
}
},
computed: {
cellTypeName () {
return this.cell.type ? this.cellTypeToClassName(this.cell.type) : ''
},
cellClasses () {
let classes = ['cell-content']
if (this.cell) {
if (this.cell.type) classes.push(`cell-type-${this.cell.type.toLowerCase()}`)
if (this.cell.component) classes.push(`cell-type-${this.cell.component.toLowerCase()}`)
if (this.cell.styleClass) {
classes = classes.concat(this.cell.styleClass.split(' '))
}
......@@ -43,17 +41,23 @@
return classes
}
},
mounted () {
watch: {
async annotation (val) {
if (val) await this.getCell()
}
},
async mounted () {
this.onScroll()
this.$parent.$el.addEventListener('scroll', this.onScroll, false)
if (this.cellTypeName && cellTypes.hasOwnProperty(this.cellTypeName) === false) {
throw new Error('This cell type is missing: ' + this.cell.type)
}
if (this.annotation) await this.getCell()
},
beforeDestroy () {
this.$parent.$el.removeEventListener('scroll', this.onScroll)
},
methods: {
async getCell () {
this.cell = await this.$store.dispatch('cells/get', this.annotation.body.source.id)
},
onScroll () {
const
bounds = this.$el.getBoundingClientRect(),
......@@ -61,9 +65,6 @@
this.inViewPort = (bounds.left >= 0 && bounds.left <= boundsParent.width) ||
(bounds.right >= 0 && bounds.right <= boundsParent.width)
},
cellTypeToClassName (type) {
return 'Cell' + type.slice(0, 1).toUpperCase() + type.slice(1).replace(/[^a-z0-9]/ig, '')
},
handleClick () {
this.selected = !this.selected
},
......
......@@ -257,12 +257,15 @@
}
},
async updateCellContent (value, cell, path) {
const destUuid = cell._uuid
if (destUuid) {
const annotation = await this.$store.dispatch('annotations/get', destUuid)
cell[path] = value
annotation.body.value = JSON.stringify(cell)
await this.$store.dispatch('annotations/patch', [destUuid, { body: annotation.body, target: annotation.target }])
if (cell.id) {
cell.data[path] = value
await this.$store.dispatch('cells/patch', [cell.id, { data: { [path]: value } }])
}
},
async updateCellConfig (value, cell, path) {
if (cell.id) {
cell.config[path] = value
await this.$store.dispatch('cells/patch', [cell.id, { config: { [path]: value } }])
}
}
}
......
......@@ -5,7 +5,7 @@
div.cell-grid(:style="gridStyle")
template(v-for="(cell, index) in cells")
.cell-item(
:style="getCellStyle(cell)",
:style="getAnnotationStyle(cell)",
:title="cell.title")
cell(:cell="cell", display="display")
......@@ -225,7 +225,7 @@
}
}
},
getCellStyle (cell) {
getAnnotationStyle (cell) {
return {
'grid-column-start': cell.x,
'grid-column-end': `span ${cell.width}`,
......
......@@ -21,24 +21,23 @@
template(v-if="!resizingGrid")
template(v-for="(cell, index) in cells")
template(v-for="(annotation, index) in annotations")
.cell-item(
v-if="cellUIStates[cell._uuid] && !cellUIStates[cell._uuid].beingDragged",
v-if="!annotationUIStates[annotation._uuid] || !annotationUIStates[annotation._uuid].beingDragged",
draggable="true",
@dragstart="event => {handleCellDragStart(event, cell)}",
@dragend="event => {handleCellDragEnd(event, cell)}",
@dragstart="event => {handleCellDragStart(event, annotation)}",
@dragend="event => {handleCellDragEnd(event, annotation)}",
@contextmenu="handleCellContextMenu",
:style="getCellStyle(cell)",
:title="cell.title",
@click.prevent="event => {handleCellClick(event, cell)}",
:class="{selected: cellUIStates[cell._uuid] ? cellUIStates[cell._uuid].selected : false}",
:style="getAnnotationStyle(annotation)",
@click.prevent="event => {handleCellClick(event, annotation)}",
:class="{selected: annotationUIStates[annotation._uuid] ? annotationUIStates[annotation._uuid].selected : false}",
:key="`cell-${index}`")
cell(:cell="cell", preview)
cell(:annotation="annotation", preview)
div.cell-item-resize-handle(
draggable="true",
@dragstart="event => {handleCellResizerDragStart(event, cell)}",
@dragend="event => {handleCellResizerDragEnd(event, cell)}",
@dragexit="event => {handleCellResizerDragEnd(event, cell)}")
@dragstart="event => {handleCellResizerDragStart(event, annotation)}",
@dragend="event => {handleCellResizerDragEnd(event, annotation)}",
@dragexit="event => {handleCellResizerDragEnd(event, annotation)}")
q-icon(name="network cell")
q-context-menu
......@@ -47,15 +46,15 @@
v-for="action in cellContextMenuActions",
:key="action.label",
v-close-overlay,
@click.native="event => {action.handler(event, cell)}")
@click.native="event => {action.handler(event, annotation)}")
q-item-main(:label="action.label")
template(v-for="(tmpCell, index) in tmpCells")
.cell-item.cell-item-tmp(:style="getCellStyle(tmpCell)", :key="`cell-tmp-${index}`")
.cell-item.cell-item-tmp(:style="getAnnotationStyle(tmpCell)", :key="`cell-tmp-${index}`")
cell(:cell="tmpCell")
template(v-else)
.cell-item(:style="getCellStyle({x:0,y:0,width:1,height:1})", key="cell-grid-resizer")
.cell-item(:style="getAnnotationStyle({x:0,y:0,width:1,height:1})", key="cell-grid-resizer")
div.cell-item-resize-handle(
draggable="true",
@dragstart="event => {handleGridResizerDragStart(event)}",
......@@ -114,10 +113,11 @@
}
},
grid: undefined,
annotations: undefined,
gridMetadata: {},
cells: [],
tmpCells: [],
cellUIStates: {},
annotationUIStates: {},
gridDimensions: { gridWidth: 0, gridHeight: 0, cellWidth: 0, cellHeight: 0 },
gridStyle: {},
contextMenuClickPosition: {},
......@@ -165,7 +165,7 @@
},
watch: {
cells () {
this.updateCellUIStates()
this.updateAnnotationUIStates()
},
gridMetadata () {
this.updateGridDimensions()
......@@ -178,9 +178,22 @@
async fetchData () {
if (this.gridUuid) {
this.grid = await this.$store.dispatch('maps/get', this.gridUuid)
await this.fetchMetadataAnnotations()
if (!Object.keys(this.grid.config).length) {
this.grid.config = {
columns: 10,
rows: 6,
ratio: 16 / 9.0
}
console.log('conf', this.grid.config)
await this.updateGridMetadataStore()
}
this.updateGridDimensions()
await this.fetchCellAnnotations()
const { items } = await this.$store.dispatch('annotations/find', {
'target.id': this.grid.id,
'body.purpose': 'linking',
'body.type': 'Cell'
})
this.annotations = items
}
},
handleGridResizerDragStart (event) {
......@@ -189,28 +202,31 @@
async handleGridResizerDragEnd () {
await this.updateGridMetadataStore()
},
handleCellResizerDragStart (event, cell) {
handleCellResizerDragStart (event, annotation) {
event.dataTransfer.setDragImage(nullImage, 0, 0)
this.cellUIStates[cell._uuid].beingResized = true
let tmpCell = this.getTmpCell(cell)
this.annotationUIStates[annotation._uuid].beingResized = true
let tmpCell = this.getTmpCell(annotation)
this.tmpCells.push(tmpCell)
},
handleCellResizerDragEnd (event, cell) {
async handleCellResizerDragEnd (event, annotation) {
let position = this.getGridPositionForEvent(event)
cell.width = Math.max(1, 1 + position.x - cell.x)
cell.height = Math.max(1, 1 + position.y - cell.y)
this.cellUIStates[cell._uuid].beingResized = false
let
parsed = annotation.target.selector.parse(),
[x, y, w, h] = parsed.xywh
w = Math.max(1, 1 + position.x - x)
h = Math.max(1, 1 + position.y - y)
const value = { xywh: [x, y, w, h] }
annotation.target.selector.value = value
this.annotationUIStates[annotation._uuid].beingResized = false
this.tmpCells = []
this.updateCellStore(cell)
await this.$store.dispatch('annotations/patch', [annotation.id, { target: { selector: { value } } }])
},
handleCellClick (event, cell) {
this.cellUIStates[cell._uuid].selected = !this.cellUIStates[cell._uuid].selected
this.annotationUIStates[cell._uuid].selected = !this.annotationUIStates[cell._uuid].selected
this.updateSelectedCells()
},
handleCellDragStart (event, cell) {
if (this.cellUIStates[cell._uuid].beingResized) {
}
else {
if (!this.annotationUIStates[cell._uuid].beingResized) {
event.dataTransfer.setData('text/plain', JSON.stringify(cell))
event.dataTransfer.setDragImage(nullImage, 0, 0)
let elContainerBoundingBox = this.$el.getBoundingClientRect()
......@@ -219,14 +235,14 @@
x: (event.clientX - elContainerBoundingBox.x) - (elBoundingBox.x - elContainerBoundingBox.x),
y: (event.clientY - elContainerBoundingBox.y) - (elBoundingBox.y - elContainerBoundingBox.y)
}
this.cellUIStates[cell._uuid].draggingOffset = offset
this.cellUIStates[cell._uuid].beginDragged = true
this.annotationUIStates[cell._uuid].draggingOffset = offset
this.annotationUIStates[cell._uuid].beginDragged = true
}
let tmpCell = this.getTmpCell(cell)
this.tmpCells.push(tmpCell)
},
handleCellDragEnd (event, cell) {
this.cellUIStates[cell._uuid].beingDragged = false
this.annotationUIStates[cell._uuid].beingDragged = false
},
// handleCellContextMenuClick () {
// },
......@@ -235,7 +251,7 @@
// // this.$store.commit('mosys/setSourcesTab', 'tab-default-cells')
// },
async handleCellContextMenuDelete (event, cell) {
this.cellUIStates[cell._uuid].selected = false
this.annotationUIStates[cell._uuid].selected = false
this.updateSelectedCells()
this.cells = this.cells.filter(c => c !== cell)
await this.$store.dispatch('annotations/delete', cell._uuid)
......@@ -264,65 +280,87 @@
}
catch (e) { /* dialog canceled */ }
},
handleGridDragOver (event) {
async handleGridDragOver (event) {
let _this = this
if (this.resizingGrid) {
const position = this.getGridPositionForEvent(event)
this.gridMetadata.ratio = position.ox / (position.oy * 1.0)
this.updateGridDimensions()
this.grid.config.ratio = position.ox / (position.oy * 1.0)
await this.updateGridMetadataStore()
}
else {
let cell = Object.keys(this.cellUIStates).filter(uuid => {
return _this.cellUIStates[uuid].beginDragged || _this.cellUIStates[uuid].beingResized
}).map(uuid => {
return _this.cellUIStates[uuid].cell
let annotation = this.annotations.filter(annotation => {
if (!_this.annotationUIStates[annotation._uuid]) return false
return _this.annotationUIStates[annotation._uuid].beginDragged ||
_this.annotationUIStates[annotation._uuid].beingResized
}).shift()
let offset, position
if (!cell) {
cell = { uuid: null, x: 1, y: 1, width: 1, height: 1 }
if (!annotation) {
annotation = {
target: this.grid.get2DArea([1, 1], [1, 1])
}
position = this.getGridPositionForEvent(event)
}
else {
offset = this.cellUIStates[cell._uuid].draggingOffset
offset = this.annotationUIStates[annotation._uuid].draggingOffset
position = this.getGridPositionForEvent(event, offset)
}
if (!this.tmpCells.length) this.tmpCells.push(annotation)
let tmpCell = this.tmpCells[0]
if (!tmpCell && this.tmpCells.length === 0) {
tmpCell = this.getTmpCell(cell)
this.tmpCells.push(tmpCell)
}
const parsed = tmpCell.target.selector.parse()
if (event.dataTransfer.types.includes('text/plain')) {
tmpCell.x = position.x
tmpCell.y = position.y
tmpCell.target.selector.value = { xywh: [position.x, position.y] }
parsed.xywh[0] = position.x
parsed.xywh[1] = position.y
event.preventDefault()
}
else {
tmpCell.width = Math.max(1, 1 + position.x - tmpCell.x)
tmpCell.height = Math.max(1, 1 + position.y - tmpCell.y)
parsed.xywh[2] = Math.max(1, 1 + position.x - tmpCell.x)
parsed.xywh[3] = Math.max(1, 1 + position.y - tmpCell.y)
}
tmpCell.target.selector.value = parsed
}
},
handleGridDragEnd () {
this.tmpCells = []
},
handleGridDrop (event) {
let cellDropped = event.dataTransfer.getData('text/plain')
if (cellDropped) {
cellDropped = JSON.parse(cellDropped)
let cell = this.cells.find(c => c._uuid === cellDropped._uuid)
let offset, position
if (cell) {
offset = this.cellUIStates[cell._uuid].draggingOffset
position = this.getGridPositionForEvent(event, offset)
async handleGridDrop (event) {
let dropData = event.dataTransfer.getData('text/plain')
if (dropData) {
dropData = JSON.parse(dropData)
let annotation = this.cells.find(c => c.id === dropData.id)
const { x, y } = this.getGridPositionForEvent(
event,
annotation ? this.annotationUIStates[annotation._uuid].draggingOffset : undefined
)
const target = this.grid.get2DArea([x, y], [1, 1])
if (annotation) {
annotation.target.selector.value = target.selector.value
await this.$store.dispatch('annotations/patch', [annotation.id, {
target: {
selector: { value: target.selector.value }
}
}])
console.debug('dropped existing annotation', annotation)
}
else {
cell = cellDropped
position = this.getGridPositionForEvent(event)
const
{ data, config, component } = dropData,
cell = await this.$store.dispatch('cells/post', { data, config, component })
annotation = await this.$store.dispatch('annotations/post', {
body: {
type: 'Cell',
purpose: 'linking',
source: {
id: cell.id
}
},
target
})
console.debug('dropped new cell', cell, annotation)
}
cell.x = position.x
cell.y = position.y
this.tmpCells = []
this.updateCellStore(cell)
// this.updateCellStore(cell)
event.preventDefault()
}
},
......@@ -404,23 +442,23 @@
},
updateSelectedCells () {
const _this = this
let selectedCells = Object.keys(this.cellUIStates).filter(k => {
return _this.cellUIStates[k].selected
let selectedCells = Object.keys(this.annotationUIStates).filter(k => {
return _this.annotationUIStates[k].selected
}).map(k => {
return _this.cells.find(c => c._uuid === k)
})
this.$store.commit('mosys/setSelectedCells', selectedCells)
},
updateCellUIStates () {
let newCellUIStates = {}
this.cells.map(c => {
newCellUIStates[c._uuid] = {
updateAnnotationUIStates () {
let newAnnotationUIStates = {}
this.annotations.map(a => {
newAnnotationUIStates[a._uuid] = {
selected: false,
beingResized: false,
cell: c
annotation: a
}
})
this.cellUIStates = newCellUIStates
this.annotationUIStates = newAnnotationUIStates
this.updateSelectedCells()
},
getTmpCell (cell, type = 'UIFeedback') {
......@@ -445,13 +483,13 @@
updateGridDimensions () {
let elWidth = this.$el.offsetWidth
let elHeight = this.$el.offsetHeight
let cellSizeRatio = this.gridMetadata.ratio
let cellSizeRatio = this.grid.config.ratio
let gridHeight = elHeight
let cellHeight = gridHeight / this.gridMetadata.rows
let cellHeight = gridHeight / this.grid.config.rows
let cellWidth = elWidth / Math.round(elWidth / (cellHeight * cellSizeRatio))
let gridWidth = cellWidth * this.gridMetadata.columns
let gridWidth = cellWidth * this.grid.config.columns
let cellsPerWidth = elWidth / cellWidth
let cellWidthMini = elWidth / this.gridMetadata.columns
let cellWidthMini = elWidth / this.grid.config.columns
let gridHeightMini = cellWidthMini / cellSizeRatio
this.gridDimensions = {
full: {
......@@ -465,7 +503,7 @@
},
mini: {
width: elWidth,
height: gridHeightMini * this.gridMetadata.rows,
height: gridHeightMini * this.grid.config.rows,
cell: {
width: cellWidthMini,
height: gridHeightMini
......@@ -517,30 +555,7 @@
}
return null
}).filter(cell => cell)
this.updateCellUIStates()
},
async fetchMetadataAnnotations () {
const query = {
'body.type': '2DGridMetadata',
'target.id': this.grid.id
}
const result = await this.$store.dispatch('annotations/find', query)
let annotation = result.items.shift()
if (annotation) {
let metadata = JSON.parse(annotation.body.value)
metadata._uuid = annotation._uuid
if (metadata) {
this.gridMetadata = metadata
}
}
else {
this.gridMetadata = {
columns: 10,
rows: 6,
ratio: 16 / 9.0
}
await this.updateGridMetadataStore()
}
this.updateAnnotationUIStates()
},
getGridCellAnnotation (cell) {
return {
......@@ -553,8 +568,11 @@
id: this.grid.id,
type: 'Map',
selector: {
type: '2DLocation',
value: `x=${cell.x}&y=${cell.y}&width=${cell.width}&height=${cell.height}`
conformsTo: 'Media',
type: 'FragmentSelector',
value: {
// xywh: [x, y, width, height]
}
}
}
}
......@@ -583,20 +601,16 @@
await this.fetchCellAnnotations()
},
async updateGridMetadataStore () {
let mapAnnotation = this.getGridMetadataAnnotation(this.grid.id, this.gridMetadata)
if (this.gridMetadata._uuid) {
await this.$store.dispatch('annotations/patch', [this.gridMetadata._uuid, mapAnnotation])
}
await this.$store.dispatch('annotations/post', mapAnnotation)
await this.$store.dispatch('maps/patch', [this.grid.id, { config: this.grid.config }])
this.updateGridDimensions()
},
getCellStyle (cell) {
getAnnotationStyle (annotation) {
const parsed = annotation.target.selector.parse()
return {
'grid-column-start': cell.x,
'grid-column-end': `span ${cell.width}`,
'grid-row-start': cell.y,
'grid-row-end': `span ${cell.height}`
'grid-column-start': parsed.xywh[0],
'grid-column-end': `span ${parsed.xywh[2]}`,
'grid-row-start': parsed.xywh[1],
'grid-row-end': `span ${parsed.xywh[3]}`
}
}
// setCellSet: function (cellSet) {
......
......@@ -53,6 +53,7 @@
inputType: 'text',
type: 'Title',
label: 'Title Cell',
component: 'CellTitle',
help: '',
error: false,
errorMessage: '',
......@@ -62,6 +63,7 @@
inputType: 'textarea',
type: 'Text',
label: 'Text Cell',
component: 'CellText',
help: '',
error: false,
errorMessage: '',
......@@ -71,6 +73,7 @@
inputType: 'url',
type: 'Video',
label: 'Video Cell',
component: 'CellVideo',
help: 'Insert a URL to: a video file or a Vimeo / YouTube video page',
error: false,
errorMessage: 'Needs to be a valid URL',
......@@ -80,6 +83,7 @@
inputType: 'url',
type: 'Image',
label: 'Image Cell',
component: 'CellImage',
help: 'Insert a URL to an image file',
error: false,
errorMessage: 'Needs to be a valid URL',
......@@ -89,6 +93,7 @@
inputType: 'url',
type: 'Internal-Link',
label: 'Link Cell',
component: 'CellInternalLink',
help: 'Insert a URL to a page in this system',
error: false,
errorMessage: 'Needs to be a valid URL',
......@@ -98,6 +103,7 @@
inputType: 'url',
type: 'IFrame',
label: 'IFrame Cell',
component: 'CellIFrame',
help: 'Insert some URL',
error: false,
errorMessage: 'Needs to be a valid URL',
......@@ -122,13 +128,9 @@
this.handleItemChanged(inputField.value, item)
}
const resourceCell = {
uuid: null,
type: item.type,
x: 1,
y: 1,
width: 1,
height: 1,
content: item.value
data: { content: item.value },
config: {},
component: item.component
}
event.dataTransfer.setData('text/plain', JSON.stringify(resourceCell))
},
......
Markdown is supported
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