GridEditor.vue 52.3 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
10
    q-modal(v-model="modal", minimized, content-css="background-color: #eee; border-radius: .75rem;")
11
      grid-editor-editing-cells-mobile
12

christianrhansen's avatar
christianrhansen committed
13
    // ----------------------------------------------------------------------------------------------- "new cell"-header
14 15
    #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
16
        q-item-main
17
          strong New {{ newCellType }}
18

19
        q-item-side
20 21 22 23 24
          q-btn.text-white(
          @click="event => {addMobileCell(event)}", round, flat,
          style="background-color: rgba(0, 0, 0, 0);")
            q-icon(name="check")

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

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

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

38
          q-btn.text-white.q-px-sm.on-right.on-left(@click="event => {handleCellContextMenuDelete(event, mobileSelectedCell)}",
39
          flat)
40
            q-icon(name="delete")
41

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

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

56 57
      // context menu grid (desktop only)
      q-context-menu.desktop-only(ref="gridmenu")
Anton's avatar
Intial  
Anton committed
58 59
        q-list(link, separator, no-border, style="min-width: 150px; max-height: 300px;")
          q-item(
60 61 62 63 64
          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
65 66 67

      template(v-if="!resizingGrid")

68
        template(v-for="(annotation, index) in annotations")
christianrhansen's avatar
christianrhansen committed
69 70

          //----- cell
71
          // v-touch-hold="event => {cellHold(event, annotation)}",
Anton's avatar
Intial  
Anton committed
72
          .cell-item(
73 74 75 76 77 78 79 80 81 82
          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}`")

83
            //--------------------------------------------------------------------------------------- edit-/close-button
84
            // TODO: find a more elegant solution
85
            //-------------------------------------------------------------------------------------------------- desktop
86
            .desktop-only
Mathias Bär's avatar
Mathias Bär committed
87
              q-btn.edit-button.absolute-top-right(
88 89 90 91 92
              @click.prevent="event => {handleCellEditClick(event, annotation)}",
              :class="getAnnotationClasses(annotation._uuid, 'editing')",
              style="top: 8px; right: 8px;",
              :icon="annotationUIStates[annotation._uuid].editing ? 'close' : 'edit'", flat, round, size="md"
              )
93
            //--------------------------------------------------------------------------------------------------- mobile
94
            .mobile-only
95
              .edit-button.absolute.fit.bg-transparent(
96
              v-touch-hold="event => {handleCellEdit(event, annotation)}",
97
              @click.prevent="event => {touchMobileCell(event, annotation)}")
98

99 100 101
                //
                  q-btn.absolute-top-right.text-dark.q-pa-none.q-mr-xs(
                  v-if="showEditingCells",
102
                  @click.prevent="event => {handleCellEdit(event, annotation)}",
103 104
                  flat, round, size="sm", style="margin-top: 2px;")
                    q-icon(name="edit", size="20px")
christianrhansen's avatar
christianrhansen committed
105

106 107 108
              //----- invisible edit-modal handler
              //
                q-btn.absolute-top-left.bg-blue(
109
                @click.prevent="event => {handleCellEdit(event, annotation)}", flat, style="opacity: 0") bla
110
                // @click.prevent="handleModal()", flat, style="opacity: 0") bla
111

112
            //----- selecting cells disabled because it has no use currently
113 114 115 116 117 118 119 120
            // switch with cell component below to re-enable it
            //cell(
              @click.native.prevent="event => {handleCellClick(event, annotation)}", :annotation="annotation", :preview="true")
            cell(
            :annotation="annotation",
            :preview="true"
            )

christianrhansen's avatar
christianrhansen committed
121
            //----- resize-handler (desktop only)
122
            .desktop-only.cell-item-resize-handle(
123 124 125 126
            draggable="true",
            @dragstart="event => {handleCellResizerDragStart(event, annotation)}",
            @dragend="event => {handleCellResizerDragEnd(event, annotation)}",
            @dragexit="event => {handleCellResizerDragEnd(event, annotation)}")
127
              q-icon.q-ma-xs(name="network cell")
128

129 130 131
            //----- context menu for cells (desktop only)
            // TODO: needs revision
            q-context-menu.desktop-only
132 133 134 135 136 137 138
              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
139

140
        //----- temporary cell when pulling a new cell into grid (desktop)
141
        template(v-for="(tmpCell, index) in tmpObjects")
142
          .cell-item.cell-item-tmp(:style="getAnnotationStyle(tmpCell)", :key="`cell-tmp-${index}`")
Anton's avatar
Intial  
Anton committed
143 144
            cell(:cell="tmpCell")

christianrhansen's avatar
christianrhansen committed
145
        //-------------------------------------------------------------------------------------- temporary cell (mobile)
146
        template(v-if="mobileTempCell.show && cachedNewCell")
147

148 149 150
          .cell-item.cell-item-tmp-mobile.row.justify-center.items-center(
          ref="_mobileTempCell",
          :style="mobileTempCellStyle(mobileTempCell)")
151

christianrhansen's avatar
christianrhansen committed
152
            cell.absolute-top-left.q-ma-sm(:cell="cachedNewCell", :temp="true")
153

154 155 156 157 158
            //
              q-btn.text-white(v-if="mobileTempCell.button",
              @click="event => {addMobileCell(event)}", round, flat, size="lg",
              style="background-color: rgba(0, 0, 0, 0);")
                q-icon(name="check")
159

160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
        //------------------------------------------------------------------------------------------------- move handler
        //----- (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")

        //----------------------------------------------------------------------------------------------- resize handler
        //----- (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;")
198

christianrhansen's avatar
christianrhansen committed
199
      // ---------------------------------------------------------------------------------------------------------------
Anton's avatar
Intial  
Anton committed
200
      template(v-else)
201
        .cell-item(:style="getAnnotationStyle({x:0,y:0,width:1,height:1})", key="cell-grid-resizer")
Anton's avatar
Intial  
Anton committed
202
          div.cell-item-resize-handle(
203 204 205 206 207
          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
208

Mathias Bär's avatar
Mathias Bär committed
209
      //template(v-if="!isMobile")
210 211 212 213 214
        .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
215
      //template(v-if="isMobile")
Mathias Bär's avatar
Mathias Bär committed
216 217 218 219 220
        .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
221

christianrhansen's avatar
christianrhansen committed
222
    // ------------------------------------------------------------------------------------------ edit box (mobile only)
223

224
    q-page-sticky.edit-box.q-mx-md.q-mb-md.transition-bottom.backdrop-filter.shadow-6.overflow-hidden.hidden(
christianrhansen's avatar
christianrhansen committed
225
    v-if="isMobile",
226
    :class="[{'show-full' : carousel.visibility && showEditingCells}, {'show-minimized' : showEditingCells}]",
227
    style="border-radius: .5rem;",
228
    position="bottom-right")
christianrhansen's avatar
christianrhansen committed
229

230
      //------------------------- buttons
231 232
      // .row.text-dark(style="background-color: rgba(0, 0, 0, .2);")
      .row.text-dark(style="background-color: rgba(255, 150, 150, .3);")
233 234 235

        //----- delete cell
        .col
christianrhansen's avatar
christianrhansen committed
236
          q-btn.q-py-none.q-pl-sm.q-ml-xs(
237
            @click="event => {handleCellContextMenuDelete(event, mobileSelectedCell)}", flat, no-ripple)
christianrhansen's avatar
christianrhansen committed
238
            q-icon(name="delete")
christianrhansen's avatar
christianrhansen committed
239

240 241
        //----- toggle edit-box
        .col.text-right
christianrhansen's avatar
christianrhansen committed
242
          q-btn.q-py-none.q-pr-sm.q-mr-xs(@click="carouselVisibility()", flat, no-ripple)
christianrhansen's avatar
christianrhansen committed
243 244
            q-icon(v-if="carousel.visibility", name="keyboard_arrow_down")
            q-icon(v-else, name="keyboard_arrow_up")
245

christianrhansen's avatar
christianrhansen committed
246
      //q-item-separator.q-ma-none
247 248 249 250 251 252

      //------------------------- carousel
      q-carousel.q-py-sm.carousel.transition-opacity(ref="carousel", v-model="slide",
      infinite, @input="getCarouselIcon(index)", style="width: 180px;")

        //----- move
christianrhansen's avatar
christianrhansen committed
253 254
        // q-carousel-slide.q-py-sm
        q-carousel-slide.q-py-none
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
          .text-center
            q-btn.edit-cell-button(@click="mobileCellMove(mobileSelectedCell, 0, -1)", round, flat, size="md")
              q-icon.rotate-90(name="keyboard_backspace")
          .text-center
            q-btn.edit-cell-button(@click="mobileCellMove(mobileSelectedCell, -1, 0)", round, flat, size="md")
              q-icon(name="keyboard_backspace")
            q-btn.invisible.text-dark(round, flat, size="md")
              q-icon.flip-vertical(name="photo_size_select_small")
            q-btn.edit-cell-button(@click="mobileCellMove(mobileSelectedCell, 1, 0)", round, flat, size="md")
              q-icon.rotate-180(name="keyboard_backspace")
          .text-center
            q-btn.edit-cell-button(@click="mobileCellMove(mobileSelectedCell, 0, 1)", round, flat, size="md")
              q-icon.rotate-270(name="keyboard_backspace")

        //----- resize
christianrhansen's avatar
christianrhansen committed
270 271
        // q-carousel-slide.q-py-sm
        q-carousel-slide.q-py-none
272 273 274 275 276 277 278 279 280 281 282 283 284
          .text-center
            q-btn.edit-cell-button(@click="mobileCellResize(mobileSelectedCell, 0, -1)", round, flat, size="md")
              q-icon(name="remove")
          .text-center
            q-btn.edit-cell-button(@click="mobileCellResize(mobileSelectedCell, -1, 0)", round, flat, size="md")
              q-icon(name="remove")
            q-btn.invisible.text-dark(round, flat, size="md")
              q-icon.rotate-45(name="zoom_out_map")
            q-btn.edit-cell-button(@click="mobileCellResize(mobileSelectedCell, 1, 0)", round, flat, size="md")
              q-icon(name="add")
          .text-center
            q-btn.edit-cell-button(@click="mobileCellResize(mobileSelectedCell, 0, 1)", round, flat, size="md")
              q-icon(name="add")
Anton's avatar
Intial  
Anton committed
285 286 287 288
</template>

<script>
  import Cell from './Cell'
289 290
  import { userHasFeature } from 'mbjs-quasar/src/lib'
  import { mapGetters } from 'vuex'
291
  import GridEditorEditingCellsMobile from './/GridEditorEditingCellsMobile'
292
  import CellHandlerMobile from './CellHandlerMobile'
Anton's avatar
Intial  
Anton committed
293 294 295 296 297 298

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

  export default {
    components: {
299
      Cell,
300 301
      GridEditorEditingCellsMobile,
      CellHandlerMobile
Anton's avatar
Intial  
Anton committed
302
    },
303
    props: ['gridUuid', 'tabsAreOpen'],
Anton's avatar
Intial  
Anton committed
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
    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,
329
        annotations: undefined,
330
        tmpObjects: [],
331
        annotationUIStates: {},
Anton's avatar
Anton committed
332
        gridDimensions: { gridWidth: 0, gridHeight: 0, cellWidth: 0, cellHeight: 0 },
Anton's avatar
Intial  
Anton committed
333
        contextMenuClickPosition: {},
334
        resizingGrid: false,
christianrhansen's avatar
christianrhansen committed
335
        mobileSelectedCell: undefined,
336
        touch: {position: {top: undefined, left: undefined}},
337
        isMobile: this.$q.platform.is.mobile,
338 339 340 341 342 343 344 345 346 347 348 349
        mobileTempCell: {
          x: 0,
          y: 0,
          ox: 0,
          oy: 0,
          width: 1,
          height: 1,
          left: 0,
          show: false,
          onGrid: false,
          button: false
        },
350
        slide: undefined,
351
        modal: false,
352
        carousel: {visibility: false, icon: 'open_with', slide: 0},
christianrhansen's avatar
christianrhansen committed
353
        // cursor: {x: undefined, y: undefined},
354
        cellHandler: {
355
          size: {width: 40, height: 40},
356
          move: {x: 20, y: undefined, gridPosition: {row: undefined, column: undefined}, pushed: false, pos: undefined, inViewport: undefined, onLeft: undefined, onRight: undefined, dataLeft: undefined},
357
          resize: {x: undefined, y: undefined, gridPosition: {row: undefined, column: undefined}, pushed: false, pos: undefined}
358
        },
359
        tempHandler: {
christianrhansen's avatar
christianrhansen committed
360 361
          move: {left: undefined, intersectingMainHandler: false, side: undefined},
          resize: {left: undefined, intersectingMainHandler: false, side: undefined}
362
        },
363
        activeHandler: false,
364
        selectedCell: {type: undefined, annotation: undefined}
Anton's avatar
Intial  
Anton committed
365 366
      }
    },
367 368
    computed: {
      ...mapGetters({
christianrhansen's avatar
christianrhansen committed
369
        cachedNewCell: 'mosys/getNewCell',
370
        user: 'auth/getUserState',
371
        // isMobile: 'globalSettings/getIsMobile',
Mathias Bär's avatar
Mathias Bär committed
372
        // editingCells: 'mosys/getEditingCells'
373
        showEditingCells: 'mosys/getShowEditingCells',
374 375
        scrollPositionCache: 'mosys/getScrollPositionCache',
        editMode: 'mosys/getEditMode'
376
      }),
377 378 379 380 381 382
      newCellType () {
        if (this.cachedNewCell) {
          let type = this.cachedNewCell.component
          return type.substr(4, type.length - 4)
        }
      },
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
      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
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
      },
      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>")`
        }
421 422
      }
    },
Anton's avatar
Intial  
Anton committed
423 424
    async mounted () {
      await this.fetchData()
425
      this.resetScrollPosition()
426 427 428
    },
    beforeDestroy () {
      this.observer.disconnect()
Anton's avatar
Intial  
Anton committed
429 430
    },
    watch: {
431
      'cellHandler.move.gridPosition': {
432 433 434 435
        handler: function (obj) {
          if (this.showEditingCells) {
            this.mobileCellMove(this.mobileSelectedCell, obj.column, obj.row)
          }
436 437 438
        },
        deep: true
      },
439 440 441
      'cellHandler.resize.gridPosition': {
        handler: function (obj) {
          if (this.showEditingCells) {
442 443 444
            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)
445 446 447 448
          }
        },
        deep: true
      },
449 450 451 452 453 454 455 456 457 458
      slide (val) {
        let icon
        switch (val) {
        case 0:
          icon = 'open_with'
          break
        case 1:
          icon = 'photo_size_select_small'
          break
        case 2:
459
          icon = 'more_horiz'
460 461 462 463 464
          break
        default:
          icon = 'open_with'
          break
        }
465 466 467
        // this.carousel.slide = val
        // this.carousel.icon = icon
        this.carousel = {slide: val, icon: icon, visibility: this.carousel.visibility}
468
      },
Anton's avatar
Anton committed
469
      annotations () {
470
        this.updateAnnotationUIStates()
Anton's avatar
Intial  
Anton committed
471 472 473 474 475 476
      },
      gridMetadata () {
        this.updateGridDimensions()
      },
      async gridUuid () {
        await this.fetchData()
Mathias Bär's avatar
Mathias Bär committed
477 478
      },
      showEditingCells (val) {
479
        // console.log('show editing cells', val)
Mathias Bär's avatar
Mathias Bär committed
480 481 482
        if (val === false) {
          this.updateAnnotationUIStates()
        }
483 484 485
      },
      tabsAreOpen () {
        this.resetScrollPosition()
Anton's avatar
Intial  
Anton committed
486 487 488
      }
    },
    methods: {
489 490 491 492 493 494 495 496
      clearHandler (target) {
        switch (target) {
        case 'cell':
          Object.keys(this.annotationUIStates).filter((k) => {
            this.annotationUIStates[k].editing = false
          })
          this.$store.commit('mosys/setEditingCells', '')
          break
497 498
        case 'cached cell':
          this.$store.commit('mosys/cacheNewCell', undefined)
499 500 501 502
          this.clearHandler('temp cell')
          break
        case 'temp cell':
          this.mobileTempCell = {x: undefined, y: undefined, width: undefined, height: undefined, onGrid: false, button: false}
503
          break
504 505
        }
      },
506
      intersectionChanged (obj) {
christianrhansen's avatar
christianrhansen committed
507 508
        let _offsetLeft = Math.sign(obj.offsetLeft)

509
        // -------------------- move
510
        if (obj.element === 'move') {
christianrhansen's avatar
christianrhansen committed
511
          this.tempHandler.move.intersectingMainHandler = obj.intersecting
512

christianrhansen's avatar
christianrhansen committed
513
          // behind left side
christianrhansen's avatar
christianrhansen committed
514
          if (_offsetLeft < 0) {
515
            this.tempHandler.move.side = 'left'
516
          }
517
          // intersecting
christianrhansen's avatar
christianrhansen committed
518
          else if (isNaN(_offsetLeft)) {
christianrhansen's avatar
christianrhansen committed
519
            if (this.tempHandler.move.intersectingMainHandler && this.tempHandler.move.side === 'right') this.tempHandler.resize.intersectingMainHandler = false
520
          }
christianrhansen's avatar
christianrhansen committed
521
          // behind right side
christianrhansen's avatar
christianrhansen committed
522
          else if (_offsetLeft === 1) {
523
            this.tempHandler.move.side = 'right'
christianrhansen's avatar
christianrhansen committed
524
            this.tempHandler.resize.intersectingMainHandler = true
525
          }
526
        }
527

528
        // -------------------- resize
christianrhansen's avatar
christianrhansen committed
529
        else if (obj.element === 'resize') {
christianrhansen's avatar
christianrhansen committed
530
          this.tempHandler.resize.intersectingMainHandler = obj.intersecting
531

christianrhansen's avatar
christianrhansen committed
532
          // on left side
christianrhansen's avatar
christianrhansen committed
533
          if (_offsetLeft === -1) {
534
            this.tempHandler.resize.side = 'left'
535
          }
christianrhansen's avatar
christianrhansen committed
536
          /*
537
          // intersecting
christianrhansen's avatar
christianrhansen committed
538
          else if (isNaN(Math.sign(obj.offsetLeft))) {
539
          }
christianrhansen's avatar
christianrhansen committed
540
          */
christianrhansen's avatar
christianrhansen committed
541
          // on right side
christianrhansen's avatar
christianrhansen committed
542
          else if (_offsetLeft === 1) {
543
            this.tempHandler.resize.side = 'right'
544
          }
545 546
        }
      },
christianrhansen's avatar
christianrhansen committed
547
      handleMoveCell (obj) {
548 549
        this.cellHandler.move.x = obj.position.left
        this.cellHandler.move.y = obj.position.top
550

christianrhansen's avatar
christianrhansen committed
551 552
        let res = this.getGridPositionForEvent(obj)

553 554 555
        this.cellHandler.move.gridPosition.row = res.y
        this.cellHandler.move.gridPosition.column = res.x

christianrhansen's avatar
christianrhansen committed
556
        if (obj.isFirst) {
557
          this.activeHandler = true
christianrhansen's avatar
christianrhansen committed
558
          this.mobileTempCell.show = true
559
          // this.mobileTempCell.button = false
560
          this.cellHandler.move.pushed = true
christianrhansen's avatar
christianrhansen committed
561
        }
562

christianrhansen's avatar
christianrhansen committed
563 564 565
        this.mobileTempCell.x = res.x
        this.mobileTempCell.y = res.y

566 567 568
        this.mobileTempCell.ox = res.ox
        this.mobileTempCell.oy = res.oy

569 570
        this.cellHandler.move.x = res.ox
        this.cellHandler.move.y = res.oy
571

christianrhansen's avatar
christianrhansen committed
572
        if (obj.isFinal) {
573
          this.activeHandler = false
christianrhansen's avatar
christianrhansen committed
574
          this.mobileTempCell.onGrid = true
575
          // this.mobileTempCell.button = true
576
          this.mobileTempCell.left = this.gridDimensions.full.cell.width * (res.x - 1)
577 578 579 580
          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
581

582 583
          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
584 585
        }
      },
christianrhansen's avatar
christianrhansen committed
586
      handleResizeCell (obj) {
christianrhansen's avatar
christianrhansen committed
587
        // this.cursor = {x: obj.position.left, y: obj.position.top}
588 589 590
        // 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
591

592 593 594
        // ---> panGrid
        let res = this.getGridPositionForEvent(obj)

595 596 597
        this.cellHandler.resize.gridPosition.row = res.y
        this.cellHandler.resize.gridPosition.column = res.x

598
        if (obj.isFirst) {
599
          this.activeHandler = true
600
          this.mobileTempCell.show = true
601
          // this.mobileTempCell.button = false
602
          this.cellHandler.resize.pushed = true
603 604 605 606 607
        }

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

608 609
        this.cellHandler.resize.x = res.ox
        this.cellHandler.resize.y = res.oy
610

611
        if (obj.isFinal) {
612
          this.activeHandler = false
613
          this.mobileTempCell.onGrid = true
614
          // this.mobileTempCell.button = true
615
          this.cellHandler.resize.pushed = false
616 617
          // this.addMobileCell(obj)
          // this.mobileTempCell.show = false
618 619
          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
620 621
        }
      },
622 623
      handleCellEdit (event, annotation) {
        console.log('handleCellEdit', annotation)
624 625 626
        this.touchMobileCell(event, annotation)
        this.handleModal()
      },
627 628 629 630
      getCarouselIcon (val) {
        console.log(val)
      },
      carouselVisibility () {
631 632 633 634
        console.log(this.carousel.slide)
        if (this.$refs.carousel) {
          this.$refs.carousel.goToSlide(this.carousel.slide)
        }
635 636
        this.carousel.visibility = !this.carousel.visibility
      },
637 638 639
      handleModal () {
        this.modal = !this.modal
      },
640
      panGrid (obj) {
641
        if (this.cachedNewCell && !this.mobileTempCell.onGrid) {
642
          // console.log('hhhhhhhhhhhhhhhhh', obj)
643 644 645 646
          let res = this.getGridPositionForEvent(obj)

          if (obj.isFirst) {
            this.mobileTempCell.show = true
647
            // this.mobileTempCell.button = false
648 649 650 651 652 653 654 655
            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) {
656
            this.mobileTempCell.onGrid = true
657
            // this.mobileTempCell.button = true
658 659
            // this.addMobileCell(obj)
            // this.mobileTempCell.show = false
660 661
            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
662

663 664
            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
665
          }
666
        }
667
      },
668
      async cellHold (event, annotation) {
669 670
        this.$store.commit('mosys/setEditingCells', '')

671
        this.$q.notify({
672
          message: 'Copied.',
673
          color: 'dark',
674 675
          textColor: 'white',
          position: 'bottom-right'
676 677 678 679 680 681 682 683 684 685
        })

        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
686 687
      moveCachedCell (obj) {
        this.touch.position = {top: obj.position.top, left: obj.position.left}
688 689 690 691 692 693 694 695 696
        /*
        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
697
      },
698
      addMobileCell (event) {
christianrhansen's avatar
christianrhansen committed
699
        if (this.cachedNewCell) {
700
          this.handleGridDrop(event)
christianrhansen's avatar
christianrhansen committed
701
          this.$store.commit('mosys/cacheNewCell', undefined)
702 703
          this.$q.notify({
            message: 'Cell was added.',
704 705 706
            color: 'dark',
            position: 'center',
            timeout: 50
707
          })
christianrhansen's avatar
christianrhansen committed
708 709
        }
      },
710
      touchMobileCell (event, cell) {
711
        if (!this.mobileTempCell.onGrid && cell) {
712 713 714 715 716 717 718 719 720
          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)
        }
721
      },
christianrhansen's avatar
christianrhansen committed
722
      handleCellTouch (event, annotation) {
723
        console.log('handleCellTouch', annotation)
724
        if (!this.cachedNewCell) {
725 726 727 728 729 730 731 732 733 734 735 736 737 738
          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

739
          this.mobileSelectedCell = annotation
740
          this.getCellType(annotation)
741
        }
christianrhansen's avatar
christianrhansen committed
742
      },
743 744 745 746 747
      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)
      },
748 749 750
      // async mobileCellMove (annotation, _x, _y) {
      async mobileCellMove (annotation, x, y) {
        // this.$el.scrollLeft = this.$el.scrollLeft + (this.gridDimensions.full.cell.width * _x)
751

752
        let
753 754
          parsed = annotation.target.selector.parse()
          /*
755
          sliced = parsed.xywh.slice(0, 4),
756
          x = sliced[0],
757 758 759
          y = sliced[1],
          w = sliced[2],
          h = sliced[3]
760
          */
761

762
        /*
763 764 765 766 767 768 769
        // ----- 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
770
        */
771 772 773 774

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

775 776 777 778 779 780 781 782
        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 }
          }
        }])
      },
783
      async mobileCellResize (annotation, _w, _h) {
784 785 786
        let
          parsed = annotation.target.selector.parse(),
          [x, y, w, h] = parsed.xywh
787 788
        console.log('mobileCellResize', w, h)
        /*
789 790 791 792 793 794 795
        // ----- 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
796
        */
797 798 799 800

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

801
        const value = { xywh: [x, y, _w, _h] }
802 803 804 805 806 807 808
        annotation.target.selector.value = value

        await this.$store.dispatch('annotations/patch', [annotation.id, { target: { selector: { value } } }])
      },
      setEditMode (mode) {
        this.$store.commit('mosys/setEditMode', mode)
      },
809 810 811 812
      //
      // DATA
      //

Anton's avatar
Intial  
Anton committed
813 814 815
      async fetchData () {
        if (this.gridUuid) {
          this.grid = await this.$store.dispatch('maps/get', this.gridUuid)
816 817 818 819 820 821 822 823
          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
824
          this.updateGridDimensions()
825 826 827 828 829 830
          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
831 832
        }
      },
833 834 835
      async updateGridMetadataStore () {
        await this.$store.dispatch('maps/patch', [this.grid.id, { config: this.grid.config }])
        this.updateGridDimensions()
Anton's avatar
Intial  
Anton committed
836
      },
837 838 839 840 841
      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
842 843
          // return _this.cells.find(c => c._uuid === k)
          return _this.annotations.find(c => c._uuid === k)
844 845
        })
        this.$store.commit('mosys/setSelectedCells', selectedCells)
Anton's avatar
Intial  
Anton committed
846
      },
Mathias Bär's avatar
Mathias Bär committed
847 848 849 850 851 852 853 854
      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)
        })
855 856 857 858 859 860 861
        /*
        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
862 863
        this.$store.commit('mosys/setEditingCells', editingCells)
      },
864 865 866 867 868
      updateAnnotationUIStates () {
        let newAnnotationUIStates = {}
        this.annotations.forEach(a => {
          newAnnotationUIStates[a._uuid] = {
            selected: false,
Mathias Bär's avatar
Mathias Bär committed
869
            editing: false,
870
            beingResized: false,
Mathias Bär's avatar
Mathias Bär committed
871
            beginDragged: false,
872
            annotation: a
Anton's avatar
Intial  
Anton committed
873
          }
874 875
        })
        this.annotationUIStates = newAnnotationUIStates
Anton's avatar
Intial  
Anton committed
876
        this.updateSelectedCells()
Mathias Bär's avatar
Mathias Bär committed
877
        this.updateEditingCells()
878
      },
879 880 881 882 883

      //
      // GRID DRAG & DROP HANDLERS
      //

884
      async handleGridDragOver (event) {
Anton's avatar
Intial  
Anton committed
885 886 887
        let _this = this
        if (this.resizingGrid) {
          const position = this.getGridPositionForEvent(event)
888 889
          this.grid.config.ratio = position.ox / (position.oy * 1.0)
          await this.updateGridMetadataStore()
Anton's avatar
Intial  
Anton committed
890 891
        }
        else {
892 893
          let annotation = this.annotations.filter(annotation => {
            if (!_this.annotationUIStates[annotation._uuid]) return false
Mathias Bär's avatar
Mathias Bär committed
894
            return _this.annotationUIStates[annotation._uuid].beingDragged ||
895
              _this.annotationUIStates[annotation._uuid].beingResized
Anton's avatar
Intial  
Anton committed
896 897
          }).shift()
          let offset, position
898 899 900 901
          if (!annotation) {
            annotation = {
              target: this.grid.get2DArea([1, 1], [1, 1])
            }
Anton's avatar
Intial  
Anton committed
902 903 904
            position = this.getGridPositionForEvent(event)
          }
          else {
905
            offset = this.annotationUIStates[annotation._uuid].draggingOffset
Anton's avatar
Intial  
Anton committed
906 907
            position = this.getGridPositionForEvent(event, offset)
          }
908 909
          if (!this.tmpObjects.length) this.tmpObjects.push(annotation)
          const parsed = annotation.target.selector.parse()
Anton's avatar
Intial  
Anton committed
910
          if (event.dataTransfer.types.includes('text/plain')) {
911 912
            parsed.xywh[0] = position.x
            parsed.xywh[1] = position.y
Anton's avatar
Intial  
Anton committed
913 914 915
            event.preventDefault()
          }
          else {
916 917
            parsed.xywh[2] = Math.max(1, 1 + position.x - parsed.xywh[0])