GridEditor.vue 44.5 KB
Newer Older
Anton's avatar
Intial  
Anton committed
1 2
<template lang="pug">

3
  div.cell-grid-container(
4
  :class="{'overflow-hidden': activeHandler}",
5
  style="overflow-y: hidden; scroll-behavior: smooth;")
christianrhansen's avatar
christianrhansen committed
6

christianrhansen's avatar
christianrhansen committed
7 8 9
    q-window-resize-observable(@resize="updateGridDimensions")

    //----- modal for existing cell editing
christianrhansen's avatar
christianrhansen committed
10
    q-modal(v-model="modal", minimized, content-css="background-color: #eee; border-radius: .5rem;")
11
      grid-editor-editing-cells-mobile
12

13
    // -----------------------------------------------------------------------------------------------------------------
christianrhansen's avatar
christianrhansen committed
14
    // ----------------------------------------------------------------------------------------------- "new cell"-header
15 16
    #new-cell-header.fixed-top.z-top.bg-dark.text-white.transition(:class="{'show': cachedNewCell}")
      q-item.q-pl-md.q-pr-xs.q-py-none.full-height
17
        q-item-main
christianrhansen's avatar
christianrhansen committed
18
          strong New {{ cachedNewCellType }}
19

christianrhansen's avatar
christianrhansen committed
20 21
        q-item-side.text-white
          q-btn(@click="event => {addMobileCell(event)}", round, flat)
22 23
            q-icon(name="check")

christianrhansen's avatar
christianrhansen committed
24
          q-btn.clear-button.q-px-md.q-mr-md.on-right(@click="clearHandler('cached cell')", flat)
christianrhansen's avatar
christianrhansen committed
25
            q-icon(name="clear")
26

christianrhansen's avatar
christianrhansen committed
27
    // ------------------------------------------------------------------------------------------ "selected cell"-header
28
    #selected-cell-header.fixed-top.z-top.bg-dark.text-white.transition(:class="{'show': showEditingCells}")
29
      q-item.q-pl-md.q-pr-none.q-py-none.full-height(v-if="!modal")
30
        q-item-main
31
          strong {{ selectedCell.type }}
32

christianrhansen's avatar
christianrhansen committed
33 34
        q-item-side.text-white
          q-btn.q-px-sm(@click="event => {handleCellEdit(event, selectedCell.annotation)}", flat)
35 36
            q-icon(name="edit")

christianrhansen's avatar
christianrhansen committed
37
          q-btn.q-px-sm.on-right.on-left(
38
          @click="event => {handleCellContextMenuDelete(event, selectedCell.annotation)}", flat)
39
            q-icon(name="delete")
40

christianrhansen's avatar
christianrhansen committed
41
          q-btn.clear-button.q-px-md.q-mr-md.on-right(@click="clearHandler('cell')", flat)
42 43
            q-icon(name="clear")

44
    // -----------------------------------------------------------------------------------------------------------------
45
    // ------------------------------------------------------------------------------------------------------------ grid
46
    div.cell-grid.relative-position(
47
    v-touch-pan="panGrid",
48 49 50 51 52 53
    @dragenter="handleGridDragOver",
    @dragover="handleGridDragOver",
    @dragleave="handleGridDragEnd",
    @drop="handleGridDrop",
    @contextmenu="handleGridContextMenu",
    :style="gridStyle")
Anton's avatar
Intial  
Anton committed
54

55
      //----------------------------------------------------------------------------------- context menu: grid (desktop)
56
      q-context-menu.desktop-only(ref="gridmenu")
Anton's avatar
Intial  
Anton committed
57 58
        q-list(link, separator, no-border, style="min-width: 150px; max-height: 300px;")
          q-item(
59 60 61 62 63
          v-for="action in gridContextMenuActions",
          :key="action.label",
          v-close-overlay,
          @click.native="event => {action.handler(event)}")
            q-item-main(:label="action.label")
Anton's avatar
Intial  
Anton committed
64 65 66

      template(v-if="!resizingGrid")

67 68
        //--------------------------------------------------------------------------------------------------------------
        //--------------------------------------------------------------------------------------------------------- cell
69
        template(v-for="(annotation, index) in annotations")
70
          // v-touch-hold="event => {cellCopy(event, annotation)}",
Anton's avatar
Intial  
Anton committed
71
          .cell-item(
72 73 74 75 76 77
          v-if="!annotationUIStates[annotation._uuid] || !annotationUIStates[annotation._uuid].beingDragged",
          draggable="true",
          @click.prevent="event => {handleCellTouch(event, annotation)}",
          @dragstart="event => {handleCellDragStart(event, annotation)}",
          @dragend="event => {handleCellDragEnd(event, annotation)}",
          @contextmenu="handleCellContextMenu",
78
          :style="getCellStyle(annotation)",
79 80 81
          :class="getAnnotationClasses(annotation._uuid, 'cell-item')",
          :key="`cell-${index}`")

82
            //------------------------------------------------------------------------------------ edit-button (desktop)
83
            .desktop-only
Mathias Bär's avatar
Mathias Bär committed
84
              q-btn.edit-button.absolute-top-right(
85 86 87
              @click.prevent="event => {handleCellEditClick(event, annotation)}",
              :class="getAnnotationClasses(annotation._uuid, 'editing')",
              style="top: 8px; right: 8px;",
88 89
              :icon="annotationUIStates[annotation._uuid].editing ? 'close' : 'edit'", flat, round, size="md")

90
            //------------------------------------------------------------------------------------- select cell (mobile)
91
            .mobile-only
92
              .edit-button.absolute.fit.bg-transparent(
93
              v-touch-hold="event => {handleCellEdit(event, annotation)}",
94
              @click.prevent="event => {touchMobileCell(event, annotation)}")
95

96
            //--------------------------------------------------------------------------------------------- cell content
97
            //----- selecting cells disabled because it has no use currently
98 99 100 101 102
            // switch with cell component below to re-enable it
            //cell(
              @click.native.prevent="event => {handleCellClick(event, annotation)}", :annotation="annotation", :preview="true")
            cell(
            :annotation="annotation",
103
            :preview="true")
104

105 106
            //-------------------------------------------------------------------------------------------------- desktop
            //------------------------------------------------------------------------------------------- resize-handler
107
            .desktop-only.cell-item-resize-handle(
108 109 110 111
            draggable="true",
            @dragstart="event => {handleCellResizerDragStart(event, annotation)}",
            @dragend="event => {handleCellResizerDragEnd(event, annotation)}",
            @dragexit="event => {handleCellResizerDragEnd(event, annotation)}")
112
              q-icon.q-ma-xs(name="network cell")
113

114
            //-------------------------------------------------------------------------------------- context menu: cells
115 116
            // TODO: needs revision
            q-context-menu.desktop-only
117 118 119 120 121 122 123
              q-list(link, separator, no-border, style="min-width: 150px; max-height: 300px;")
                q-item(
                v-for="action in cellContextMenuActions",
                :key="action.label",
                v-close-overlay,
                @click.native="event => {action.handler(event, annotation)}")
                  q-item-main(:label="action.label")
Anton's avatar
Intial  
Anton committed
124

125 126
        //----------------------------------------------------------------------------------------------- temporary cell
        //------------------------------------------------------------------------------------------------------ desktop
127
        template(v-for="(tmpCell, index) in tmpObjects")
128
          .cell-item.cell-item-tmp(:style="getCellStyle(tmpCell)", :key="`cell-tmp-${index}`")
Anton's avatar
Intial  
Anton committed
129 130
            cell(:cell="tmpCell")

131
        //------------------------------------------------------------------------------------------------------- mobile
132
        template(v-if="mobileTempCell.show && cachedNewCell")
133
          .cell-item.cell-item-tmp-mobile(:style="getCellStyle(mobileTempCell)")
134

christianrhansen's avatar
christianrhansen committed
135 136
        //------------------------------------------------------------------------------------------------------ handler
        //--------------------------------------------------------------------------------------------------------- move
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
        //----- (main)
        cell-handler-mobile.main-handler.shadow-1(
        v-if="!cellHandler.resize.pushed",
        @onIntersectionChange="intersectionChanged",
        :element="'move'",
        v-touch-pan="handleMoveCell",
        :style="{left: cellHandler.move.x - 20 + 'px', top: cellHandler.move.y - 20 + 'px'}",
        :class="[{'pushed': !cellHandler.move.pushed}, {'hide': !mobileTempCell.onGrid && !showEditingCells}]")
          q-icon.rotate-180(name="open_with", size="22px")

        //----- (temp)
        //----- (when main move handler is outside of viewport)
        cell-handler-mobile.temp-handler.shadow-1(
        v-touch-pan="handleMoveCell",
        :doubleTap="{el: $el, type: 'scroll', oLeft: mobileTempCell.left}",
        :class="[(!mobileTempCell.onGrid || tempHandler.move.intersectingMainHandler || cellHandler.resize.pushed || cellHandler.move.pushed ? 'hide': 'show'), (tempHandler.move.side === 'left' ? 'left-side' : 'right-side')]",
        :style="{top: cellHandler.move.y + 59 - 20 + 'px'}",)
          q-icon.rotate-180(name="open_with", size="22px")

christianrhansen's avatar
christianrhansen committed
156
        //------------------------------------------------------------------------------------------------------- resize
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
        //----- (main)
        cell-handler-mobile.main-handler.shadow-1(
        v-if="!cellHandler.move.pushed",
        @onIntersectionChange="intersectionChanged",
        :element="'resize'",
        v-touch-pan="handleResizeCell",
        :style="{left: cellHandler.resize.x - 20 + 'px', top: cellHandler.resize.y - 20 + 'px'}",
        :class="[{'pushed': !cellHandler.resize.pushed}, {'hide': !mobileTempCell.onGrid && !showEditingCells}]")
          q-icon(name="signal_cellular_4_bar", size="12px", style="margin-left: -3px;")

        //----- (temp)
        //----- (when main resize handler is outside of viewport)
        cell-handler-mobile.temp-handler.shadow-1(
        v-touch-pan="handleResizeCell",
        :class="[(!mobileTempCell.onGrid || tempHandler.resize.intersectingMainHandler || cellHandler.move.pushed || cellHandler.resize.pushed ? 'hide': 'show'), (tempHandler.resize.side === 'left' ? 'left-side' : 'right-side')]",
        :style="{top: cellHandler.resize.y + 59 - 20 + 'px'}",)
          q-icon(name="signal_cellular_4_bar", size="12px", style="margin-left: -3px;")
174

christianrhansen's avatar
christianrhansen committed
175
      // ---------------------------------------------------------------------------------------------------------------
Anton's avatar
Intial  
Anton committed
176
      template(v-else)
177
        .cell-item(:style="getCellStyle({x:0,y:0,width:1,height:1})", key="cell-grid-resizer")
Anton's avatar
Intial  
Anton committed
178
          div.cell-item-resize-handle(
179 180 181 182 183
          draggable="true",
          @dragstart="event => {handleGridResizerDragStart(event)}",
          @dragend="event => {handleGridResizerDragEnd(event)}",
          @dragexit="event => {handleGridResizerDragEnd(event)}")
            q-icon(name="network cell")
Anton's avatar
Intial  
Anton committed
184

Mathias Bär's avatar
Mathias Bär committed
185
      //template(v-if="!isMobile")
186 187 188 189 190
        .fixed-top-right(style="right:18px; top:68px", v-if="!$store.state.mosys.showSources")
          q-btn(round, color="primary", small, @click="handleGridButtonClickEdit", style="margin-right: 0.5em")
            q-icon(name="add")
          q-btn(round, color="primary", small, @click="$router.push(`/mosys/grids/${$route.params.uuid}`)")
            q-icon(name="remove red eye")
Mathias Bär's avatar
Mathias Bär committed
191
      //template(v-if="isMobile")
Mathias Bär's avatar
Mathias Bär committed
192 193 194 195 196
        .fixed-top-right.q-mt-sm(v-if="!$store.state.mosys.showSources", style="z-index: 10000; padding-top: 3px;")
          q-btn.q-mr-sm(round, color="primary", size="sm", @click="handleGridButtonClickEdit")
            q-icon(name="add")
          q-btn.q-mr-md(round, color="primary", size="sm", @click="$router.push(`/mosys/grids/${$route.params.uuid}`)")
            q-icon(name="remove red eye")
Anton's avatar
Intial  
Anton committed
197 198 199 200 201

</template>

<script>
  import Cell from './Cell'
202 203
  import { userHasFeature } from 'mbjs-quasar/src/lib'
  import { mapGetters } from 'vuex'
204
  import GridEditorEditingCellsMobile from './/GridEditorEditingCellsMobile'
205
  import CellHandlerMobile from './CellHandlerMobile'
Anton's avatar
Intial  
Anton committed
206 207 208 209 210 211

  const nullImage = new Image()
  nullImage.src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='

  export default {
    components: {
212
      Cell,
213 214
      GridEditorEditingCellsMobile,
      CellHandlerMobile
Anton's avatar
Intial  
Anton committed
215
    },
216
    props: ['gridUuid', 'tabsAreOpen'],
Anton's avatar
Intial  
Anton committed
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
    data () {
      return {
        gridContextMenuActions: {
          insert_column_left: {
            label: 'Insert Column Left',
            handler: this.handleGridContextMenuInsertColumnLeft
          },
          delete_column: {
            label: 'Delete Column',
            handler: this.handleGridContextMenuDeleteColumn
          },
          insert_row_above: {
            label: 'Insert Row Above',
            handler: this.handleGridContextMenuInsertRowAbove
          },
          delete_row: {
            label: 'Delete Row',
            handler: this.handleGridContextMenuDeleteRow
          },
          edit_grid_dimensions: {
            label: 'Change Grid',
            handler: () => { this.resizingGrid = !this.resizingGrid }
          }
        },
        grid: undefined,
242
        annotations: undefined,
243
        tmpObjects: [],
244
        annotationUIStates: {},
Anton's avatar
Anton committed
245
        gridDimensions: { gridWidth: 0, gridHeight: 0, cellWidth: 0, cellHeight: 0 },
Anton's avatar
Intial  
Anton committed
246
        contextMenuClickPosition: {},
247
        resizingGrid: false,
248
        isMobile: this.$q.platform.is.mobile,
249 250 251 252 253 254 255 256 257 258
        mobileTempCell: {
          x: 0,
          y: 0,
          width: 1,
          height: 1,
          left: 0,
          show: false,
          onGrid: false,
          button: false
        },
259
        modal: false,
260
        cellHandler: {
261
          size: {width: 40, height: 40},
262
          move: {x: 20, y: undefined, gridPosition: {row: undefined, column: undefined}, pushed: false, pos: undefined, inViewport: undefined, onLeft: undefined, onRight: undefined, dataLeft: undefined},
263
          resize: {x: undefined, y: undefined, gridPosition: {row: undefined, column: undefined}, pushed: false, pos: undefined}
264
        },
265
        tempHandler: {
christianrhansen's avatar
christianrhansen committed
266 267
          move: {left: undefined, intersectingMainHandler: false, side: undefined},
          resize: {left: undefined, intersectingMainHandler: false, side: undefined}
268
        },
269
        activeHandler: false,
270
        selectedCell: {type: undefined, annotation: undefined}
Anton's avatar
Intial  
Anton committed
271 272
      }
    },
273 274
    computed: {
      ...mapGetters({
christianrhansen's avatar
christianrhansen committed
275
        cachedNewCell: 'mosys/getNewCell',
276
        user: 'auth/getUserState',
277
        // isMobile: 'globalSettings/getIsMobile',
Mathias Bär's avatar
Mathias Bär committed
278
        // editingCells: 'mosys/getEditingCells'
279
        showEditingCells: 'mosys/getShowEditingCells',
280 281
        scrollPositionCache: 'mosys/getScrollPositionCache',
        editMode: 'mosys/getEditMode'
282
      }),
christianrhansen's avatar
christianrhansen committed
283
      cachedNewCellType () {
284 285 286 287 288
        if (this.cachedNewCell) {
          let type = this.cachedNewCell.component
          return type.substr(4, type.length - 4)
        }
      },
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
      cellContextMenuActions () {
        const actions = {
          delete: {
            label: 'Delete',
            handler: this.handleCellContextMenuDelete
          },
          insert_column_left: {
            label: 'Insert Column Left',
            handler: this.handleGridContextMenuInsertColumnLeft
          },
          insert_row_above: {
            label: 'Insert Row Above',
            handler: this.handleGridContextMenuInsertRowAbove
          }
        }
        if (userHasFeature(this.user, 'cssediting')) {
          actions.edit_css_classname = {
            label: 'Edit CSS class name',
            handler: this.handleCellContextMenuEditCSS
          }
        }
        return actions
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
      },
      gridStyle () {
        if (!this.gridDimensions || !this.gridDimensions.full) return {}
        // TODO: fix mobile grid editor view
        const cell = this.gridDimensions.full.cell
        return {
          width: `${this.gridDimensions.full.width}px`,
          height: '100%',
          'grid-auto-columns': `${cell.width}px`,
          'grid-auto-rows': `${cell.height}px`,
          'background-image': `url("data:image/svg+xml;utf8,` +
            `<svg xmlns='http://www.w3.org/2000/svg' width='100%' height='100%'><defs>` +
            `<pattern id='smallGrid' width='${cell.width}' height='${cell.height}' patternUnits='userSpaceOnUse'>` +
            `<path d='M ${cell.width} 0 L 0 0 0 ${cell.height}' fill='none' stroke='gray' stroke-width='0.5'/>` +
            `</pattern></defs><rect width='100%' height='100%' fill='url(%23smallGrid)' /></svg>")`
        }
327 328
      }
    },
Anton's avatar
Intial  
Anton committed
329 330
    async mounted () {
      await this.fetchData()
331
      this.resetScrollPosition()
332 333 334
    },
    beforeDestroy () {
      this.observer.disconnect()
Anton's avatar
Intial  
Anton committed
335 336
    },
    watch: {
337
      'cellHandler.move.gridPosition': {
338 339
        handler: function (obj) {
          if (this.showEditingCells) {
340
            this.mobileCellMove(this.selectedCell.annotation, obj.column, obj.row)
341
          }
342 343 344
        },
        deep: true
      },
345 346 347
      'cellHandler.resize.gridPosition': {
        handler: function (obj) {
          if (this.showEditingCells) {
348 349
            let w = obj.column - this.cellHandler.move.gridPosition.column + 1
            let h = obj.row - this.cellHandler.move.gridPosition.row + 1
350
            this.mobileCellResize(this.selectedCell.annotation, w, h)
351 352 353 354
          }
        },
        deep: true
      },
Anton's avatar
Anton committed
355
      annotations () {
356
        this.updateAnnotationUIStates()
Anton's avatar
Intial  
Anton committed
357 358 359 360 361 362
      },
      gridMetadata () {
        this.updateGridDimensions()
      },
      async gridUuid () {
        await this.fetchData()
Mathias Bär's avatar
Mathias Bär committed
363 364
      },
      showEditingCells (val) {
365
        // console.log('show editing cells', val)
Mathias Bär's avatar
Mathias Bär committed
366 367 368
        if (val === false) {
          this.updateAnnotationUIStates()
        }
369 370 371
      },
      tabsAreOpen () {
        this.resetScrollPosition()
Anton's avatar
Intial  
Anton committed
372 373 374
      }
    },
    methods: {
375 376 377 378 379 380 381 382
      clearHandler (target) {
        switch (target) {
        case 'cell':
          Object.keys(this.annotationUIStates).filter((k) => {
            this.annotationUIStates[k].editing = false
          })
          this.$store.commit('mosys/setEditingCells', '')
          break
383 384
        case 'cached cell':
          this.$store.commit('mosys/cacheNewCell', undefined)
385 386 387
          this.clearHandler('temp cell')
          break
        case 'temp cell':
388
          this.mobileTempCell = {x: undefined, y: undefined, width: undefined, height: undefined, onGrid: false}
389
          break
390 391
        }
      },
392
      intersectionChanged (obj) {
christianrhansen's avatar
christianrhansen committed
393 394
        let _offsetLeft = Math.sign(obj.offsetLeft)

395
        // -------------------- move
396
        if (obj.element === 'move') {
christianrhansen's avatar
christianrhansen committed
397
          this.tempHandler.move.intersectingMainHandler = obj.intersecting
398

christianrhansen's avatar
christianrhansen committed
399
          // behind left side
christianrhansen's avatar
christianrhansen committed
400
          if (_offsetLeft < 0) {
401
            this.tempHandler.move.side = 'left'
402
          }
403
          // intersecting
christianrhansen's avatar
christianrhansen committed
404
          else if (isNaN(_offsetLeft)) {
christianrhansen's avatar
christianrhansen committed
405
            if (this.tempHandler.move.intersectingMainHandler && this.tempHandler.move.side === 'right') this.tempHandler.resize.intersectingMainHandler = false
406
          }
christianrhansen's avatar
christianrhansen committed
407
          // behind right side
christianrhansen's avatar
christianrhansen committed
408
          else if (_offsetLeft === 1) {
409
            this.tempHandler.move.side = 'right'
christianrhansen's avatar
christianrhansen committed
410
            this.tempHandler.resize.intersectingMainHandler = true
411
          }
412
        }
413

414
        // -------------------- resize
christianrhansen's avatar
christianrhansen committed
415
        else if (obj.element === 'resize') {
christianrhansen's avatar
christianrhansen committed
416
          this.tempHandler.resize.intersectingMainHandler = obj.intersecting
417

christianrhansen's avatar
christianrhansen committed
418
          // on left side
christianrhansen's avatar
christianrhansen committed
419
          if (_offsetLeft === -1) {
420
            this.tempHandler.resize.side = 'left'
421
          }
christianrhansen's avatar
christianrhansen committed
422
          /*
423
          // intersecting
christianrhansen's avatar
christianrhansen committed
424
          else if (isNaN(Math.sign(obj.offsetLeft))) {
425
          }
christianrhansen's avatar
christianrhansen committed
426
          */
christianrhansen's avatar
christianrhansen committed
427
          // on right side
christianrhansen's avatar
christianrhansen committed
428
          else if (_offsetLeft === 1) {
429
            this.tempHandler.resize.side = 'right'
430
          }
431 432
        }
      },
christianrhansen's avatar
christianrhansen committed
433
      handleMoveCell (obj) {
christianrhansen's avatar
christianrhansen committed
434 435 436 437 438
        let
          pos = this.getGridPositionForEvent(obj),
          fullCell = this.gridDimensions.full.cell,
          moveHandler = this.cellHandler.move,
          tempCell = this.mobileTempCell
439

christianrhansen's avatar
christianrhansen committed
440
        if (obj.isFirst) {
441
          this.activeHandler = true
christianrhansen's avatar
christianrhansen committed
442 443
          tempCell.show = true
          moveHandler.pushed = true
christianrhansen's avatar
christianrhansen committed
444
        }
445

christianrhansen's avatar
christianrhansen committed
446 447
        tempCell.x = pos.x
        tempCell.y = pos.y
448

christianrhansen's avatar
christianrhansen committed
449 450 451 452
        moveHandler.x = pos.ox
        moveHandler.y = pos.oy
        moveHandler.gridPosition.row = pos.y
        moveHandler.gridPosition.column = pos.x
453

christianrhansen's avatar
christianrhansen committed
454
        if (obj.isFinal) {
455
          this.activeHandler = false
christianrhansen's avatar
christianrhansen committed
456 457 458 459 460 461
          tempCell.onGrid = true
          moveHandler.pushed = false
          moveHandler.x = fullCell.width * (tempCell.x - 1) + 8 + 20
          moveHandler.y = fullCell.height * (tempCell.y - 1) + 8 + 20
          this.cellHandler.resize.x = fullCell.width * (pos.x + tempCell.width - 1) - 8 - 20
          this.cellHandler.resize.y = fullCell.height * (pos.y + tempCell.height - 1) - 8 - 20
christianrhansen's avatar
christianrhansen committed
462 463
        }
      },
christianrhansen's avatar
christianrhansen committed
464
      handleResizeCell (obj) {
465 466 467 468
        let
          pos = this.getGridPositionForEvent(obj),
          resizeHandler = this.cellHandler.resize,
          tempCell = this.mobileTempCell
469 470

        if (obj.isFirst) {
471
          this.activeHandler = true
472 473
          tempCell.show = true
          resizeHandler.pushed = true
474 475
        }

476 477
        tempCell.width = pos.x - tempCell.x + 1
        tempCell.height = pos.y - tempCell.y + 1
478

479 480
        resizeHandler.x = pos.ox
        resizeHandler.y = pos.oy
481 482
        resizeHandler.gridPosition.row = pos.y
        resizeHandler.gridPosition.column = pos.x
483

484
        if (obj.isFinal) {
485
          this.activeHandler = false
486 487 488 489
          tempCell.onGrid = true
          resizeHandler.pushed = false
          resizeHandler.x = this.gridDimensions.full.cell.width * pos.x - 8 - 20
          resizeHandler.y = this.gridDimensions.full.cell.height * pos.y - 8 - 20
490 491
        }
      },
492 493
      handleCellEdit (event, annotation) {
        console.log('handleCellEdit', annotation)
494 495 496
        this.touchMobileCell(event, annotation)
        this.handleModal()
      },
497 498 499
      handleModal () {
        this.modal = !this.modal
      },
500
      panGrid (obj) {
501
        if (this.cachedNewCell && !this.mobileTempCell.onGrid) {
christianrhansen's avatar
christianrhansen committed
502 503 504 505 506 507
          let
            pos = this.getGridPositionForEvent(obj),
            fullCell = this.gridDimensions.full.cell,
            tempCell = this.mobileTempCell,
            moveHandler = this.cellHandler.move,
            resizeHandler = this.cellHandler.resize
508 509

          if (obj.isFirst) {
christianrhansen's avatar
christianrhansen committed
510 511 512
            tempCell.show = true
            tempCell.x = pos.x
            tempCell.y = pos.y
513 514
          }

christianrhansen's avatar
christianrhansen committed
515 516
          tempCell.width = pos.x - tempCell.x + 1
          tempCell.height = pos.y - tempCell.y + 1
517 518

          if (obj.isFinal) {
christianrhansen's avatar
christianrhansen committed
519 520 521 522 523 524 525
            tempCell.onGrid = true

            moveHandler.x = fullCell.width * (tempCell.x - 1) + 8 + 20
            moveHandler.y = fullCell.height * (tempCell.y - 1) + 8 + 20

            resizeHandler.x = fullCell.width * pos.x - 8 - 20
            resizeHandler.y = fullCell.height * pos.y - 8 - 20
526
          }
527
        }
528
      },
529
      async cellCopy (event, annotation) {
530 531
        this.$store.commit('mosys/setEditingCells', '')

532
        this.$q.notify({
533
          message: 'Copied.',
534
          color: 'dark',
535 536
          textColor: 'white',
          position: 'bottom-right'
537 538 539 540 541 542 543 544 545 546
        })

        const _cell = await this.$store.dispatch('cells/get', annotation.body.source.id)
        const resourceCell = {
          data: { content: '' },
          config: {},
          component: _cell.component
        }
        this.$store.commit('mosys/cacheNewCell', resourceCell)
      },
547
      addMobileCell (event) {
christianrhansen's avatar
christianrhansen committed
548
        if (this.cachedNewCell) {
549
          this.handleGridDrop(event)
christianrhansen's avatar
christianrhansen committed
550
          this.$store.commit('mosys/cacheNewCell', undefined)
551 552
          this.$q.notify({
            message: 'Cell was added.',
christianrhansen's avatar
christianrhansen committed
553 554 555
            color: 'primary',
            position: 'bottom-left',
            timeout: 800
556
          })
christianrhansen's avatar
christianrhansen committed
557 558
        }
      },
559
      touchMobileCell (event, cell) {
560
        if (!this.mobileTempCell.onGrid && cell) {
561 562 563 564 565 566 567 568 569
          Object.keys(this.annotationUIStates).filter((k) => {
            if (k === cell._uuid && this.annotationUIStates[k].editing) console.log(k, cell._uuid)
            else this.annotationUIStates[k].editing = false
          })
          this.annotationUIStates[cell._uuid].editing = !this.annotationUIStates[cell._uuid].editing
          this.updateEditingCells()
          this.$root.$emit('mosys_saveScrollPosition')
          // this.handleCellEditClick(event, annotation)
        }
570
      },
christianrhansen's avatar
christianrhansen committed
571
      handleCellTouch (event, annotation) {
572
        console.log('handleCellTouch', annotation)
573
        if (!this.cachedNewCell) {
574 575 576 577 578 579 580 581 582 583 584 585 586 587
          let
            parsed = annotation.target.selector.parse(),
            sliced = parsed.xywh.slice(0, 4),
            x = sliced[0],
            y = sliced[1],
            w = sliced[2],
            h = sliced[3]

          this.cellHandler.move.x = this.gridDimensions.full.cell.width * (x - 1) + 8 + 20
          this.cellHandler.move.y = this.gridDimensions.full.cell.height * (y - 1) + 8 + 20

          this.cellHandler.resize.x = this.gridDimensions.full.cell.width * (x + w - 1) - 8 - 20
          this.cellHandler.resize.y = this.gridDimensions.full.cell.height * (y + h - 1) - 8 - 20

588
          this.selectedCell.annotation = annotation
589
          this.getCellType(annotation)
590
        }
christianrhansen's avatar
christianrhansen committed
591
      },
592 593 594 595 596
      async getCellType (annotation) {
        let cell = await this.$store.dispatch('cells/get', annotation.body.source.id)
        let cellType = cell.component
        this.selectedCell.type = cellType.substr(4, cellType.length - 4)
      },
597 598
      async mobileCellMove (annotation, x, y) {
        // this.$el.scrollLeft = this.$el.scrollLeft + (this.gridDimensions.full.cell.width * _x)
599

christianrhansen's avatar
christianrhansen committed
600
        let parsed = annotation.target.selector.parse()
601
        let target = this.grid.get2DArea([x, y], parsed.xywh.slice(2))
christianrhansen's avatar
christianrhansen committed
602

603 604 605 606 607 608 609
        annotation.target.selector.value = target.selector.value
        await this.$store.dispatch('annotations/patch', [annotation.id, {
          target: {
            selector: { value: target.selector.value }
          }
        }])
      },
610
      async mobileCellResize (annotation, _w, _h) {
611 612
        let
          parsed = annotation.target.selector.parse(),
613 614 615
          sliced = parsed.xywh.slice(0, 4),
          x = sliced[0],
          y = sliced[1]
616

617
        const value = { xywh: [x, y, _w, _h] }
618 619 620 621 622 623 624
        annotation.target.selector.value = value

        await this.$store.dispatch('annotations/patch', [annotation.id, { target: { selector: { value } } }])
      },
      setEditMode (mode) {
        this.$store.commit('mosys/setEditMode', mode)
      },
625 626 627 628
      //
      // DATA
      //

Anton's avatar
Intial  
Anton committed
629 630 631
      async fetchData () {
        if (this.gridUuid) {
          this.grid = await this.$store.dispatch('maps/get', this.gridUuid)
632 633 634 635 636 637 638 639
          if (!Object.keys(this.grid.config).length) {
            this.grid.config = {
              columns: 10,
              rows: 6,
              ratio: 16 / 9.0
            }
            await this.updateGridMetadataStore()
          }
Anton's avatar
Intial  
Anton committed
640
          this.updateGridDimensions()
641 642 643 644 645 646
          const { items } = await this.$store.dispatch('annotations/find', {
            'target.id': this.grid.id,
            'body.purpose': 'linking',
            'body.type': 'Cell'
          })
          this.annotations = items
Anton's avatar
Intial  
Anton committed
647 648
        }
      },
649 650 651
      async updateGridMetadataStore () {
        await this.$store.dispatch('maps/patch', [this.grid.id, { config: this.grid.config }])
        this.updateGridDimensions()
Anton's avatar
Intial  
Anton committed
652
      },
653 654 655 656 657
      updateSelectedCells () {
        const _this = this
        let selectedCells = Object.keys(this.annotationUIStates).filter(k => {
          return _this.annotationUIStates[k].selected
        }).map(k => {
Christian Hansen's avatar
Christian Hansen committed
658 659
          // return _this.cells.find(c => c._uuid === k)
          return _this.annotations.find(c => c._uuid === k)
660 661
        })
        this.$store.commit('mosys/setSelectedCells', selectedCells)
Anton's avatar
Intial  
Anton committed
662
      },
Mathias Bär's avatar
Mathias Bär committed
663 664 665 666 667 668 669 670
      updateEditingCells () {
        const _this = this
        let editingCells = Object.keys(this.annotationUIStates).filter(k => {
          return _this.annotationUIStates[k].editing
        }).map(k => {
          // return _this.cells.find(c => c._uuid === k)
          return _this.annotations.find(c => c._uuid === k)
        })
671 672 673 674 675 676 677
        /*
        console.log('this.annotationUIStates: ', this.annotationUIStates)
        console.log('editingCells: ', editingCells)
        if (this.isMobile) {
          console.log('MOBILE')
        }
        */
Mathias Bär's avatar
Mathias Bär committed
678 679
        this.$store.commit('mosys/setEditingCells', editingCells)
      },
680 681 682 683 684
      updateAnnotationUIStates () {
        let newAnnotationUIStates = {}
        this.annotations.forEach(a => {
          newAnnotationUIStates[a._uuid] = {
            selected: false,
Mathias Bär's avatar
Mathias Bär committed
685
            editing: false,
686
            beingResized: false,
Mathias Bär's avatar
Mathias Bär committed
687
            beginDragged: false,
688
            annotation: a
Anton's avatar
Intial  
Anton committed
689
          }
690 691
        })
        this.annotationUIStates = newAnnotationUIStates
Anton's avatar
Intial  
Anton committed
692
        this.updateSelectedCells()
Mathias Bär's avatar
Mathias Bär committed
693
        this.updateEditingCells()
694
      },
695 696 697 698 699

      //
      // GRID DRAG & DROP HANDLERS
      //

700
      async handleGridDragOver (event) {
Anton's avatar
Intial  
Anton committed
701 702 703
        let _this = this
        if (this.resizingGrid) {
          const position = this.getGridPositionForEvent(event)
704 705
          this.grid.config.ratio = position.ox / (position.oy * 1.0)
          await this.updateGridMetadataStore()
Anton's avatar
Intial  
Anton committed
706 707
        }
        else {
708 709
          let annotation = this.annotations.filter(annotation => {
            if (!_this.annotationUIStates[annotation._uuid]) return false
Mathias Bär's avatar
Mathias Bär committed
710
            return _this.annotationUIStates[annotation._uuid].beingDragged ||
711
              _this.annotationUIStates[annotation._uuid].beingResized
Anton's avatar
Intial  
Anton committed
712 713
          }).shift()
          let offset, position
714 715 716 717
          if (!annotation) {
            annotation = {
              target: this.grid.get2DArea([1, 1], [1, 1])
            }
Anton's avatar
Intial  
Anton committed
718 719 720
            position = this.getGridPositionForEvent(event)
          }
          else {
721
            offset = this.annotationUIStates[annotation._uuid].draggingOffset
Anton's avatar
Intial  
Anton committed
722 723
            position = this.getGridPositionForEvent(event, offset)
          }
724 725
          if (!this.tmpObjects.length) this.tmpObjects.push(annotation)
          const parsed = annotation.target.selector.parse()
Anton's avatar
Intial  
Anton committed
726
          if (event.dataTransfer.types.includes('text/plain')) {
727 728
            parsed.xywh[0] = position.x
            parsed.xywh[1] = position.y
Anton's avatar
Intial  
Anton committed
729 730 731
            event.preventDefault()
          }
          else {
732 733
            parsed.xywh[2] = Math.max(1, 1 + position.x - parsed.xywh[0])
            parsed.xywh[3] = Math.max(1, 1 + position.y - parsed.xywh[1])
Anton's avatar
Intial  
Anton committed
734
          }
735
          annotation.target.selector.value = parsed
Anton's avatar
Intial  
Anton committed
736 737 738
        }
      },
      handleGridDragEnd () {
739
        this.tmpObjects = []
Anton's avatar
Intial  
Anton committed
740
      },
741
      async handleGridDrop (event) {
742 743 744 745 746
        let dropData
        if (event.dataTransfer) dropData = event.dataTransfer.getData('text/plain')
        if (!dropData) dropData = JSON.stringify(this.cachedNewCell)
        // console.log('handleGridDrop - event', event)
        // console.log('handleGridDrop - dropData', dropData)
747 748
        if (dropData) {
          dropData = JSON.parse(dropData)
749
          let annotation = this.annotations.find(a => a.id === dropData.id)
750 751 752 753 754
          const { x, y } = this.getGridPositionForEvent(
            event,
            annotation ? this.annotationUIStates[annotation._uuid].draggingOffset : undefined
          )
          if (annotation) {
755 756 757
            const
              parsed = annotation.target.selector.parse(),
              target = this.grid.get2DArea([x, y], parsed.xywh.slice(2))
758 759 760 761 762 763
            annotation.target.selector.value = target.selector.value
            await this.$store.dispatch('annotations/patch', [annotation.id, {
              target: {
                selector: { value: target.selector.value }
              }
            }])
Anton's avatar
Intial  
Anton committed
764 765
          }
          else {
766 767
            if (this.mobileTempCell.x) console.log('this.mobileTempCell', this.mobileTempCell)
            let test = this.grid.get2DArea([this.mobileTempCell.x, this.mobileTempCell.y], [this.mobileTempCell.width, this.mobileTempCell.height])
768 769 770 771 772 773 774 775 776 777 778
            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
                }
              },
779 780
              // target: this.grid.get2DArea([x, y], [1, 1])
              target: test
781
            })
782
            this.annotations.push(annotation)
Anton's avatar
Anton committed
783
            this.updateAnnotationUIStates()
Anton's avatar
Intial  
Anton committed
784
          }
785

786
          this.tmpObjects = []
Anton's avatar
Intial  
Anton committed
787 788
          event.preventDefault()
        }
789
        this.clearHandler('temp cell')
Anton's avatar
Intial  
Anton committed
790
      },
791 792 793 794

      //
      // CELL DRAG & DROP HANDLERS
      //
Mathias Bär's avatar
Mathias Bär committed
795 796 797
      handleCellEditClick (event, cell) {
        this.annotationUIStates[cell._uuid].editing = !this.annotationUIStates[cell._uuid].editing
        this.updateEditingCells()
798
        this.$root.$emit('mosys_saveScrollPosition')
Mathias Bär's avatar
Mathias Bär committed
799
      },
800
      /*
801 802 803 804
      handleCellClick (event, cell) {
        this.annotationUIStates[cell._uuid].selected = !this.annotationUIStates[cell._uuid].selected
        this.updateSelectedCells()
      },
805
      */
806
      handleCellDragStart (event, annotation) {
christianrhansen's avatar
christianrhansen committed
807
        console.log('§§§§§§§§', annotation)
808 809 810 811 812 813 814 815 816 817 818 819
        if (!this.isMobile) {
          if (!this.annotationUIStates[annotation._uuid].beingResized) {
            event.dataTransfer.setData('text/plain', JSON.stringify(annotation))
            event.dataTransfer.setDragImage(nullImage, 0, 0)
            let elContainerBoundingBox = this.$el.getBoundingClientRect()
            let elBoundingBox = event.target.getBoundingClientRect()
            let offset = {
              x: (event.clientX - elContainerBoundingBox.x) - (elBoundingBox.x - elContainerBoundingBox.x),
              y: (event.clientY - elContainerBoundingBox.y) - (elBoundingBox.y - elContainerBoundingBox.y)
            }
            this.annotationUIStates[annotation._uuid].draggingOffset = offset
            this.annotationUIStates[annotation._uuid].beingDragged = true
820
          }
821
          this.tmpObjects.push(annotation)
822 823 824
        }
      },
      handleCellDragEnd (event, annotation) {
825 826 827
        if (!this.mobile) {
          this.annotationUIStates[annotation._uuid].beingDragged = false
        }
828 829 830 831
      },
      async handleCellContextMenuDelete (event, annotation) {
        this.annotationUIStates[annotation._uuid].selected = false
        this.updateSelectedCells()
Mathias Bär's avatar
Mathias Bär committed
832
        this.updateEditingCells()
833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875
        await this.$store.dispatch('cells/delete', annotation.body.source.id)
        await this.$store.dispatch('annotations/delete', annotation.id)
        this.annotations = this.annotations.filter(a => a.id !== annotation.id)
      },

      //
      // GRID RESIZE HANDLERS

      handleGridResizerDragStart (event) {
        event.dataTransfer.setDragImage(nullImage, 0, 0)
      },
      async handleGridResizerDragEnd () {
        await this.updateGridMetadataStore()
      },

      //
      // CELL RESIZE HANDLERS
      //

      handleCellResizerDragStart (event, annotation) {
        event.dataTransfer.setDragImage(nullImage, 0, 0)
        this.annotationUIStates[annotation._uuid].beingResized = true
        this.tmpObjects.push(annotation)
      },
      async handleCellResizerDragEnd (event, annotation) {
        let position = this.getGridPositionForEvent(event)
        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.tmpObjects = []
        await this.$store.dispatch('annotations/patch', [annotation.id, { target: { selector: { value } } }])
      },

      //
      // CELL CONTEXT MENU
      //

      handleCellContextMenu (event) {
Anton's avatar
Intial  
Anton committed
876 877
        this.contextMenuClickPosition = this.getGridPositionForEvent(event)
      },
878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894
      async handleCellContextMenuEditCSS (event, annotation) {
        try {
          let styleClass = await this.$q.dialog({
            title: this.$t('forms.edit_css_class.title'),
            ok: this.$t('buttons.set_css_class'),
            cancel: this.$t('buttons.cancel'),
            prompt: {
              model: annotation.styleClass,
              type: 'text'
            }
          })
          if (styleClass) {
            if (!styleClass.length) styleClass = undefined
            else if (styleClass.indexOf('.') === 0) styleClass = styleClass.substr(1)
          }
          annotation.styleClass = styleClass
          this.$store.dispatch('annotations/patch', [annotation._uuid, { target: { styleClass: styleClass || null } }])
Anton's avatar
Intial  
Anton committed
895
        }
896 897 898 899 900 901 902 903 904
        catch (e) { /* dialog canceled */ }
      },

      //
      // GRID CONTEXT MENU
      //

      handleGridContextMenu (event) {
        this.contextMenuClickPosition = this.getGridPositionForEvent(event)
Anton's avatar
Intial  
Anton committed
905 906 907
      },
      async handleGridContextMenuInsertColumnLeft () {
        let position = this.contextMenuClickPosition
908 909 910 911 912 913 914 915
        for (let annotation of this.annotations) {
          const parsed = annotation.target.selector.parse()
          if (parsed.xywh[0] >= position.x) {
            parsed.xywh[0] += 1
            annotation.target.selector.value = parsed
            await this.$store.dispatch('annotations/patch', [annotation.id, {
              target: { selector: { value: parsed } }
            }])
Anton's avatar
Intial  
Anton committed
916 917
          }
        }
918
        this.grid.config.columns += 1
Anton's avatar
Intial  
Anton committed
919 920 921 922
        await this.updateGridMetadataStore()
      },
      async handleGridContextMenuDeleteColumn () {
        let position = this.contextMenuClickPosition
923 924 925 926 927 928 929 930
        for (let annotation of this.annotations) {
          const parsed = annotation.target.selector.parse()
          if (parsed.xywh[0] > position.x) {
            parsed.xywh[0] -= 1
            annotation.target.selector.value = parsed
            await this.$store.dispatch('annotations/patch', [annotation.id, {
              target: { selector: { value: parsed } }
            }])
Anton's avatar
Intial  
Anton committed
931 932
          }
        }
933
        this.grid.config.columns -= 1
Anton's avatar
Intial  
Anton committed
934 935 936 937
        await this.updateGridMetadataStore()
      },
      async handleGridContextMenuInsertRowAbove () {
        let position = this.contextMenuClickPosition
938 939 940 941 942 943 944 945
        for (let annotation of this.annotations) {
          const parsed = annotation.target.selector.parse()
          if (parsed.xywh[1] >= position.y) {
            parsed.xywh[1] += 1
            annotation.target.selector.value = parsed
            await this.$store.dispatch('annotations/patch', [annotation.id, {
              target: { selector: { value: parsed } }
            }])
Anton's avatar
Intial  
Anton committed
946 947
          }
        }
948
        this.grid.config.rows += 1
Anton's avatar
Intial  
Anton committed
949 950 951 952
        await this.updateGridMetadataStore()
      },
      async handleGridContextMenuDeleteRow () {
        let position = this.contextMenuClickPosition
953 954 955 956 957 958 959 960
        for (let annotation of this.annotations) {
          const parsed = annotation.target.selector.parse()
          if (parsed.xywh[1] > position.y) {
            parsed.xywh[1] -= 1
            annotation.target.selector.value = parsed
            await this.$store.dispatch('annotations/patch', [annotation.id, {
              target: { selector: { value: parsed } }
            }])
Anton's avatar
Intial  
Anton committed
961 962
          }
        }
963
        this.grid.config.rows -= 1
Anton's avatar
Intial  
Anton committed
964 965
        await this.updateGridMetadataStore()
      },
966 967 968 969 970

      //
      // NAVIGATION
      //

Anton's avatar
Intial  
Anton committed
971
      handleGridButtonClickEdit () {
972
        this.$store.commit('mosys/toggleSources')
Anton's avatar
Intial  
Anton committed
973
      },
974 975 976 977 978

      //
      // GRID HELPERS
      //

Anton's avatar
Anton committed
979
      getGridPositionForEvent (event, offset = { x: 0, y: 0 }) {
980
        // console.log('###', event)
Anton's avatar
Anton committed
981
        offset = { x: 0, y: 0 } // TODO: remove quick fix
982 983 984 985 986 987 988

        let _clientX, _clientY

        if (event.clientX && event.clientY) {
          _clientX = event.clientX
          _clientY = event.clientY
        }
989
        if (event.position) {
990 991 992 993
          _clientX = event.position.left
          _clientY = event.position.top
        }

Anton's avatar
Intial  
Anton committed
994
        const elContainerBoundingBox = this.$el.getBoundingClientRect()
995 996 997 998
        const ox = _clientX + this.$el.scrollLeft - elContainerBoundingBox.x - offset.x
        const oy = _clientY + this.$el.scrollTop - elContainerBoundingBox.y - offset.y
        // const ox = event.clientX + this.$el.scrollLeft - elContainerBoundingBox.x - offset.x
        // const oy = event.clientY + this.$el.scrollTop - elContainerBoundingBox.y - offset.y
Anton's avatar
Intial  
Anton committed
999 1000
        const x = Math.ceil(ox / this.gridDimensions.full.cell.width)
        const y = Math.ceil(oy / this.gridDimensions.full.cell.height)
Anton's avatar
Anton committed
1001
        return { x: x, y: y, ox: ox, oy: oy }
Anton's avatar
Intial  
Anton committed
1002
      },
1003 1004 1005 1006
      updateGridDimensions (size) {
        if (!this.grid || !this.grid.config) return

        let elWidth = size ? size.width : this.$el.offsetWidth
1007
        // let elHeight = size ? size.height : this.$el.offsetHeight
1008
        let cellSizeRatio = this.grid.config.ratio
1009 1010
        // let gridHeight = elHeight
        let gridHeight = this.$el.offsetHeight
1011
        let cellHeight = gridHeight / this.grid.config.rows
Anton's avatar
Intial  
Anton committed
1012
        let cellWidth = elWidth / Math.round(elWidth / (cellHeight * cellSizeRatio))
1013
        console.log('cellWidth', cellWidth)
1014
        let gridWidth = cellWidth * this.grid.config.columns
Anton's avatar
Intial  
Anton committed
1015
        let cellsPerWidth = elWidth / cellWidth
1016
        let cellWidthMini = elWidth / this.grid.config.columns
Anton's avatar
Intial  
Anton committed
1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029
        let gridHeightMini = cellWidthMini / cellSizeRatio
        this.gridDimensions = {
          full: {
            width: gridWidth,
            height: gridHeight,
            cell: {
              width: cellWidth,
              height: cellHeight
            },
            cells_per_width: cellsPerWidth
          },
          mini: {
            width: elWidth,
1030
            height: gridHeightMini * this.grid.config.rows,
Anton's avatar
Intial  
Anton committed
1031 1032 1033 1034 1035 1036 1037
            cell: {
              width: cellWidthMini,
              height: gridHeightMini
            }
          }
        }
      },
1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054
      getCellStyle (obj) {
        let x, y, w, h

        // ----- existing cell
        if (obj.id) {
          const parsed = obj.target.selector.parse()
          x = parsed.xywh[0]
          y = parsed.xywh[1]
          w = parsed.xywh[2]
          h = parsed.xywh[3]
        }
        // ----- temp cell
        else {
          x = obj.x
          y = obj.y
          w = obj.width
          h = obj.height
1055
        }
Anton's avatar
Intial  
Anton committed
1056
        return {
1057 1058 1059 1060
          'grid-column-start': x,
          'grid-column-end': `span ${w}`,
          'grid-row-start': y,
          'grid-row-end': `span ${h}`
Anton's avatar
Intial  
Anton committed
1061
        }
Mathias Bär's avatar
Mathias Bär committed
1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074
      },
      getAnnotationClasses (uuid, which) {
        if (which === 'editing') {
          if (this.annotationUIStates[uuid].editing) {
            return 'bg-primary text-white'
          }
          else {
            return 'bg-grey'
          }
        }
        if (which === 'cell-item') {
          return {
            selected: this.annotationUIStates[uuid] ? this.annotationUIStates[uuid].selected : false,
1075 1076
            editing: this.annotationUIStates[uuid] ? this.annotationUIStates[uuid].editing : false,
            hover: !this.isMobile
Mathias Bär's avatar
Mathias Bär committed
1077 1078