GridEditor.vue 46 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
18
          strong New {{ newCellType }}
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")

christianrhansen's avatar
christianrhansen committed
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 78 79 80 81
          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",
          :style="getAnnotationStyle(annotation)",
          :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;",
christianrhansen's avatar
christianrhansen committed
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",
christianrhansen's avatar
christianrhansen committed
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

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

christianrhansen's avatar
christianrhansen committed
131
        //------------------------------------------------------------------------------------------------------- mobile
132
        template(v-if="mobileTempCell.show && cachedNewCell")
133 134 135
          .cell-item.cell-item-tmp-mobile.row.justify-center.items-center(
          ref="_mobileTempCell",
          :style="mobileTempCellStyle(mobileTempCell)")
christianrhansen's avatar
christianrhansen committed
136
            cell.absolute-top-left.q-ma-sm(:cell="cachedNewCell", :temp="true")
137

christianrhansen's avatar
christianrhansen committed
138 139
        //------------------------------------------------------------------------------------------------------ handler
        //--------------------------------------------------------------------------------------------------------- move
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
        //----- (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
159
        //------------------------------------------------------------------------------------------------------- resize
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
        //----- (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;")
177

christianrhansen's avatar
christianrhansen committed
178
      // ---------------------------------------------------------------------------------------------------------------
Anton's avatar
Intial  
Anton committed
179
      template(v-else)
180
        .cell-item(:style="getAnnotationStyle({x:0,y:0,width:1,height:1})", key="cell-grid-resizer")
Anton's avatar
Intial  
Anton committed
181
          div.cell-item-resize-handle(
182 183 184 185 186
          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
187

Mathias Bär's avatar
Mathias Bär committed
188
      //template(v-if="!isMobile")
189 190 191 192 193
        .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
194
      //template(v-if="isMobile")
Mathias Bär's avatar
Mathias Bär committed
195 196 197 198 199
        .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
200 201 202 203 204

</template>

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

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

  export default {
    components: {
215
      Cell,
216 217
      GridEditorEditingCellsMobile,
      CellHandlerMobile
Anton's avatar
Intial  
Anton committed
218
    },
219
    props: ['gridUuid', 'tabsAreOpen'],
Anton's avatar
Intial  
Anton committed
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
    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,
245
        annotations: undefined,
246
        tmpObjects: [],
247
        annotationUIStates: {},
Anton's avatar
Anton committed
248
        gridDimensions: { gridWidth: 0, gridHeight: 0, cellWidth: 0, cellHeight: 0 },
Anton's avatar
Intial  
Anton committed
249
        contextMenuClickPosition: {},
250
        resizingGrid: false,
251
        isMobile: this.$q.platform.is.mobile,
252 253 254 255 256 257 258 259 260 261 262 263
        mobileTempCell: {
          x: 0,
          y: 0,
          ox: 0,
          oy: 0,
          width: 1,
          height: 1,
          left: 0,
          show: false,
          onGrid: false,
          button: false
        },
264
        modal: false,
265
        cellHandler: {
266
          size: {width: 40, height: 40},
267
          move: {x: 20, y: undefined, gridPosition: {row: undefined, column: undefined}, pushed: false, pos: undefined, inViewport: undefined, onLeft: undefined, onRight: undefined, dataLeft: undefined},
268
          resize: {x: undefined, y: undefined, gridPosition: {row: undefined, column: undefined}, pushed: false, pos: undefined}
269
        },
270
        tempHandler: {
christianrhansen's avatar
christianrhansen committed
271 272
          move: {left: undefined, intersectingMainHandler: false, side: undefined},
          resize: {left: undefined, intersectingMainHandler: false, side: undefined}
273
        },
274
        activeHandler: false,
275
        selectedCell: {type: undefined, annotation: undefined}
Anton's avatar
Intial  
Anton committed
276 277
      }
    },
278 279
    computed: {
      ...mapGetters({
christianrhansen's avatar
christianrhansen committed
280
        cachedNewCell: 'mosys/getNewCell',
281
        user: 'auth/getUserState',
282
        // isMobile: 'globalSettings/getIsMobile',
Mathias Bär's avatar
Mathias Bär committed
283
        // editingCells: 'mosys/getEditingCells'
284
        showEditingCells: 'mosys/getShowEditingCells',
285 286
        scrollPositionCache: 'mosys/getScrollPositionCache',
        editMode: 'mosys/getEditMode'
287
      }),
288 289 290 291 292 293
      newCellType () {
        if (this.cachedNewCell) {
          let type = this.cachedNewCell.component
          return type.substr(4, type.length - 4)
        }
      },
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
      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
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
      },
      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>")`
        }
332 333
      }
    },
Anton's avatar
Intial  
Anton committed
334 335
    async mounted () {
      await this.fetchData()
336
      this.resetScrollPosition()
337 338 339
    },
    beforeDestroy () {
      this.observer.disconnect()
Anton's avatar
Intial  
Anton committed
340 341
    },
    watch: {
342
      'cellHandler.move.gridPosition': {
343 344
        handler: function (obj) {
          if (this.showEditingCells) {
345
            this.mobileCellMove(this.selectedCell.annotation, obj.column, obj.row)
346
          }
347 348 349
        },
        deep: true
      },
350 351 352
      'cellHandler.resize.gridPosition': {
        handler: function (obj) {
          if (this.showEditingCells) {
353 354
            let w = obj.column - this.cellHandler.move.gridPosition.column + 1
            let h = obj.row - this.cellHandler.move.gridPosition.row + 1
355
            this.mobileCellResize(this.selectedCell.annotation, w, h)
356 357 358 359
          }
        },
        deep: true
      },
Anton's avatar
Anton committed
360
      annotations () {
361
        this.updateAnnotationUIStates()
Anton's avatar
Intial  
Anton committed
362 363 364 365 366 367
      },
      gridMetadata () {
        this.updateGridDimensions()
      },
      async gridUuid () {
        await this.fetchData()
Mathias Bär's avatar
Mathias Bär committed
368 369
      },
      showEditingCells (val) {
370
        // console.log('show editing cells', val)
Mathias Bär's avatar
Mathias Bär committed
371 372 373
        if (val === false) {
          this.updateAnnotationUIStates()
        }
374 375 376
      },
      tabsAreOpen () {
        this.resetScrollPosition()
Anton's avatar
Intial  
Anton committed
377 378 379
      }
    },
    methods: {
380 381 382 383 384 385 386 387
      clearHandler (target) {
        switch (target) {
        case 'cell':
          Object.keys(this.annotationUIStates).filter((k) => {
            this.annotationUIStates[k].editing = false
          })
          this.$store.commit('mosys/setEditingCells', '')
          break
388 389
        case 'cached cell':
          this.$store.commit('mosys/cacheNewCell', undefined)
390 391 392 393
          this.clearHandler('temp cell')
          break
        case 'temp cell':
          this.mobileTempCell = {x: undefined, y: undefined, width: undefined, height: undefined, onGrid: false, button: false}
394
          break
395 396
        }
      },
397
      intersectionChanged (obj) {
christianrhansen's avatar
christianrhansen committed
398 399
        let _offsetLeft = Math.sign(obj.offsetLeft)

400
        // -------------------- move
401
        if (obj.element === 'move') {
christianrhansen's avatar
christianrhansen committed
402
          this.tempHandler.move.intersectingMainHandler = obj.intersecting
403

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

419
        // -------------------- resize
christianrhansen's avatar
christianrhansen committed
420
        else if (obj.element === 'resize') {
christianrhansen's avatar
christianrhansen committed
421
          this.tempHandler.resize.intersectingMainHandler = obj.intersecting
422

christianrhansen's avatar
christianrhansen committed
423
          // on left side
christianrhansen's avatar
christianrhansen committed
424
          if (_offsetLeft === -1) {
425
            this.tempHandler.resize.side = 'left'
426
          }
christianrhansen's avatar
christianrhansen committed
427
          /*
428
          // intersecting
christianrhansen's avatar
christianrhansen committed
429
          else if (isNaN(Math.sign(obj.offsetLeft))) {
430
          }
christianrhansen's avatar
christianrhansen committed
431
          */
christianrhansen's avatar
christianrhansen committed
432
          // on right side
christianrhansen's avatar
christianrhansen committed
433
          else if (_offsetLeft === 1) {
434
            this.tempHandler.resize.side = 'right'
435
          }
436 437
        }
      },
christianrhansen's avatar
christianrhansen committed
438
      handleMoveCell (obj) {
439 440
        this.cellHandler.move.x = obj.position.left
        this.cellHandler.move.y = obj.position.top
441

christianrhansen's avatar
christianrhansen committed
442 443
        let res = this.getGridPositionForEvent(obj)

444 445 446
        this.cellHandler.move.gridPosition.row = res.y
        this.cellHandler.move.gridPosition.column = res.x

christianrhansen's avatar
christianrhansen committed
447
        if (obj.isFirst) {
448
          this.activeHandler = true
christianrhansen's avatar
christianrhansen committed
449
          this.mobileTempCell.show = true
450
          // this.mobileTempCell.button = false
451
          this.cellHandler.move.pushed = true
christianrhansen's avatar
christianrhansen committed
452
        }
453

christianrhansen's avatar
christianrhansen committed
454 455 456
        this.mobileTempCell.x = res.x
        this.mobileTempCell.y = res.y

457 458 459
        this.mobileTempCell.ox = res.ox
        this.mobileTempCell.oy = res.oy

460 461
        this.cellHandler.move.x = res.ox
        this.cellHandler.move.y = res.oy
462

christianrhansen's avatar
christianrhansen committed
463
        if (obj.isFinal) {
464
          this.activeHandler = false
christianrhansen's avatar
christianrhansen committed
465
          this.mobileTempCell.onGrid = true
466
          // this.mobileTempCell.button = true
467
          this.mobileTempCell.left = this.gridDimensions.full.cell.width * (res.x - 1)
468 469 470 471
          this.cellHandler.move.pushed = false
          // this.cellHandler.move.y = this.gridDimensions.full.cell.height * (res.y - 1) + 8 + 59 + 20
          this.cellHandler.move.x = this.gridDimensions.full.cell.width * (this.mobileTempCell.x - 1) + 8 + 20
          this.cellHandler.move.y = this.gridDimensions.full.cell.height * (this.mobileTempCell.y - 1) + 8 + 20
472

473 474
          this.cellHandler.resize.x = this.gridDimensions.full.cell.width * (res.x + this.mobileTempCell.width - 1) - 8 - 20
          this.cellHandler.resize.y = this.gridDimensions.full.cell.height * (res.y + this.mobileTempCell.height - 1) - 8 - 20
christianrhansen's avatar
christianrhansen committed
475 476
        }
      },
christianrhansen's avatar
christianrhansen committed
477
      handleResizeCell (obj) {
christianrhansen's avatar
christianrhansen committed
478
        // this.cursor = {x: obj.position.left, y: obj.position.top}
479 480 481
        // this.cellHandler.resize = {x: obj.position.left, y: obj.position.top}
        this.cellHandler.resize.x = obj.position.left
        this.cellHandler.resize.y = obj.position.top
482

483 484 485
        // ---> panGrid
        let res = this.getGridPositionForEvent(obj)

486 487 488
        this.cellHandler.resize.gridPosition.row = res.y
        this.cellHandler.resize.gridPosition.column = res.x

489
        if (obj.isFirst) {
490
          this.activeHandler = true
491
          this.mobileTempCell.show = true
492
          // this.mobileTempCell.button = false
493
          this.cellHandler.resize.pushed = true
494 495 496 497 498
        }

        this.mobileTempCell.width = res.x - this.mobileTempCell.x + 1
        this.mobileTempCell.height = res.y - this.mobileTempCell.y + 1

499 500
        this.cellHandler.resize.x = res.ox
        this.cellHandler.resize.y = res.oy
501

502
        if (obj.isFinal) {
503
          this.activeHandler = false
504
          this.mobileTempCell.onGrid = true
505
          // this.mobileTempCell.button = true
506
          this.cellHandler.resize.pushed = false
507 508
          // this.addMobileCell(obj)
          // this.mobileTempCell.show = false
509 510
          this.cellHandler.resize.x = this.gridDimensions.full.cell.width * res.x - 8 - 20
          this.cellHandler.resize.y = this.gridDimensions.full.cell.height * res.y - 8 - 20
511 512
        }
      },
513 514
      handleCellEdit (event, annotation) {
        console.log('handleCellEdit', annotation)
515 516 517
        this.touchMobileCell(event, annotation)
        this.handleModal()
      },
518 519 520
      handleModal () {
        this.modal = !this.modal
      },
521
      panGrid (obj) {
522
        if (this.cachedNewCell && !this.mobileTempCell.onGrid) {
523
          // console.log('hhhhhhhhhhhhhhhhh', obj)
524 525 526 527
          let res = this.getGridPositionForEvent(obj)

          if (obj.isFirst) {
            this.mobileTempCell.show = true
528
            // this.mobileTempCell.button = false
529 530 531 532 533 534 535 536
            this.mobileTempCell.x = res.x
            this.mobileTempCell.y = res.y
          }

          this.mobileTempCell.width = res.x - this.mobileTempCell.x + 1
          this.mobileTempCell.height = res.y - this.mobileTempCell.y + 1

          if (obj.isFinal) {
537
            this.mobileTempCell.onGrid = true
538
            // this.mobileTempCell.button = true
539 540
            // this.addMobileCell(obj)
            // this.mobileTempCell.show = false
541 542
            this.cellHandler.move.x = this.gridDimensions.full.cell.width * (this.mobileTempCell.x - 1) + 8 + 20
            this.cellHandler.move.y = this.gridDimensions.full.cell.height * (this.mobileTempCell.y - 1) + 8 + 20
543

544 545
            this.cellHandler.resize.x = this.gridDimensions.full.cell.width * res.x - 8 - 20
            this.cellHandler.resize.y = this.gridDimensions.full.cell.height * res.y - 8 - 20
546
          }
547
        }
548
      },
549
      async cellCopy (event, annotation) {
550 551
        this.$store.commit('mosys/setEditingCells', '')

552
        this.$q.notify({
553
          message: 'Copied.',
554
          color: 'dark',
555 556
          textColor: 'white',
          position: 'bottom-right'
557 558 559 560 561 562 563 564 565 566
        })

        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)
      },
567
      addMobileCell (event) {
christianrhansen's avatar
christianrhansen committed
568
        if (this.cachedNewCell) {
569
          this.handleGridDrop(event)
christianrhansen's avatar
christianrhansen committed
570
          this.$store.commit('mosys/cacheNewCell', undefined)
571 572
          this.$q.notify({
            message: 'Cell was added.',
573 574 575
            color: 'dark',
            position: 'center',
            timeout: 50
576
          })
christianrhansen's avatar
christianrhansen committed
577 578
        }
      },
579
      touchMobileCell (event, cell) {
580
        if (!this.mobileTempCell.onGrid && cell) {
581 582 583 584 585 586 587 588 589
          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)
        }
590
      },
christianrhansen's avatar
christianrhansen committed
591
      handleCellTouch (event, annotation) {
592
        console.log('handleCellTouch', annotation)
593
        if (!this.cachedNewCell) {
594 595 596 597 598 599 600 601 602 603 604 605 606 607
          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

608
          this.selectedCell.annotation = annotation
609
          this.getCellType(annotation)
610
        }
christianrhansen's avatar
christianrhansen committed
611
      },
612 613 614 615 616
      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)
      },
617 618
      async mobileCellMove (annotation, x, y) {
        // this.$el.scrollLeft = this.$el.scrollLeft + (this.gridDimensions.full.cell.width * _x)
619

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

623 624 625 626 627 628 629
        annotation.target.selector.value = target.selector.value
        await this.$store.dispatch('annotations/patch', [annotation.id, {
          target: {
            selector: { value: target.selector.value }
          }
        }])
      },
630
      async mobileCellResize (annotation, _w, _h) {
631 632
        let
          parsed = annotation.target.selector.parse(),
christianrhansen's avatar
christianrhansen committed
633 634 635
          sliced = parsed.xywh.slice(0, 4),
          x = sliced[0],
          y = sliced[1]
636

637
        const value = { xywh: [x, y, _w, _h] }
638 639 640 641 642 643 644
        annotation.target.selector.value = value

        await this.$store.dispatch('annotations/patch', [annotation.id, { target: { selector: { value } } }])
      },
      setEditMode (mode) {
        this.$store.commit('mosys/setEditMode', mode)
      },
645 646 647 648
      //
      // DATA
      //

Anton's avatar
Intial  
Anton committed
649 650 651
      async fetchData () {
        if (this.gridUuid) {
          this.grid = await this.$store.dispatch('maps/get', this.gridUuid)
652 653 654 655 656 657 658 659
          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
660
          this.updateGridDimensions()
661 662 663 664 665 666
          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
667 668
        }
      },
669 670 671
      async updateGridMetadataStore () {
        await this.$store.dispatch('maps/patch', [this.grid.id, { config: this.grid.config }])
        this.updateGridDimensions()
Anton's avatar
Intial  
Anton committed
672
      },
673 674 675 676 677
      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
678 679
          // return _this.cells.find(c => c._uuid === k)
          return _this.annotations.find(c => c._uuid === k)
680 681
        })
        this.$store.commit('mosys/setSelectedCells', selectedCells)
Anton's avatar
Intial  
Anton committed
682
      },
Mathias Bär's avatar
Mathias Bär committed
683 684 685 686 687 688 689 690
      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)
        })
691 692 693 694 695 696 697
        /*
        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
698 699
        this.$store.commit('mosys/setEditingCells', editingCells)
      },
700 701 702 703 704
      updateAnnotationUIStates () {
        let newAnnotationUIStates = {}
        this.annotations.forEach(a => {
          newAnnotationUIStates[a._uuid] = {
            selected: false,
Mathias Bär's avatar
Mathias Bär committed
705
            editing: false,
706
            beingResized: false,
Mathias Bär's avatar
Mathias Bär committed
707
            beginDragged: false,
708
            annotation: a
Anton's avatar
Intial  
Anton committed
709
          }
710 711
        })
        this.annotationUIStates = newAnnotationUIStates
Anton's avatar
Intial  
Anton committed
712
        this.updateSelectedCells()
Mathias Bär's avatar
Mathias Bär committed
713
        this.updateEditingCells()
714
      },
715 716 717 718 719

      //
      // GRID DRAG & DROP HANDLERS
      //

720
      async handleGridDragOver (event) {
Anton's avatar
Intial  
Anton committed
721 722 723
        let _this = this
        if (this.resizingGrid) {
          const position = this.getGridPositionForEvent(event)
724 725
          this.grid.config.ratio = position.ox / (position.oy * 1.0)
          await this.updateGridMetadataStore()
Anton's avatar
Intial  
Anton committed
726 727
        }
        else {
728 729
          let annotation = this.annotations.filter(annotation => {
            if (!_this.annotationUIStates[annotation._uuid]) return false
Mathias Bär's avatar
Mathias Bär committed
730
            return _this.annotationUIStates[annotation._uuid].beingDragged ||
731
              _this.annotationUIStates[annotation._uuid].beingResized
Anton's avatar
Intial  
Anton committed
732 733
          }).shift()
          let offset, position
734 735 736 737
          if (!annotation) {
            annotation = {
              target: this.grid.get2DArea([1, 1], [1, 1])
            }
Anton's avatar
Intial  
Anton committed
738 739 740
            position = this.getGridPositionForEvent(event)
          }
          else {
741
            offset = this.annotationUIStates[annotation._uuid].draggingOffset
Anton's avatar
Intial  
Anton committed
742 743
            position = this.getGridPositionForEvent(event, offset)
          }
744 745
          if (!this.tmpObjects.length) this.tmpObjects.push(annotation)
          const parsed = annotation.target.selector.parse()
Anton's avatar
Intial  
Anton committed
746
          if (event.dataTransfer.types.includes('text/plain')) {
747 748
            parsed.xywh[0] = position.x
            parsed.xywh[1] = position.y
Anton's avatar
Intial  
Anton committed
749 750 751
            event.preventDefault()
          }
          else {
752 753
            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
754
          }
755
          annotation.target.selector.value = parsed
Anton's avatar
Intial  
Anton committed
756 757 758
        }
      },
      handleGridDragEnd () {
759
        this.tmpObjects = []
Anton's avatar
Intial  
Anton committed
760
      },
761
      async handleGridDrop (event) {
762 763 764 765 766
        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)
767 768
        if (dropData) {
          dropData = JSON.parse(dropData)
769
          let annotation = this.annotations.find(a => a.id === dropData.id)
770 771 772 773 774
          const { x, y } = this.getGridPositionForEvent(
            event,
            annotation ? this.annotationUIStates[annotation._uuid].draggingOffset : undefined
          )
          if (annotation) {
775 776 777
            const
              parsed = annotation.target.selector.parse(),
              target = this.grid.get2DArea([x, y], parsed.xywh.slice(2))
778 779 780 781 782 783
            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
784 785
          }
          else {
786 787
            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])
788 789 790 791 792 793 794 795 796 797 798
            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
                }
              },
799 800
              // target: this.grid.get2DArea([x, y], [1, 1])
              target: test
801
            })
802
            this.annotations.push(annotation)
Anton's avatar
Anton committed
803
            this.updateAnnotationUIStates()
Anton's avatar
Intial  
Anton committed
804
          }
805

806
          this.tmpObjects = []
Anton's avatar
Intial  
Anton committed
807 808
          event.preventDefault()
        }
809
        this.clearHandler('temp cell')
Anton's avatar
Intial  
Anton committed
810
      },
811 812 813 814

      //
      // CELL DRAG & DROP HANDLERS
      //
Mathias Bär's avatar
Mathias Bär committed
815 816 817
      handleCellEditClick (event, cell) {
        this.annotationUIStates[cell._uuid].editing = !this.annotationUIStates[cell._uuid].editing
        this.updateEditingCells()
818
        this.$root.$emit('mosys_saveScrollPosition')
Mathias Bär's avatar
Mathias Bär committed
819
      },
820
      /*