GridEditor.vue 52.7 KB
Newer Older
Anton's avatar
Intial  
Anton committed
1 2
<template lang="pug">

3
  // :class="{'overflow-hidden': mobileTempCell.show && cachedNewCell && !mobileTempCell.button}",
4
  div.cell-grid-container(
5
  :class="{'overflow-hidden': activeHandler}",
6
  style="overflow-y: hidden; scroll-behavior: smooth;")
7
    q-modal(v-model="modal", minimized, content-css="background-color: #eee; border-radius: .75rem;")
8
      grid-editor-editing-cells-mobile
9 10 11
      //
        q-btn.absolute-top-right.q-ma-sm.bg-dark(@click="handleModal()", round, size="sm", flat)
          q-icon(name="clear")
12

13 14 15 16 17 18
    // this needs to stay for adding cells on mobile for whatever reason (strangest bug ever...)
    // FIXME: find the problem and fix it
    .mobile-only.fixed-top-left.q-caption.z-max.hidden
      .bg-red {{ cachedNewCell }}
      .bg-green {{ mobileTempCell }}

19
    q-window-resize-observable(@resize="updateGridDimensions")
Anton's avatar
Intial  
Anton committed
20

christianrhansen's avatar
christianrhansen committed
21
    // ------------------------------------------------------------------------------------------------- new cell header
22 23
    #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
24
        q-item-main
25
          strong New {{ newCellType }}
26

27
        q-item-side
28 29 30 31 32
          q-btn.text-white(
          @click="event => {addMobileCell(event)}", round, flat,
          style="background-color: rgba(0, 0, 0, 0);")
            q-icon(name="check")

33 34
          q-btn.text-white.q-px-md.q-mr-md.on-right(@click="clearHandler('cached cell')", flat,
            style="border-radius: .5rem; width: 42px; border: 1px solid rgba(255, 255, 255, .3);")
christianrhansen's avatar
christianrhansen committed
35
            q-icon(name="clear")
36

37 38
    // -------------------------------------------------------------------------------------------- selected cell header
    #selected-cell-header.fixed-top.z-top.bg-dark.text-white.transition(:class="{'show': showEditingCells}")
39
      q-item.q-pl-md.q-pr-none.q-py-none.full-height(v-if="!modal")
40
        q-item-main
41
          strong {{ selectedCell.type }}
42

43
        q-item-side
44
          q-btn.text-white.q-px-sm(@click="event => {handleCellInfoTouch(event, selectedCell.annotation)}", flat)
45 46
            q-icon(name="edit")

47
          q-btn.text-white.q-px-sm.on-right.on-left(@click="event => {handleCellContextMenuDelete(event, mobileSelectedCell)}",
48
          flat)
49
            q-icon(name="delete")
50

51
          q-btn.text-white.q-px-md.q-mr-md.on-right(@click="clearHandler('cell')", flat,
52 53 54
          style="border-radius: .5rem; width: 42px; border: 1px solid rgba(255, 255, 255, .3);")
            q-icon(name="clear")

55
    // ------------------------------------------------------------------------------------------------------------ grid
56
      @click="event => {addMobileCell(event)}",
57
    div.cell-grid.relative-position(
58
    v-touch-pan="panGrid",
59 60 61 62 63 64
    @dragenter="handleGridDragOver",
    @dragover="handleGridDragOver",
    @dragleave="handleGridDragEnd",
    @drop="handleGridDrop",
    @contextmenu="handleGridContextMenu",
    :style="gridStyle")
Anton's avatar
Intial  
Anton committed
65

66 67
      // context menu grid (desktop only)
      q-context-menu.desktop-only(ref="gridmenu")
Anton's avatar
Intial  
Anton committed
68 69
        q-list(link, separator, no-border, style="min-width: 150px; max-height: 300px;")
          q-item(
70 71 72 73 74
          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
75 76 77

      template(v-if="!resizingGrid")

78
        template(v-for="(annotation, index) in annotations")
christianrhansen's avatar
christianrhansen committed
79 80

          //----- cell
81
          // v-touch-hold="event => {cellHold(event, annotation)}",
Anton's avatar
Intial  
Anton committed
82
          .cell-item(
83 84 85 86 87 88 89 90 91 92
          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}`")

93
            //--------------------------------------------------------------------------------------- edit-/close-button
94
            // TODO: find a more elegant solution
95
            //-------------------------------------------------------------------------------------------------- desktop
96
            .desktop-only
Mathias Bär's avatar
Mathias Bär committed
97
              q-btn.edit-button.absolute-top-right(
98 99 100 101 102
              @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"
              )
103
            //--------------------------------------------------------------------------------------------------- mobile
104
            .mobile-only
105 106 107
              .edit-button.absolute.fit.bg-transparent(
              v-touch-hold="event => {handleCellInfoTouch(event, annotation)}",
              @click.prevent="event => {touchMobileCell(event, annotation)}")
108

109 110 111 112 113 114
                //
                  q-btn.absolute-top-right.text-dark.q-pa-none.q-mr-xs(
                  v-if="showEditingCells",
                  @click.prevent="event => {handleCellInfoTouch(event, annotation)}",
                  flat, round, size="sm", style="margin-top: 2px;")
                    q-icon(name="edit", size="20px")
christianrhansen's avatar
christianrhansen committed
115

116 117 118 119 120
              //----- invisible edit-modal handler
              //
                q-btn.absolute-top-left.bg-blue(
                @click.prevent="event => {handleCellInfoTouch(event, annotation)}", flat, style="opacity: 0") bla
                // @click.prevent="handleModal()", flat, style="opacity: 0") bla
121

122
            //----- selecting cells disabled because it has no use currently
123 124 125 126 127 128 129 130
            // 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
131
            //----- resize-handler (desktop only)
132
            .desktop-only.cell-item-resize-handle(
133 134 135 136
            draggable="true",
            @dragstart="event => {handleCellResizerDragStart(event, annotation)}",
            @dragend="event => {handleCellResizerDragEnd(event, annotation)}",
            @dragexit="event => {handleCellResizerDragEnd(event, annotation)}")
137
              q-icon.q-ma-xs(name="network cell")
138

139 140 141
            //----- context menu for cells (desktop only)
            // TODO: needs revision
            q-context-menu.desktop-only
142 143 144 145 146 147 148
              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
149

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

christianrhansen's avatar
christianrhansen committed
155
        //-------------------------------------------------------------------------------------- temporary cell (mobile)
156
        template(v-if="mobileTempCell.show && cachedNewCell")
157

158 159 160
          .cell-item.cell-item-tmp-mobile.row.justify-center.items-center(
          ref="_mobileTempCell",
          :style="mobileTempCellStyle(mobileTempCell)")
161

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

164 165 166 167 168
            //
              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")
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 198 199 200 201 202 203 204 205 206 207
        //------------------------------------------------------------------------------------------------- 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;")
208

christianrhansen's avatar
christianrhansen committed
209
      // ---------------------------------------------------------------------------------------------------------------
Anton's avatar
Intial  
Anton committed
210
      template(v-else)
211
        .cell-item(:style="getAnnotationStyle({x:0,y:0,width:1,height:1})", key="cell-grid-resizer")
Anton's avatar
Intial  
Anton committed
212
          div.cell-item-resize-handle(
213 214 215 216 217
          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
218

Mathias Bär's avatar
Mathias Bär committed
219
      //template(v-if="!isMobile")
220 221 222 223 224
        .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
225
      //template(v-if="isMobile")
Mathias Bär's avatar
Mathias Bär committed
226 227 228 229 230
        .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
231

christianrhansen's avatar
christianrhansen committed
232
    // ------------------------------------------------------------------------------------------ edit box (mobile only)
233

234
    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
235
    v-if="isMobile",
236
    :class="[{'show-full' : carousel.visibility && showEditingCells}, {'show-minimized' : showEditingCells}]",
237
    style="border-radius: .5rem;",
238
    position="bottom-right")
christianrhansen's avatar
christianrhansen committed
239

240
      //------------------------- buttons
241 242
      // .row.text-dark(style="background-color: rgba(0, 0, 0, .2);")
      .row.text-dark(style="background-color: rgba(255, 150, 150, .3);")
243 244 245

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

250 251
        //----- toggle edit-box
        .col.text-right
christianrhansen's avatar
christianrhansen committed
252
          q-btn.q-py-none.q-pr-sm.q-mr-xs(@click="carouselVisibility()", flat, no-ripple)
christianrhansen's avatar
christianrhansen committed
253 254
            q-icon(v-if="carousel.visibility", name="keyboard_arrow_down")
            q-icon(v-else, name="keyboard_arrow_up")
255

christianrhansen's avatar
christianrhansen committed
256
      //q-item-separator.q-ma-none
257 258 259 260 261 262

      //------------------------- 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
263 264
        // q-carousel-slide.q-py-sm
        q-carousel-slide.q-py-none
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
          .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
280 281
        // q-carousel-slide.q-py-sm
        q-carousel-slide.q-py-none
282 283 284 285 286 287 288 289 290 291 292 293 294
          .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
295 296 297 298
</template>

<script>
  import Cell from './Cell'
299 300
  import { userHasFeature } from 'mbjs-quasar/src/lib'
  import { mapGetters } from 'vuex'
301
  import GridEditorEditingCellsMobile from './/GridEditorEditingCellsMobile'
302
  import CellHandlerMobile from './CellHandlerMobile'
Anton's avatar
Intial  
Anton committed
303 304 305 306 307 308

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

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

518
        // -------------------- move
519
        if (obj.element === 'move') {
christianrhansen's avatar
christianrhansen committed
520
          this.tempHandler.move.intersectingMainHandler = obj.intersecting
521

christianrhansen's avatar
christianrhansen committed
522
          // behind left side
christianrhansen's avatar
christianrhansen committed
523
          if (_offsetLeft < 0) {
524
            this.tempHandler.move.side = 'left'
525
          }
526
          // intersecting
christianrhansen's avatar
christianrhansen committed
527
          else if (isNaN(_offsetLeft)) {
christianrhansen's avatar
christianrhansen committed
528
            if (this.tempHandler.move.intersectingMainHandler && this.tempHandler.move.side === 'right') this.tempHandler.resize.intersectingMainHandler = false
529
          }
christianrhansen's avatar
christianrhansen committed
530
          // behind right side
christianrhansen's avatar
christianrhansen committed
531
          else if (_offsetLeft === 1) {
532
            this.tempHandler.move.side = 'right'
christianrhansen's avatar
christianrhansen committed
533
            this.tempHandler.resize.intersectingMainHandler = true
534
          }
535
        }
536

537
        // -------------------- resize
christianrhansen's avatar
christianrhansen committed
538
        else if (obj.element === 'resize') {
christianrhansen's avatar
christianrhansen committed
539
          this.tempHandler.resize.intersectingMainHandler = obj.intersecting
540

christianrhansen's avatar
christianrhansen committed
541
          // on left side
christianrhansen's avatar
christianrhansen committed
542
          if (_offsetLeft === -1) {
543
            this.tempHandler.resize.side = 'left'
544
          }
christianrhansen's avatar
christianrhansen committed
545
          /*
546
          // intersecting
christianrhansen's avatar
christianrhansen committed
547
          else if (isNaN(Math.sign(obj.offsetLeft))) {
548
          }
christianrhansen's avatar
christianrhansen committed
549
          */
christianrhansen's avatar
christianrhansen committed
550
          // on right side
christianrhansen's avatar
christianrhansen committed
551
          else if (_offsetLeft === 1) {
552
            this.tempHandler.resize.side = 'right'
553
          }
554 555
        }
      },
christianrhansen's avatar
christianrhansen committed
556
      handleMoveCell (obj) {
557 558
        this.cellHandler.move.x = obj.position.left
        this.cellHandler.move.y = obj.position.top
559

christianrhansen's avatar
christianrhansen committed
560 561
        let res = this.getGridPositionForEvent(obj)

562 563 564
        this.cellHandler.move.gridPosition.row = res.y
        this.cellHandler.move.gridPosition.column = res.x

christianrhansen's avatar
christianrhansen committed
565
        if (obj.isFirst) {
566
          this.activeHandler = true
christianrhansen's avatar
christianrhansen committed
567 568
          this.mobileTempCell.show = true
          this.mobileTempCell.button = false
569
          this.cellHandler.move.pushed = true
christianrhansen's avatar
christianrhansen committed
570
        }
571

christianrhansen's avatar
christianrhansen committed
572 573 574
        this.mobileTempCell.x = res.x
        this.mobileTempCell.y = res.y

575 576 577
        this.mobileTempCell.ox = res.ox
        this.mobileTempCell.oy = res.oy

578 579
        this.cellHandler.move.x = res.ox
        this.cellHandler.move.y = res.oy
580

christianrhansen's avatar
christianrhansen committed
581
        if (obj.isFinal) {
582
          this.activeHandler = false
christianrhansen's avatar
christianrhansen committed
583 584
          this.mobileTempCell.onGrid = true
          this.mobileTempCell.button = true
585
          this.mobileTempCell.left = this.gridDimensions.full.cell.width * (res.x - 1)
586 587 588 589
          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
590

591 592
          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
593 594
        }
      },
christianrhansen's avatar
christianrhansen committed
595
      handleResizeCell (obj) {
christianrhansen's avatar
christianrhansen committed
596
        // this.cursor = {x: obj.position.left, y: obj.position.top}
597 598 599
        // 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
600

601 602 603
        // ---> panGrid
        let res = this.getGridPositionForEvent(obj)

604 605 606
        this.cellHandler.resize.gridPosition.row = res.y
        this.cellHandler.resize.gridPosition.column = res.x

607
        if (obj.isFirst) {
608
          this.activeHandler = true
609 610
          this.mobileTempCell.show = true
          this.mobileTempCell.button = false
611
          this.cellHandler.resize.pushed = true
612 613 614 615 616
        }

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

617 618
        this.cellHandler.resize.x = res.ox
        this.cellHandler.resize.y = res.oy
619

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

          if (obj.isFirst) {
            this.mobileTempCell.show = true
656
            this.mobileTempCell.button = false
657 658 659 660 661 662 663 664
            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) {
665
            this.mobileTempCell.onGrid = true
666
            this.mobileTempCell.button = true
667 668
            // this.addMobileCell(obj)
            // this.mobileTempCell.show = false
669 670
            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
671

672 673
            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
674
          }
675
        }
676
      },
677
      async cellHold (event, annotation) {
678 679
        this.$store.commit('mosys/setEditingCells', '')

680
        this.$q.notify({
681
          message: 'Copied.',
682
          color: 'dark',
683 684
          textColor: 'white',
          position: 'bottom-right'
685 686 687 688 689 690 691 692 693 694
        })

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

748
          this.mobileSelectedCell = annotation
749
          this.getCellType(annotation)
750
        }
christianrhansen's avatar
christianrhansen committed
751
      },
752 753 754 755 756
      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)
      },
757 758 759
      // async mobileCellMove (annotation, _x, _y) {
      async mobileCellMove (annotation, x, y) {
        // this.$el.scrollLeft = this.$el.scrollLeft + (this.gridDimensions.full.cell.width * _x)
760

761
        let
762 763
          parsed = annotation.target.selector.parse()
          /*
764
          sliced = parsed.xywh.slice(0, 4),
765
          x = sliced[0],
766 767 768
          y = sliced[1],
          w = sliced[2],
          h = sliced[3]
769
          */
770

771
        /*
772 773 774 775 776 777 778
        // ----- 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
779
        */
780 781 782 783

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

784 785 786 787 788 789 790 791
        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 }
          }
        }])
      },
792
      async mobileCellResize (annotation, _w, _h) {
793 794 795
        let
          parsed = annotation.target.selector.parse(),
          [x, y, w, h] = parsed.xywh
796 797
        console.log('mobileCellResize', w, h)
        /*
798 799 800 801 802 803 804
        // ----- 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
805
        */
806 807 808 809

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

810
        const value = { xywh: [x, y, _w, _h] }
811 812 813 814 815 816 817
        annotation.target.selector.value = value

        await this.$store.dispatch('annotations/patch', [annotation.id, { target: { selector: { value } } }])
      },
      setEditMode (mode) {
        this.$store.commit('mosys/setEditMode', mode)
      },
818 819 820 821
      //
      // DATA
      //

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

      //
      // GRID DRAG & DROP HANDLERS
      //

893
      async handleGridDragOver (event) {
Anton's avatar
Intial  
Anton committed
894 895 896
        let _this = this
        if (this.resizingGrid) {
          const position = this.getGridPositionForEvent(event)
897 898
          this.grid.config.ratio = position.ox / (position.oy * 1.0)
          await this.updateGridMetadataStore()
Anton's avatar
Intial  
Anton committed
899 900
        }
        else {
901 902
          let annotation = this.annotations.filter(annotation => {
            if (!_this.annotationUIStates[annotation._uuid]) return false
Mathias Bär's avatar
Mathias Bär committed
903
            return _this.annotationUIStates[annotation._uuid].beingDragged ||
904
              _this.annotationUIStates[annotation._uuid].beingResized
Anton's avatar
Intial  
Anton committed
905 906
          }).shift()
          let offset, position
907 908 909 910
          if (!annotation) {
            annotation = {
              target: this.grid.get2DArea([1, 1], [1, 1])
            }
Anton's avatar
Intial  
Anton committed
911 912 913
            position = this.getGridPositionForEvent(event)
          }
          else {
914
            offset = this.annotationUIStates[annotation._uuid].draggingOffset
Anton's avatar
Intial  
Anton committed
915 916
            position = this.getGridPositionForEvent(event, offset)
          }
917 918
          if (!this.tmpObjects.length) this.tmpObjects.push(annotation)
          const parsed = annotation.target.selector.parse()
Anton's avatar
Intial  
Anton committed
919
          if (event.dataTransfer.types.includes('text/plain')) {
920 921
            parsed.xywh[0] = position.x
            parsed.xywh[1] = position.y
Anton's avatar
Intial  
Anton committed
922 923 924
            event.preventDefault()
          }
          else {
925 926
            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
927
          }
928
          annotation.target.selector.value = parsed
Anton's avatar
Intial  
Anton committed
929 930 931
        }
      },
      handleGridDragEnd () {