GridEditor.vue 47.4 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)
      },
christianrhansen's avatar
christianrhansen committed
570 571
      moveCachedCell (obj) {
        this.touch.position = {top: obj.position.top, left: obj.position.left}
572 573 574 575 576 577 578 579 580
        /*
        if (obj.isFinal) {
          console.log(obj.isFinal)
          console.log(window.event)
          console.log(this.touch.position)
          console.log('--->->->', this.getGridPositionForEvent(event))
        }
        */
        // console.log('--->>>', this.getGridPositionForEvent(event))
christianrhansen's avatar
christianrhansen committed
581
      },
582
      addMobileCell (event) {
christianrhansen's avatar
christianrhansen committed
583
        if (this.cachedNewCell) {
584
          this.handleGridDrop(event)
christianrhansen's avatar
christianrhansen committed
585
          this.$store.commit('mosys/cacheNewCell', undefined)
586 587
          this.$q.notify({
            message: 'Cell was added.',
588 589 590
            color: 'dark',
            position: 'center',
            timeout: 50
591
          })
christianrhansen's avatar
christianrhansen committed
592 593
        }
      },
594
      touchMobileCell (event, cell) {
595
        if (!this.mobileTempCell.onGrid && cell) {
596 597 598 599 600 601 602 603 604
          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)
        }
605
      },
christianrhansen's avatar
christianrhansen committed
606
      handleCellTouch (event, annotation) {
607
        console.log('handleCellTouch', annotation)
608
        if (!this.cachedNewCell) {
609 610 611 612 613 614 615 616 617 618 619 620 621 622
          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

623
          this.mobileSelectedCell = annotation
624
          this.getCellType(annotation)
625
        }
christianrhansen's avatar
christianrhansen committed
626
      },
627 628 629 630 631
      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)
      },
632 633 634
      // async mobileCellMove (annotation, _x, _y) {
      async mobileCellMove (annotation, x, y) {
        // this.$el.scrollLeft = this.$el.scrollLeft + (this.gridDimensions.full.cell.width * _x)
635

636
        let
637 638
          parsed = annotation.target.selector.parse()
          /*
639
          sliced = parsed.xywh.slice(0, 4),
640
          x = sliced[0],
641 642 643
          y = sliced[1],
          w = sliced[2],
          h = sliced[3]
644
          */
645

646
        /*
647 648 649 650 651 652 653
        // ----- x
        if (_x === -1) if (x > 1) x += _x
        if (_x === 1 && x < this.grid.config.columns - w + 1) x += _x

        // ----- y
        if (_y === -1) if (y > 1) y += _y
        if (_y === 1 && y < this.grid.config.rows - h + 1) y += _y
654
        */
655 656 657 658

        // x += _x
        // y += _y

659 660 661 662 663 664 665 666
        let target = this.grid.get2DArea([x, y], parsed.xywh.slice(2))
        annotation.target.selector.value = target.selector.value
        await this.$store.dispatch('annotations/patch', [annotation.id, {
          target: {
            selector: { value: target.selector.value }
          }
        }])
      },
667
      async mobileCellResize (annotation, _w, _h) {
668 669 670
        let
          parsed = annotation.target.selector.parse(),
          [x, y, w, h] = parsed.xywh
671 672
        console.log('mobileCellResize', w, h)
        /*
673 674 675 676 677 678 679
        // ----- w
        if (_w === -1) if (w > 1) w += _w
        if (_w === 1 && x < this.grid.config.columns - w + 1) w += _w

        // ----- h
        if (_h === -1) if (h > 1) h += _h
        if (_h === 1 && y < this.grid.config.rows - h + 1) h += _h
680
        */
681 682 683 684

        // w += _w
        // h += _h

685
        const value = { xywh: [x, y, _w, _h] }
686 687 688 689 690 691 692
        annotation.target.selector.value = value

        await this.$store.dispatch('annotations/patch', [annotation.id, { target: { selector: { value } } }])
      },
      setEditMode (mode) {
        this.$store.commit('mosys/setEditMode', mode)
      },
693 694 695 696
      //
      // DATA
      //

Anton's avatar
Intial  
Anton committed
697 698 699
      async fetchData () {
        if (this.gridUuid) {
          this.grid = await this.$store.dispatch('maps/get', this.gridUuid)
700 701 702 703 704 705 706 707
          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
708
          this.updateGridDimensions()
709 710 711 712 713 714
          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
715 716
        }
      },
717 718 719
      async updateGridMetadataStore () {
        await this.$store.dispatch('maps/patch', [this.grid.id, { config: this.grid.config }])
        this.updateGridDimensions()
Anton's avatar
Intial  
Anton committed
720
      },
721 722 723 724 725
      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
726 727
          // return _this.cells.find(c => c._uuid === k)
          return _this.annotations.find(c => c._uuid === k)
728 729
        })
        this.$store.commit('mosys/setSelectedCells', selectedCells)
Anton's avatar
Intial  
Anton committed
730
      },
Mathias Bär's avatar
Mathias Bär committed
731 732 733 734 735 736 737 738
      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)
        })
739 740 741 742 743 744 745
        /*
        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
746 747
        this.$store.commit('mosys/setEditingCells', editingCells)
      },
748 749 750 751 752
      updateAnnotationUIStates () {
        let newAnnotationUIStates = {}
        this.annotations.forEach(a => {
          newAnnotationUIStates[a._uuid] = {
            selected: false,
Mathias Bär's avatar
Mathias Bär committed
753
            editing: false,
754
            beingResized: false,
Mathias Bär's avatar
Mathias Bär committed
755
            beginDragged: false,
756
            annotation: a
Anton's avatar
Intial  
Anton committed
757
          }
758 759
        })
        this.annotationUIStates = newAnnotationUIStates
Anton's avatar
Intial  
Anton committed
760
        this.updateSelectedCells()
Mathias Bär's avatar
Mathias Bär committed
761
        this.updateEditingCells()
762
      },
763 764 765 766 767

      //
      // GRID DRAG & DROP HANDLERS
      //

768
      async handleGridDragOver (event) {
Anton's avatar
Intial  
Anton committed
769 770 771
        let _this = this
        if (this.resizingGrid) {
          const position = this.getGridPositionForEvent(event)
772 773
          this.grid.config.ratio = position.ox / (position.oy * 1.0)
          await this.updateGridMetadataStore()
Anton's avatar
Intial  
Anton committed
774 775
        }
        else {
776 777
          let annotation = this.annotations.filter(annotation => {
            if (!_this.annotationUIStates[annotation._uuid]) return false
Mathias Bär's avatar
Mathias Bär committed
778
            return _this.annotationUIStates[annotation._uuid].beingDragged ||
779
              _this.annotationUIStates[annotation._uuid].beingResized
Anton's avatar
Intial  
Anton committed
780 781
          }).shift()
          let offset, position
782 783 784 785
          if (!annotation) {
            annotation = {
              target: this.grid.get2DArea([1, 1], [1, 1])
            }
Anton's avatar
Intial  
Anton committed
786 787 788
            position = this.getGridPositionForEvent(event)
          }
          else {
789
            offset = this.annotationUIStates[annotation._uuid].draggingOffset
Anton's avatar
Intial  
Anton committed
790 791
            position = this.getGridPositionForEvent(event, offset)
          }
792 793
          if (!this.tmpObjects.length) this.tmpObjects.push(annotation)
          const parsed = annotation.target.selector.parse()
Anton's avatar
Intial  
Anton committed
794
          if (event.dataTransfer.types.includes('text/plain')) {
795 796
            parsed.xywh[0] = position.x
            parsed.xywh[1] = position.y
Anton's avatar
Intial  
Anton committed
797 798 799
            event.preventDefault()
          }
          else {
800 801
            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
802
          }
803
          annotation.target.selector.value = parsed
Anton's avatar
Intial  
Anton committed
804 805 806
        }
      },
      handleGridDragEnd () {
807
        this.tmpObjects = []
Anton's avatar
Intial  
Anton committed
808
      },
809
      async handleGridDrop (event) {
810 811 812 813 814
        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)
815 816
        if (dropData) {
          dropData = JSON.parse(dropData)
817
          let annotation = this.annotations.find(a => a.id === dropData.id)
818 819 820 821 822
          const { x, y } = this.getGridPositionForEvent(
            event,
            annotation ? this.annotationUIStates[annotation._uuid].draggingOffset : undefined
          )
          if (annotation) {
823 824 825
            const
              parsed = annotation.target.selector.parse(),
              target = this.grid.get2DArea([x, y], parsed.xywh.slice(2))
826 827 828 829 830 831
            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
832 833
          }
          else {
834 835
            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])
836 837 838 839 840 841 842 843 844 845 846
            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
                }
              },
847 848
              // target: this.grid.get2DArea([x, y], [1, 1])
              target: test
849
            })
850
            this.annotations.push(annotation)
Anton's avatar
Anton committed
851
            this.updateAnnotationUIStates()
Anton's avatar
Intial  
Anton committed
852
          }
853

854
          this.tmpObjects = []
Anton's avatar
Intial  
Anton committed
855 856
          event.preventDefault()
        }
857
        this.clearHandler('temp cell')
Anton's avatar
Intial  
Anton committed
858
      },
859 860 861 862

      //
      // CELL DRAG & DROP HANDLERS
      //
Mathias Bär's avatar
Mathias Bär committed
863 864 865
      handleCellEditClick (event, cell) {
        this.annotationUIStates[cell._uuid].editing = !this.annotationUIStates[cell._uuid].editing
        this.updateEditingCells()
866
        this.$root.$emit('mosys_saveScrollPosition')
Mathias Bär's avatar
Mathias Bär committed
867
      },
868
      /*
869 870 871 872
      handleCellClick (event, cell) {
        this.annotationUIStates[cell._uuid].selected = !this.annotationUIStates[cell._uuid].selected
        this.updateSelectedCells()
      },
873
      */
874
      handleCellDragStart (event, annotation) {
christianrhansen's avatar
christianrhansen committed
875
        console.log('§§§§§§§§', annotation)
876 877 878 879 880 881 882 883 884 885 886 887
        if (!this.isMobile) {
          if (!this.annotationUIStates[annotation._uuid].beingResized) {
            event.dataTransfer.setData('text/plain', JSON.stringify(annotation))
            event.dataTransfer.setDragImage(nullImage, 0, 0)
            let elContainerBoundingBox = this.$el.getBoundingClientRect()
            let elBoundingBox = event.target.getBoundingClientRect()
            let offset = {
              x: (event.clientX - elContainerBoundingBox.x) - (elBoundingBox.x - elContainerBoundingBox.x),
              y: (event.clientY - elContainerBoundingBox.y) - (elBoundingBox.y - elContainerBoundingBox.y)
            }
            this.annotationUIStates[annotation._uuid].draggingOffset = offset
            this.annotationUIStates[annotation._uuid].beingDragged = true
888
          }
889
          this.tmpObjects.push(annotation)
890 891 892
        }
      },
      handleCellDragEnd (event, annotation) {
893 894 895
        if (!this.mobile) {
          this.annotationUIStates[annotation._uuid].beingDragged = false
        }
896 897 898 899
      },
      async handleCellContextMenuDelete (event, annotation) {
        this.annotationUIStates[annotation._uuid].selected = false
        this.updateSelectedCells()
Mathias Bär's avatar
Mathias Bär committed