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

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

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

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

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

christianrhansen's avatar
christianrhansen committed
444 445
        let res = this.getGridPositionForEvent(obj)

446 447 448
        this.cellHandler.move.gridPosition.row = res.y
        this.cellHandler.move.gridPosition.column = res.x

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

christianrhansen's avatar
christianrhansen committed
456 457 458
        this.mobileTempCell.x = res.x
        this.mobileTempCell.y = res.y

459 460 461
        this.mobileTempCell.ox = res.ox
        this.mobileTempCell.oy = res.oy

462 463
        this.cellHandler.move.x = res.ox
        this.cellHandler.move.y = res.oy
464

christianrhansen's avatar
christianrhansen committed
465
        if (obj.isFinal) {
466
          this.activeHandler = false
christianrhansen's avatar
christianrhansen committed
467
          this.mobileTempCell.onGrid = true
468
          // this.mobileTempCell.button = true
469
          this.mobileTempCell.left = this.gridDimensions.full.cell.width * (res.x - 1)
470 471 472 473
          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
474

475 476
          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
477 478
        }
      },
christianrhansen's avatar
christianrhansen committed
479
      handleResizeCell (obj) {
christianrhansen's avatar
christianrhansen committed
480
        // this.cursor = {x: obj.position.left, y: obj.position.top}
481 482 483
        // 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
484

485 486 487
        // ---> panGrid
        let res = this.getGridPositionForEvent(obj)

488 489 490
        this.cellHandler.resize.gridPosition.row = res.y
        this.cellHandler.resize.gridPosition.column = res.x

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

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

501 502
        this.cellHandler.resize.x = res.ox
        this.cellHandler.resize.y = res.oy
503

504
        if (obj.isFinal) {
505
          this.activeHandler = false
506
          this.mobileTempCell.onGrid = true
507
          // this.mobileTempCell.button = true
508
          this.cellHandler.resize.pushed = false
509 510
          // this.addMobileCell(obj)
          // this.mobileTempCell.show = false
511 512
          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
513 514
        }
      },
515 516
      handleCellEdit (event, annotation) {
        console.log('handleCellEdit', annotation)
517 518 519
        this.touchMobileCell(event, annotation)
        this.handleModal()
      },
520 521 522
      handleModal () {
        this.modal = !this.modal
      },
523
      panGrid (obj) {
524
        if (this.cachedNewCell && !this.mobileTempCell.onGrid) {
525
          // console.log('hhhhhhhhhhhhhhhhh', obj)
526 527 528 529
          let res = this.getGridPositionForEvent(obj)

          if (obj.isFirst) {
            this.mobileTempCell.show = true
530
            // this.mobileTempCell.button = false
531 532 533 534 535 536 537 538
            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) {
539
            this.mobileTempCell.onGrid = true
540
            // this.mobileTempCell.button = true
541 542
            // this.addMobileCell(obj)
            // this.mobileTempCell.show = false
543 544
            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
545

546 547
            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
548
          }
549
        }
550
      },
551
      async cellCopy (event, annotation) {
552 553
        this.$store.commit('mosys/setEditingCells', '')

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

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

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

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

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

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

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

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

      //
      // GRID DRAG & DROP HANDLERS
      //

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

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

      //
      // CELL DRAG & DROP HANDLERS
      //
Mathias Bär's avatar
Mathias Bär committed
817 818 819
      handleCellEditClick (event, cell) {
        this.annotationUIStates[cell._uuid].editing = !this.annotationUIStates[cell._uuid].editing
        this.updateEditingCells()
820
        this.$root.$emit('mosys_saveScrollPosition')
Mathias Bär's avatar
Mathias Bär committed
821
      },
822
      /*
823 824 825 826
      handleCellClick (event, cell) {
        this.annotationUIStates[cell._uuid].selected = !this.annotationUIStates[cell._uuid].selected
        this.updateSelectedCells()
      },