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

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

12
13
14
15
16
17
    // 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 }}

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

christianrhansen's avatar
christianrhansen committed
20
21
    // ------------------------------------------------------------------------------------------ cached new cell helper
    // q-page-sticky.z-top(v-touch-pan="moveCachedCell", v-if="cachedNewCell", position="top-right")
christianrhansen's avatar
christianrhansen committed
22
23
24
    //
      .fixed-top-right.z-top(v-touch-pan="moveCachedCell", v-if="cachedNewCell",
      // :style="{top: touch.position.top - 16 - 16 + 'px'}")
25
    // .fixed-top.z-top(v-touch-pan="moveCachedCell", v-if="cachedNewCell")
christianrhansen's avatar
christianrhansen committed
26
27
    #cell-helper.fixed-top.z-top.bg-dark.text-white.transition(:class="{'show': cachedNewCell}")
      q-item.q-px-md.q-py-none.full-height
28
        q-item-main
29
          | Place new cell
30
        q-item-side
christianrhansen's avatar
christianrhansen committed
31
32
33
34
          //
            q-btn.bg-transparent.text-white.on-right(@click.native="clearTempCell()", round, flat,
            // :disabled="!mobileTempCell.onGrid")
              q-icon(name="undo")
35
          q-btn.bg-transparent.text-white.on-right(@click.native="clearCachedCell()", round, flat)
36
            q-icon(name="delete")
37
38

    // ------------------------------------------------------------------------------------------------------------ grid
39
      @click="event => {addMobileCell(event)}",
40
    div.cell-grid.relative-position(
41
    v-touch-pan="panGrid",
42
43
44
45
46
47
    @dragenter="handleGridDragOver",
    @dragover="handleGridDragOver",
    @dragleave="handleGridDragEnd",
    @drop="handleGridDrop",
    @contextmenu="handleGridContextMenu",
    :style="gridStyle")
Anton's avatar
Intial  
Anton committed
48

49
50
      // context menu grid (desktop only)
      q-context-menu.desktop-only(ref="gridmenu")
Anton's avatar
Intial  
Anton committed
51
52
        q-list(link, separator, no-border, style="min-width: 150px; max-height: 300px;")
          q-item(
53
54
55
56
57
          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
58
59
60

      template(v-if="!resizingGrid")

61
        template(v-for="(annotation, index) in annotations")
christianrhansen's avatar
christianrhansen committed
62
63

          //----- cell
64
          // v-touch-hold="event => {cellHold(event, annotation)}",
Anton's avatar
Intial  
Anton committed
65
          .cell-item(
66
          v-if="!annotationUIStates[annotation._uuid] || !annotationUIStates[annotation._uuid].beingDragged",
67
          v-touch-hold="event => {handleCellInfoTouch(event, annotation)}",
68
69
70
71
72
73
74
75
76
77
78
79
          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}`")

            //----- edit-/close-button
            // TODO: find a more elegant solution
            .desktop-only
Mathias Bär's avatar
Mathias Bär committed
80
              q-btn.edit-button.absolute-top-right(
81
82
83
84
85
86
87
88
              @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"
              )
            .mobile-only
              q-btn.edit-button.absolute.fit.bg-transparent(
              @click.prevent="event => {touchMobileCell(event, annotation)}", flat)
christianrhansen's avatar
christianrhansen committed
89

90
91
92
93
94
              //----- 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
95

96
            //----- selecting cells disabled because it has no use currently
97
98
99
100
101
102
103
104
            // 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
105
            //----- resize-handler (desktop only)
106
            .desktop-only.cell-item-resize-handle(
107
108
109
110
            draggable="true",
            @dragstart="event => {handleCellResizerDragStart(event, annotation)}",
            @dragend="event => {handleCellResizerDragEnd(event, annotation)}",
            @dragexit="event => {handleCellResizerDragEnd(event, annotation)}")
111
              q-icon.q-ma-xs(name="network cell")
112

113
114
115
            //----- context menu for cells (desktop only)
            // TODO: needs revision
            q-context-menu.desktop-only
116
117
118
119
120
121
122
              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
123

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

129
        //------------------------- temporary cell (mobile)
130
131
        // template
        template(v-if="mobileTempCell.show && cachedNewCell")
132

133
          //----- cell
134
135
136
          .cell-item.cell-item-tmp-mobile.row.justify-center.items-center(
          ref="_mobileTempCell",
          :style="mobileTempCellStyle(mobileTempCell)")
137

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

christianrhansen's avatar
christianrhansen committed
140
            q-btn.text-white(v-if="mobileTempCell.button",
141
142
            @click="event => {addMobileCell(event)}", round, flat, size="lg",
            style="background-color: rgba(0, 0, 0, 0);")
143
              q-icon(name="check")
144

145
146
          //----------------------------------------------------------------------------------------------- move handler
          //----- (main)
christianrhansen's avatar
christianrhansen committed
147
          cell-handler-mobile.main-handler(
148
          v-if="!handlerNewCell.resize.pushed",
149
150
151
          @onIntersectionChange="intersectionChanged",
          :element="'move'",
          v-touch-pan="handleMoveCell",
christianrhansen's avatar
christianrhansen committed
152
          :style="{left: handlerNewCell.move.x - 20 + 'px', top: handlerNewCell.move.y - 20 + 'px'}",
153
          :class="[{'pushed': !handlerNewCell.move.pushed}, {'hide': !mobileTempCell.onGrid}]")
christianrhansen's avatar
christianrhansen committed
154
155
            q-icon.self-center.rotate-180(name="open_with", size="22px")

156
          //----- (temp)
157
          //----- (when main move handler is outside of viewport)
christianrhansen's avatar
christianrhansen committed
158
          cell-handler-mobile.temp-handler(
159
          v-touch-pan="handleMoveCell",
christianrhansen's avatar
christianrhansen committed
160
          :class="[(!mobileTempCell.onGrid || tempHandler.move.intersectingMain || handlerNewCell.resize.pushed || handlerNewCell.move.pushed ? 'hide': 'show'), (tempHandler.move.side === 'left' ? 'left-side' : 'right-side')]",
161
          :style="{top: handlerNewCell.move.y + 59 - 20 + 'px'}",)
162
163
            q-icon.self-center.rotate-180(name="open_with", size="22px")

164
165
          //--------------------------------------------------------------------------------------------- resize handler
          //----- (main)
christianrhansen's avatar
christianrhansen committed
166
          cell-handler-mobile.main-handler(
167
          v-if="!handlerNewCell.move.pushed",
168
169
170
          @onIntersectionChange="intersectionChanged",
          :element="'resize'",
          v-touch-pan="handleResizeCell",
171
          :style="{left: handlerNewCell.resize.x - 20 + 'px', top: handlerNewCell.resize.y - 20 + 'px'}",
172
          :class="[{'pushed': !handlerNewCell.resize.pushed}, {'hide': !mobileTempCell.onGrid}]")
173
            q-icon.self-center(name="signal_cellular_4_bar", size="12px", style="margin-left: -3px;")
174

175
176
          //----- (temp)
          //----- (when main resize handler is outside of viewport)
christianrhansen's avatar
christianrhansen committed
177
          cell-handler-mobile.temp-handler(
178
          v-touch-pan="handleResizeCell",
christianrhansen's avatar
christianrhansen committed
179
          :class="[(!mobileTempCell.onGrid || tempHandler.resize.intersectingMain || handlerNewCell.move.pushed || handlerNewCell.resize.pushed ? 'hide': 'show'), (tempHandler.resize.side === 'left' ? 'left-side' : 'right-side')]",
180
          :style="{top: handlerNewCell.resize.y + 59 - 20 + 'px'}",)
181
182
            q-icon.self-center(name="signal_cellular_4_bar", size="12px", style="margin-left: -3px;")

christianrhansen's avatar
christianrhansen committed
183
      // ---------------------------------------------------------------------------------------------------------------
Anton's avatar
Intial  
Anton committed
184
      template(v-else)
185
        .cell-item(:style="getAnnotationStyle({x:0,y:0,width:1,height:1})", key="cell-grid-resizer")
Anton's avatar
Intial  
Anton committed
186
          div.cell-item-resize-handle(
187
188
189
190
191
          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
192

Mathias Bär's avatar
Mathias Bär committed
193
      //template(v-if="!isMobile")
194
195
196
197
198
        .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
199
      //template(v-if="isMobile")
Mathias Bär's avatar
Mathias Bär committed
200
201
202
203
204
        .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
205

christianrhansen's avatar
christianrhansen committed
206
    // ------------------------------------------------------------------------------------------ edit box (mobile only)
207

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

214
      //------------------------- buttons
215
216
      // .row.text-dark(style="background-color: rgba(0, 0, 0, .2);")
      .row.text-dark(style="background-color: rgba(255, 150, 150, .3);")
217
218
219

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

224
225
        //----- toggle edit-box
        .col.text-right
christianrhansen's avatar
christianrhansen committed
226
          q-btn.q-py-none.q-pr-sm.q-mr-xs(@click="carouselVisibility()", flat, no-ripple)
christianrhansen's avatar
christianrhansen committed
227
228
            q-icon(v-if="carousel.visibility", name="keyboard_arrow_down")
            q-icon(v-else, name="keyboard_arrow_up")
229

christianrhansen's avatar
christianrhansen committed
230
      //q-item-separator.q-ma-none
231
232
233
234
235
236

      //------------------------- 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
237
238
        // q-carousel-slide.q-py-sm
        q-carousel-slide.q-py-none
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
          .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
254
255
        // q-carousel-slide.q-py-sm
        q-carousel-slide.q-py-none
256
257
258
259
260
261
262
263
264
265
266
267
268
          .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
269
270
271
272
</template>

<script>
  import Cell from './Cell'
273
274
  import { userHasFeature } from 'mbjs-quasar/src/lib'
  import { mapGetters } from 'vuex'
275
  import GridEditorEditingCellsMobile from './/GridEditorEditingCellsMobile'
276
  import CellHandlerMobile from './CellHandlerMobile'
Anton's avatar
Intial  
Anton committed
277
278
279
280
281
282

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

  export default {
    components: {
283
      Cell,
284
285
      GridEditorEditingCellsMobile,
      CellHandlerMobile
Anton's avatar
Intial  
Anton committed
286
    },
287
    props: ['gridUuid', 'tabsAreOpen'],
Anton's avatar
Intial  
Anton committed
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
    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,
313
        annotations: undefined,
314
        tmpObjects: [],
315
        annotationUIStates: {},
Anton's avatar
Anton committed
316
        gridDimensions: { gridWidth: 0, gridHeight: 0, cellWidth: 0, cellHeight: 0 },
Anton's avatar
Intial  
Anton committed
317
        contextMenuClickPosition: {},
318
        resizingGrid: false,
christianrhansen's avatar
christianrhansen committed
319
        mobileSelectedCell: undefined,
320
        touch: {position: {top: undefined, left: undefined}},
321
        isMobile: this.$q.platform.is.mobile,
322
323
324
325
326
327
328
329
330
331
332
333
        mobileTempCell: {
          x: 0,
          y: 0,
          ox: 0,
          oy: 0,
          width: 1,
          height: 1,
          left: 0,
          show: false,
          onGrid: false,
          button: false
        },
334
        slide: undefined,
335
        modal: false,
336
        carousel: {visibility: false, icon: 'open_with', slide: 0},
christianrhansen's avatar
christianrhansen committed
337
        // cursor: {x: undefined, y: undefined},
christianrhansen's avatar
christianrhansen committed
338
        handlerNewCell: {
339
          size: {width: 40, height: 40},
340
          move: {x: 20, y: undefined, pushed: false, pos: undefined, inViewport: undefined, onLeft: undefined, onRight: undefined, dataLeft: undefined},
christianrhansen's avatar
christianrhansen committed
341
          resize: {x: undefined, y: undefined, pushed: false, pos: undefined}
342
        },
343
        handlerNewCellTemp: {visibility: true, left: 0},
344
        tempHandler: {
christianrhansen's avatar
christianrhansen committed
345
346
          move: {left: undefined, intersectingMain: false, side: undefined},
          resize: {left: undefined, intersectingMain: false, side: undefined}
347
        }
Anton's avatar
Intial  
Anton committed
348
349
      }
    },
350
351
    computed: {
      ...mapGetters({
christianrhansen's avatar
christianrhansen committed
352
        cachedNewCell: 'mosys/getNewCell',
353
        user: 'auth/getUserState',
354
        // isMobile: 'globalSettings/getIsMobile',
Mathias Bär's avatar
Mathias Bär committed
355
        // editingCells: 'mosys/getEditingCells'
356
        showEditingCells: 'mosys/getShowEditingCells',
357
358
        scrollPositionCache: 'mosys/getScrollPositionCache',
        editMode: 'mosys/getEditMode'
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
      }),
      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
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
      },
      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>")`
        }
398
399
      }
    },
Anton's avatar
Intial  
Anton committed
400
401
    async mounted () {
      await this.fetchData()
402
      this.resetScrollPosition()
403
404
405
    },
    beforeDestroy () {
      this.observer.disconnect()
Anton's avatar
Intial  
Anton committed
406
407
    },
    watch: {
408
409
410
411
412
413
414
415
416
417
      slide (val) {
        let icon
        switch (val) {
        case 0:
          icon = 'open_with'
          break
        case 1:
          icon = 'photo_size_select_small'
          break
        case 2:
418
          icon = 'more_horiz'
419
420
421
422
423
          break
        default:
          icon = 'open_with'
          break
        }
424
425
426
        // this.carousel.slide = val
        // this.carousel.icon = icon
        this.carousel = {slide: val, icon: icon, visibility: this.carousel.visibility}
427
      },
Anton's avatar
Anton committed
428
      annotations () {
429
        this.updateAnnotationUIStates()
Anton's avatar
Intial  
Anton committed
430
431
432
433
434
435
      },
      gridMetadata () {
        this.updateGridDimensions()
      },
      async gridUuid () {
        await this.fetchData()
Mathias Bär's avatar
Mathias Bär committed
436
437
      },
      showEditingCells (val) {
438
        // console.log('show editing cells', val)
Mathias Bär's avatar
Mathias Bär committed
439
440
441
        if (val === false) {
          this.updateAnnotationUIStates()
        }
442
443
444
      },
      tabsAreOpen () {
        this.resetScrollPosition()
Anton's avatar
Intial  
Anton committed
445
446
447
      }
    },
    methods: {
448
      intersectionChanged (obj) {
christianrhansen's avatar
christianrhansen committed
449
450
        let _offsetLeft = Math.sign(obj.offsetLeft)

451
        // -------------------- move
452
        if (obj.element === 'move') {
christianrhansen's avatar
christianrhansen committed
453
          this.tempHandler.move.intersectingMain = obj.intersecting
454

christianrhansen's avatar
christianrhansen committed
455
          // behind left side
christianrhansen's avatar
christianrhansen committed
456
          if (_offsetLeft < 0) {
457
            this.tempHandler.move.side = 'left'
458
          }
459
          // intersecting
christianrhansen's avatar
christianrhansen committed
460
          else if (isNaN(_offsetLeft)) {
461
            if (this.tempHandler.move.intersectingMain && this.tempHandler.move.side === 'right') this.tempHandler.resize.intersectingMain = false
462
          }
christianrhansen's avatar
christianrhansen committed
463
          // behind right side
christianrhansen's avatar
christianrhansen committed
464
          else if (_offsetLeft === 1) {
465
            this.tempHandler.move.side = 'right'
466
            this.tempHandler.resize.intersectingMain = true
467
          }
468
        }
469

470
        // -------------------- resize
christianrhansen's avatar
christianrhansen committed
471
        else if (obj.element === 'resize') {
christianrhansen's avatar
christianrhansen committed
472
          this.tempHandler.resize.intersectingMain = obj.intersecting
473

christianrhansen's avatar
christianrhansen committed
474
          // on left side
christianrhansen's avatar
christianrhansen committed
475
          if (_offsetLeft === -1) {
476
            this.tempHandler.resize.side = 'left'
477
          }
christianrhansen's avatar
christianrhansen committed
478
          /*
479
          // intersecting
christianrhansen's avatar
christianrhansen committed
480
          else if (isNaN(Math.sign(obj.offsetLeft))) {
481
          }
christianrhansen's avatar
christianrhansen committed
482
          */
christianrhansen's avatar
christianrhansen committed
483
          // on right side
christianrhansen's avatar
christianrhansen committed
484
          else if (_offsetLeft === 1) {
485
            this.tempHandler.resize.side = 'right'
486
          }
487
488
        }
      },
christianrhansen's avatar
christianrhansen committed
489
      handleMoveCell (obj) {
490
        this.handlerNewCellTemp.left = -100
christianrhansen's avatar
christianrhansen committed
491
492
        this.handlerNewCell.move.x = obj.position.left
        this.handlerNewCell.move.y = obj.position.top
493

christianrhansen's avatar
christianrhansen committed
494
495
496
497
498
        let res = this.getGridPositionForEvent(obj)

        if (obj.isFirst) {
          this.mobileTempCell.show = true
          this.mobileTempCell.button = false
christianrhansen's avatar
christianrhansen committed
499
          this.handlerNewCell.move.pushed = true
christianrhansen's avatar
christianrhansen committed
500
        }
501

christianrhansen's avatar
christianrhansen committed
502
503
504
        this.mobileTempCell.x = res.x
        this.mobileTempCell.y = res.y

505
506
507
        this.mobileTempCell.ox = res.ox
        this.mobileTempCell.oy = res.oy

christianrhansen's avatar
christianrhansen committed
508
509
        this.handlerNewCell.move.x = res.ox
        this.handlerNewCell.move.y = res.oy
510

christianrhansen's avatar
christianrhansen committed
511
512
513
        if (obj.isFinal) {
          this.mobileTempCell.onGrid = true
          this.mobileTempCell.button = true
514
          this.mobileTempCell.left = this.gridDimensions.full.cell.width * (res.x - 1)
christianrhansen's avatar
christianrhansen committed
515
516
517
518
          this.handlerNewCell.move.pushed = false
          // this.handlerNewCell.move.y = this.gridDimensions.full.cell.height * (res.y - 1) + 8 + 59 + 20
          this.handlerNewCell.move.x = this.gridDimensions.full.cell.width * (this.mobileTempCell.x - 1) + 8 + 20
          this.handlerNewCell.move.y = this.gridDimensions.full.cell.height * (this.mobileTempCell.y - 1) + 8 + 20
519
520
521

          this.handlerNewCell.resize.x = this.gridDimensions.full.cell.width * (res.x + this.mobileTempCell.width - 1) - 8 - 20
          this.handlerNewCell.resize.y = this.gridDimensions.full.cell.height * (res.y + this.mobileTempCell.height - 1) - 8 - 20
christianrhansen's avatar
christianrhansen committed
522
523
        }
      },
christianrhansen's avatar
christianrhansen committed
524
      handleResizeCell (obj) {
christianrhansen's avatar
christianrhansen committed
525
        // this.cursor = {x: obj.position.left, y: obj.position.top}
526
527
528
529
        // this.handlerNewCell.resize = {x: obj.position.left, y: obj.position.top}
        this.handlerNewCell.resize.x = obj.position.left
        this.handlerNewCell.resize.y = obj.position.top

530
531
532
533
534
535
        // ---> panGrid
        let res = this.getGridPositionForEvent(obj)

        if (obj.isFirst) {
          this.mobileTempCell.show = true
          this.mobileTempCell.button = false
536
          this.handlerNewCell.resize.pushed = true
537
538
539
540
541
        }

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

542
543
544
        this.handlerNewCell.resize.x = res.ox
        this.handlerNewCell.resize.y = res.oy

545
546
547
        if (obj.isFinal) {
          this.mobileTempCell.onGrid = true
          this.mobileTempCell.button = true
548
          this.handlerNewCell.resize.pushed = false
549
550
          // this.addMobileCell(obj)
          // this.mobileTempCell.show = false
551
552
          this.handlerNewCell.resize.x = this.gridDimensions.full.cell.width * res.x - 8 - 20
          this.handlerNewCell.resize.y = this.gridDimensions.full.cell.height * res.y - 8 - 20
553
554
        }
      },
555
556
557
558
      clearCachedCell () {
        this.$store.commit('mosys/cacheNewCell', undefined)
        this.clearTempCell()
      },
559
560
561
562
      handleCellInfoTouch (event, annotation) {
        this.touchMobileCell(event, annotation)
        this.handleModal()
      },
563
564
565
566
      getCarouselIcon (val) {
        console.log(val)
      },
      carouselVisibility () {
567
568
569
570
        console.log(this.carousel.slide)
        if (this.$refs.carousel) {
          this.$refs.carousel.goToSlide(this.carousel.slide)
        }
571
572
        this.carousel.visibility = !this.carousel.visibility
      },
573
574
575
      handleModal () {
        this.modal = !this.modal
      },
576
      clearTempCell () {
577
        this.mobileTempCell = {x: undefined, y: undefined, width: undefined, height: undefined, onGrid: false, button: false}
578
      },
579
      panGrid (obj) {
580
        if (this.cachedNewCell && !this.mobileTempCell.onGrid) {
581
          // console.log('hhhhhhhhhhhhhhhhh', obj)
582
583
584
585
          let res = this.getGridPositionForEvent(obj)

          if (obj.isFirst) {
            this.mobileTempCell.show = true
586
            this.mobileTempCell.button = false
587
588
589
590
591
592
593
594
            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) {
595
            this.mobileTempCell.onGrid = true
596
            this.mobileTempCell.button = true
597
598
            // this.addMobileCell(obj)
            // this.mobileTempCell.show = false
christianrhansen's avatar
christianrhansen committed
599
600
            this.handlerNewCell.move.x = this.gridDimensions.full.cell.width * (this.mobileTempCell.x - 1) + 8 + 20
            this.handlerNewCell.move.y = this.gridDimensions.full.cell.height * (this.mobileTempCell.y - 1) + 8 + 20
601
602
603

            this.handlerNewCell.resize.x = this.gridDimensions.full.cell.width * res.x - 8 - 20
            this.handlerNewCell.resize.y = this.gridDimensions.full.cell.height * res.y - 8 - 20
604
          }
605
        }
606
      },
607
      async cellHold (event, annotation) {
608
609
        this.$store.commit('mosys/setEditingCells', '')

610
        this.$q.notify({
611
          message: 'Copied.',
612
          color: 'dark',
613
614
          textColor: 'white',
          position: 'bottom-right'
615
616
617
618
619
620
621
622
623
624
        })

        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
625
626
      moveCachedCell (obj) {
        this.touch.position = {top: obj.position.top, left: obj.position.left}
627
628
629
630
631
632
633
634
635
        /*
        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
636
      },
637
638
      addMobileCell (event) {
        // console.log('addMobileCell()', event, annotation)
christianrhansen's avatar
christianrhansen committed
639
        if (this.cachedNewCell) {
640
          this.handleGridDrop(event)
christianrhansen's avatar
christianrhansen committed
641
          this.$store.commit('mosys/cacheNewCell', undefined)
642
643
          this.$q.notify({
            message: 'Cell was added.',
644
645
646
            color: 'dark',
            position: 'center',
            timeout: 50
647
          })
christianrhansen's avatar
christianrhansen committed
648
649
        }
      },
650
651
652
653
654
655
656
657
658
659
      touchMobileCell (event, cell) {
        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)
      },
christianrhansen's avatar
christianrhansen committed
660
      handleCellTouch (event, annotation) {
661
662
663
664
        if (!this.cachedNewCell) {
          console.log(event, annotation)
          this.mobileSelectedCell = annotation
        }
christianrhansen's avatar
christianrhansen committed
665
      },
666
      async mobileCellMove (annotation, _x, _y) {
667
668
        this.$el.scrollLeft = this.$el.scrollLeft + (this.gridDimensions.full.cell.width * _x)

669
670
        let
          parsed = annotation.target.selector.parse(),
671
          sliced = parsed.xywh.slice(0, 4),
672
          x = sliced[0],
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
          y = sliced[1],
          w = sliced[2],
          h = sliced[3]

        // ----- 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

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

688
689
690
691
692
693
694
695
        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 }
          }
        }])
      },
696
      async mobileCellResize (annotation, _w, _h) {
697
698
699
        let
          parsed = annotation.target.selector.parse(),
          [x, y, w, h] = parsed.xywh
700
701
702
703
704
705
706
707
708
709
710
711

        // ----- 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

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

712
713
714
715
716
717
718
719
        const value = { xywh: [x, y, w, h] }
        annotation.target.selector.value = value

        await this.$store.dispatch('annotations/patch', [annotation.id, { target: { selector: { value } } }])
      },
      setEditMode (mode) {
        this.$store.commit('mosys/setEditMode', mode)
      },
720
721
722
723
      //
      // DATA
      //

Anton's avatar
Intial  
Anton committed
724
725
726
      async fetchData () {
        if (this.gridUuid) {
          this.grid = await this.$store.dispatch('maps/get', this.gridUuid)
727
728
729
730
731
732
733
734
          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
735
          this.updateGridDimensions()
736
737
738
739
740
741
          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
742
743
        }
      },
744
745
746
      async updateGridMetadataStore () {
        await this.$store.dispatch('maps/patch', [this.grid.id, { config: this.grid.config }])
        this.updateGridDimensions()
Anton's avatar
Intial  
Anton committed
747
      },
748
749
750
751
752
      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
753
754
          // return _this.cells.find(c => c._uuid === k)
          return _this.annotations.find(c => c._uuid === k)
755
756
        })
        this.$store.commit('mosys/setSelectedCells', selectedCells)
Anton's avatar
Intial  
Anton committed
757
      },
Mathias Bär's avatar
Mathias Bär committed
758
759
760
761
762
763
764
765
      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)
        })
766
767
768
769
770
771
772
        /*
        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
773
774
        this.$store.commit('mosys/setEditingCells', editingCells)
      },
775
776
777
778
779
      updateAnnotationUIStates () {
        let newAnnotationUIStates = {}
        this.annotations.forEach(a => {
          newAnnotationUIStates[a._uuid] = {
            selected: false,
Mathias Bär's avatar
Mathias Bär committed
780
            editing: false,
781
            beingResized: false,
Mathias Bär's avatar
Mathias Bär committed
782
            beginDragged: false,
783
            annotation: a
Anton's avatar
Intial  
Anton committed
784
          }
785
786
        })
        this.annotationUIStates = newAnnotationUIStates
Anton's avatar
Intial  
Anton committed
787
        this.updateSelectedCells()
Mathias Bär's avatar
Mathias Bär committed
788
        this.updateEditingCells()
789
      },
790
791
792
793
794

      //
      // GRID DRAG & DROP HANDLERS
      //

795
      async handleGridDragOver (event) {
Anton's avatar
Intial  
Anton committed
796
797
798
        let _this = this
        if (this.resizingGrid) {
          const position = this.getGridPositionForEvent(event)
799
800
          this.grid.config.ratio = position.ox / (position.oy * 1.0)
          await this.updateGridMetadataStore()
Anton's avatar
Intial  
Anton committed
801
802
        }
        else {
803
804
          let annotation = this.annotations.filter(annotation => {
            if (!_this.annotationUIStates[annotation._uuid]) return false
Mathias Bär's avatar
Mathias Bär committed
805
            return _this.annotationUIStates[annotation._uuid].beingDragged ||
806
              _this.annotationUIStates[annotation._uuid].beingResized
Anton's avatar
Intial  
Anton committed
807
808
          }).shift()
          let offset, position
809
810
811
812
          if (!annotation) {
            annotation = {
              target: this.grid.get2DArea([1, 1], [1, 1])
            }
Anton's avatar
Intial  
Anton committed
813
814
815
            position = this.getGridPositionForEvent(event)
          }
          else {
816
            offset = this.annotationUIStates[annotation._uuid].draggingOffset
Anton's avatar
Intial  
Anton committed
817
818
            position = this.getGridPositionForEvent(event, offset)
          }
819
820
          if (!this.tmpObjects.length) this.tmpObjects.push(annotation)
          const parsed = annotation.target.selector.parse()
Anton's avatar
Intial  
Anton committed
821
          if (event.dataTransfer.types.includes('text/plain')) {
822
823
            parsed.xywh[0] = position.x
            parsed.xywh[1] = position.y
Anton's avatar
Intial  
Anton committed
824
825
826
            event.preventDefault()
          }
          else {
827
828
            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
829
          }
830
          annotation.target.selector.value = parsed
Anton's avatar
Intial  
Anton committed
831
832
833
        }
      },
      handleGridDragEnd () {
834
        this.tmpObjects = []
Anton's avatar
Intial  
Anton committed
835
      },
836
      async handleGridDrop (event) {
837
838
839
840
841
        let dropData
        if (event.dataTransfer) dropData = event.dataTransfer.getData('text/plain')
        if (!dropData) dropData = JSON.stringify(this.cachedNewCell)
        // console.log('handleGridDrop - event', event)
        // console.log('handleGridDrop - dropData', dropData)
842
843
        if (dropData) {
          dropData = JSON.parse(dropData)
844
          let annotation = this.annotations.find(a => a.id === dropData.id)
845
846
847
848
849
          const { x, y } = this.getGridPositionForEvent(
            event,
            annotation ? this.annotationUIStates[annotation._uuid].draggingOffset : undefined
          )
          if (annotation) {
850
851
852
            const
              parsed = annotation.target.selector.parse(),
              target = this.grid.get2DArea([x, y], parsed.xywh.slice(2))
853
854
855
856
857
858
            annotation.target.selector.value = target.selector.value
            await this.$store.dispatch('annotations/patch', [annotation.id, {
              target: {
                selector: { value: target.selector.value }
              }
            }])
Anton's avatar
Intial  
Anton committed
859
860
          }
          else {
861
862
            if (this.mobileTempCell.x) console.log('this.mobileTempCell', this.mobileTempCell)
            let test = this.grid.get2DArea([this.mobileTempCell.x, this.mobileTempCell.y], [this.mobileTempCell.width, this.mobileTempCell.height])
863
864
865
866
867
868
869
870
871
872
873
            const
              { data, config, component } = dropData,
              cell = await this.$store.dispatch('cells/post', { data, config, component })
            annotation = await this.$store.dispatch('annotations/post', {
              body: {
                type: 'Cell',
                purpose: 'linking',
                source: {
                  id: cell.id
                }
              },
874
875
              // target: this.grid.get2DArea([x, y], [1, 1])
              target: test
876
            })
877
            this.annotations.push(annotation)
Anton's avatar
Anton committed
878
            this.updateAnnotationUIStates()
Anton's avatar
Intial  
Anton committed
879
          }
880

881
          this.tmpObjects = []
Anton's avatar
Intial  
Anton committed
882
883
          event.preventDefault()
        }
884
        this.clearTempCell()
Anton's avatar
Intial  
Anton committed
885
      },
886
887
888
889

      //
      // CELL DRAG & DROP HANDLERS
      //
Mathias Bär's avatar
Mathias Bär committed
890
891
892
      handleCellEditClick (event, cell) {
        this.annotationUIStates[cell._uuid].editing = !this.annotationUIStates[cell._uuid].editing
        this.updateEditingCells()
893
        this.$root.$emit('mosys_saveScrollPosition')
Mathias Bär's avatar
Mathias Bär committed
894
      },
895
896
897
898
899
      handleCellClick (event, cell) {
        this.annotationUIStates[cell._uuid].selected = !this.annotationUIStates[cell._uuid].selected
        this.updateSelectedCells()
      },
      handleCellDragStart (event, annotation) {
christianrhansen's avatar
christianrhansen committed
900
        console.log('§§§§§§§§', annotation)
901
902
903
904
905
906
907
908
909
910
911
912
        if (!this.isMobile) {
          if (!this.annotationUIStates[annotation._uuid].beingResized) {
            event.dataTransfer.setData('text/plain', JSON.stringify(annotation))
            event.dataTransfer.setDragImage(nullImage, 0, 0)
            let elContainerBoundingBox = this.$el.getBoundingClientRect()
            let elBoundingBox = event.target.getBoundingClientRect()
            let offset = {
              x: (event.clientX - elContainerBoundingBox.x) - (elBoundingBox.x - elContainerBoundingBox.x),
              y: (event.clientY - elContainerBoundingBox.y) - (elBoundingBox.y - elContainerBoundingBox.y)
            }
            this.annotationUIStates[annotation._uuid].draggingOffset = offset
            this.annotationUIStates[annotation._uuid].beingDragged = true
913
          }
914
          this.tmpObjects.push(annotation)
915
916
917
        }
      },
      handleCellDragEnd (event, annotation) {
918
919
920
        if (!this.mobile) {
          this.annotationUIStates[annotation._uuid].beingDragged = false
        }
921
922
923
924
      },
      async handleCellContextMenuDelete (event, annotation) {
        this.annotationUIStates[annotation._uuid].selected = false
        this.updateSelectedCells()
Mathias Bär's avatar
Mathias Bär committed
925
        this.updateEditingCells()
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
        await this.$store.dispatch('cells/delete', annotation.body.source.id)
        await this.$store.dispatch('annotations/delete', annotation.id)
        this.annotations = this.annotations.filter(a => a.id !== annotation.id)
      },

      //
      // GRID RESIZE HANDLERS

      handleGridResizerDragStart (event) {
        event.dataTransfer.setDragImage(nullImage, 0, 0)
      },
      async handleGridResizerDragEnd ()