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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

      //
      // GRID DRAG & DROP HANDLERS
      //

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

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

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