@blokkli/editor 1.0.4 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +345 -16
  3. package/dist/runtime/adapter/drupal/graphql/base.graphql +2 -1
  4. package/dist/runtime/adapter/drupal/graphqlMiddleware.js +4 -1
  5. package/dist/runtime/adapter/index.d.ts +12 -1
  6. package/dist/runtime/blokkliPlugins/Sidebar/Detached/index.vue +41 -39
  7. package/dist/runtime/blokkliPlugins/Sidebar/index.vue +10 -16
  8. package/dist/runtime/blokkliPlugins/ViewOption/index.vue +2 -0
  9. package/dist/runtime/components/BlokkliField.vue +75 -17
  10. package/dist/runtime/components/BlokkliItem.vue +34 -6
  11. package/dist/runtime/components/BlokkliProvider.vue +18 -1
  12. package/dist/runtime/components/Edit/BlockProxy/index.vue +102 -0
  13. package/dist/runtime/components/Edit/DragInteractions/index.vue +49 -25
  14. package/dist/runtime/components/Edit/DraggableList.vue +140 -30
  15. package/dist/runtime/components/Edit/EditProvider.vue +4 -0
  16. package/dist/runtime/components/Edit/Features/AddList/index.vue +3 -0
  17. package/dist/runtime/components/Edit/Features/Artboard/Overview/index.vue +111 -0
  18. package/dist/runtime/components/Edit/Features/Artboard/Scrollbar/index.vue +47 -0
  19. package/dist/runtime/components/Edit/Features/Artboard/index.vue +301 -9
  20. package/dist/runtime/components/Edit/Features/BlockAddList/index.vue +29 -4
  21. package/dist/runtime/components/Edit/Features/CommandPalette/Palette/index.vue +3 -4
  22. package/dist/runtime/components/Edit/Features/Debug/Rects/index.vue +27 -0
  23. package/dist/runtime/components/Edit/Features/DraggingOverlay/DragItems/index.vue +24 -3
  24. package/dist/runtime/components/Edit/Features/DraggingOverlay/DropTargets/fragment.glsl +56 -24
  25. package/dist/runtime/components/Edit/Features/DraggingOverlay/DropTargets/index.vue +184 -29
  26. package/dist/runtime/components/Edit/Features/DraggingOverlay/DropTargets/vertex.glsl +36 -16
  27. package/dist/runtime/components/Edit/Features/DraggingOverlay/index.vue +4 -0
  28. package/dist/runtime/components/Edit/Features/Duplicate/index.vue +1 -13
  29. package/dist/runtime/components/Edit/Features/EditableMask/index.vue +2 -2
  30. package/dist/runtime/components/Edit/Features/History/index.vue +1 -1
  31. package/dist/runtime/components/Edit/Features/Options/Form/Checkbox/index.vue +2 -2
  32. package/dist/runtime/components/Edit/Features/Options/Form/Checkboxes/index.vue +28 -25
  33. package/dist/runtime/components/Edit/Features/Options/Form/Color/index.vue +1 -1
  34. package/dist/runtime/components/Edit/Features/Options/Form/Item.vue +67 -39
  35. package/dist/runtime/components/Edit/Features/Options/Form/Number/index.vue +6 -2
  36. package/dist/runtime/components/Edit/Features/Options/Form/Radios/index.vue +1 -1
  37. package/dist/runtime/components/Edit/Features/Options/Form/Range/index.vue +2 -2
  38. package/dist/runtime/components/Edit/Features/Options/Form/Text/index.vue +2 -1
  39. package/dist/runtime/components/Edit/Features/Options/Form/index.vue +83 -33
  40. package/dist/runtime/components/Edit/Features/ProxyView/index.vue +38 -0
  41. package/dist/runtime/components/Edit/Features/Publish/index.vue +53 -6
  42. package/dist/runtime/components/Edit/Features/Search/Overlay/index.vue +3 -13
  43. package/dist/runtime/components/Edit/Features/Selection/Overlay/index.vue +1 -1
  44. package/dist/runtime/components/Edit/Features/Settings/Dialog/index.vue +2 -0
  45. package/dist/runtime/components/Edit/Features/Structure/List/Field/index.vue +3 -3
  46. package/dist/runtime/components/Edit/Features/Structure/index.vue +3 -3
  47. package/dist/runtime/components/Edit/ScrollBoundary/index.vue +24 -0
  48. package/dist/runtime/components/Edit/index.d.ts +2 -1
  49. package/dist/runtime/components/Edit/index.js +3 -1
  50. package/dist/runtime/composables/defineBlokkli.js +10 -5
  51. package/dist/runtime/constants/index.d.ts +1 -1
  52. package/dist/runtime/constants/index.js +6 -1
  53. package/dist/runtime/css/output.css +1 -1
  54. package/dist/runtime/helpers/animationProvider.js +10 -4
  55. package/dist/runtime/helpers/domProvider.d.ts +12 -4
  56. package/dist/runtime/helpers/domProvider.js +70 -26
  57. package/dist/runtime/helpers/featuresProvider.d.ts +1 -1
  58. package/dist/runtime/helpers/runtimeHelpers/index.d.ts +6 -0
  59. package/dist/runtime/helpers/runtimeHelpers/index.js +25 -0
  60. package/dist/runtime/helpers/selectionProvider.d.ts +2 -1
  61. package/dist/runtime/helpers/selectionProvider.js +7 -8
  62. package/dist/runtime/helpers/symbols.d.ts +7 -0
  63. package/dist/runtime/helpers/symbols.js +7 -0
  64. package/dist/runtime/helpers/typesProvider.d.ts +1 -1
  65. package/dist/runtime/helpers/uiProvider.d.ts +1 -0
  66. package/dist/runtime/helpers/uiProvider.js +16 -3
  67. package/dist/runtime/helpers/webgl/index.d.ts +6 -1
  68. package/dist/runtime/helpers/webgl/index.js +38 -5
  69. package/dist/runtime/icons/eye.svg +1 -0
  70. package/dist/runtime/types/generatedModuleTypes.d.ts +12 -4
  71. package/dist/runtime/types/index.d.ts +30 -1
  72. package/package.json +7 -5
  73. package/dist/runtime/components/Edit/Features/Artboard/Manager/Artboard.d.ts +0 -204
  74. package/dist/runtime/components/Edit/Features/Artboard/Manager/Artboard.js +0 -748
  75. package/dist/runtime/components/Edit/Features/Artboard/Manager/Scrollbar/index.vue +0 -176
  76. package/dist/runtime/components/Edit/Features/Artboard/Manager/index.vue +0 -317
  77. package/dist/runtime/helpers/Artboard/index.d.ts +0 -16
  78. package/dist/runtime/helpers/Artboard/index.js +0 -20
@@ -3,13 +3,19 @@
3
3
  </template>
4
4
 
5
5
  <script setup lang="ts">
6
- import { falsy, getDistance, getInteractionCoordinates } from '#blokkli/helpers'
6
+ import {
7
+ falsy,
8
+ getDistance,
9
+ getInteractionCoordinates,
10
+ isInsideRect,
11
+ } from '#blokkli/helpers'
7
12
  import onBlokkliEvent from '#blokkli/helpers/composables/onBlokkliEvent'
8
13
  import type { Coord, Rectangle } from '#blokkli/types'
9
14
  import { watch, ref, useBlokkli, onMounted, onBeforeUnmount } from '#imports'
10
15
 
11
- const { dom, eventBus, selection, keyboard, ui, animation, state } =
12
- useBlokkli()
16
+ const { dom, eventBus, selection, keyboard, ui, state } = useBlokkli()
17
+
18
+ const rootEl = ui.rootElement()
13
19
 
14
20
  const rects = ref<{ uuid: string; rect: Rectangle }[]>([])
15
21
 
@@ -93,17 +99,36 @@ function getInteractedElement(
93
99
  return { editableFieldName, uuid, timestamp: Date.now(), x, y }
94
100
  }
95
101
 
102
+ // Try to find a block to select by matching its rects.
103
+ // Some blocks might not render anything and thus have a height of 0.
104
+ // All registered block rects enfore a minimum height. That way we might
105
+ // still be able to select a block.
106
+ const visibleUuids = dom.getVisibleBlocks()
107
+ for (let i = 0; i < visibleUuids.length; i++) {
108
+ const rect = dom.getBlockRect(visibleUuids[i])
109
+ if (rect) {
110
+ const relativeRect = ui.getViewportRelativeRect(rect)
111
+ if (isInsideRect(x, y, relativeRect)) {
112
+ return {
113
+ uuid: visibleUuids[i],
114
+ timestamp: Date.now(),
115
+ x,
116
+ y,
117
+ }
118
+ }
119
+ }
120
+ }
121
+
96
122
  return null
97
123
  }
98
124
 
99
125
  function onPointerMove(e: PointerEvent) {
100
- e.preventDefault()
101
- e.stopPropagation()
102
- e.stopImmediatePropagation()
103
- animation.setMouseCoords(e.clientX, e.clientY)
104
126
  if (keyboard.isPressingSpace.value || state.editMode.value !== 'editing') {
105
127
  return
106
128
  }
129
+ e.preventDefault()
130
+ e.stopPropagation()
131
+ e.stopImmediatePropagation()
107
132
  if (e.pointerType === 'touch') {
108
133
  return onTouchMove(e)
109
134
  }
@@ -150,6 +175,7 @@ function onPointerMove(e: PointerEvent) {
150
175
  }
151
176
 
152
177
  let pointerDownTimestamp = 0
178
+ let pointerUpTimestamp = 0
153
179
 
154
180
  function onPointerDown(e: PointerEvent) {
155
181
  if (!keyboard.isPressingSpace.value) {
@@ -158,6 +184,9 @@ function onPointerDown(e: PointerEvent) {
158
184
  e.stopImmediatePropagation()
159
185
  }
160
186
 
187
+ rootEl.removeEventListener('pointermove', onPointerMove)
188
+ rootEl.addEventListener('pointermove', onPointerMove)
189
+
161
190
  if (e.pointerType === 'touch') {
162
191
  return onTouchStart(e)
163
192
  }
@@ -171,17 +200,19 @@ function onPointerDown(e: PointerEvent) {
171
200
 
172
201
  pointerDownTimestamp = Date.now()
173
202
  const coords = { x: e.clientX, y: e.clientY }
203
+ mouseStartCoordinates = coords
174
204
 
175
205
  const interacted = getInteractedElement(e)
176
206
  pointerDownElement = interacted
177
207
  if (interacted) {
178
208
  return
179
209
  }
180
- mouseStartCoordinates = coords
210
+
181
211
  eventBus.emit('mouse:down', { ...coords, type: 'mouse', distance: 0 })
182
212
  }
183
213
 
184
214
  function onPointerUp(e: PointerEvent) {
215
+ rootEl.removeEventListener('pointermove', onPointerMove)
185
216
  e.preventDefault()
186
217
  e.stopPropagation()
187
218
  e.stopImmediatePropagation()
@@ -223,11 +254,12 @@ function onPointerUp(e: PointerEvent) {
223
254
  // Handle double clicking.
224
255
  if (
225
256
  clicked &&
257
+ pointerUpTimestamp &&
226
258
  lastInteractedElement &&
227
259
  (clicked.uuid === lastInteractedElement.uuid ||
228
260
  clicked.editableFieldName === lastInteractedElement.editableFieldName)
229
261
  ) {
230
- const deltaTime = Date.now() - pointerDownTimestamp
262
+ const deltaTime = Date.now() - pointerUpTimestamp
231
263
  const deltaX = Math.abs(lastInteractedElement.x - e.clientX)
232
264
  const deltaY = Math.abs(lastInteractedElement.y - e.clientY)
233
265
  if (deltaTime < 400 && deltaX < 3 && deltaY < 3) {
@@ -248,6 +280,7 @@ function onPointerUp(e: PointerEvent) {
248
280
  }
249
281
  }
250
282
  lastInteractedElement = clicked
283
+ pointerUpTimestamp = Date.now()
251
284
  if (clicked?.uuid) {
252
285
  dom.refreshBlockRect(clicked.uuid)
253
286
  if (keyboard.isPressingControl.value || selection.isMultiSelecting.value) {
@@ -387,51 +420,42 @@ function onClick(e: MouseEvent) {
387
420
  }
388
421
 
389
422
  onMounted(() => {
390
- const el = ui.rootElement()
391
423
  const providerElement = ui.providerElement()
392
424
 
393
425
  providerElement.addEventListener('click', onClick, {
394
426
  capture: true,
395
427
  })
396
428
 
397
- el.addEventListener('click', onClick, {
429
+ rootEl.addEventListener('click', onClick, {
398
430
  capture: true,
399
431
  })
400
432
 
401
- el.addEventListener('pointerdown', onPointerDown, {
433
+ rootEl.addEventListener('pointerdown', onPointerDown, {
402
434
  capture: true,
403
435
  })
404
436
 
405
- el.addEventListener('pointermove', onPointerMove, {
406
- capture: true,
407
- })
408
-
409
- el.addEventListener('pointerup', onPointerUp, {
437
+ rootEl.addEventListener('pointerup', onPointerUp, {
410
438
  capture: true,
411
439
  })
412
440
  })
413
441
 
414
442
  onBeforeUnmount(() => {
415
- const el = ui.rootElement()
416
443
  const providerElement = ui.providerElement()
417
444
 
418
445
  providerElement.removeEventListener('click', onClick, {
419
446
  capture: true,
420
447
  })
421
448
 
422
- el.removeEventListener('click', onClick, {
423
- capture: true,
424
- })
425
-
426
- el.removeEventListener('pointerdown', onPointerDown, {
449
+ rootEl.removeEventListener('click', onClick, {
427
450
  capture: true,
428
451
  })
429
452
 
430
- el.removeEventListener('pointermove', onPointerMove, {
453
+ rootEl.removeEventListener('pointerdown', onPointerDown, {
431
454
  capture: true,
432
455
  })
456
+ rootEl.removeEventListener('pointermove', onPointerMove)
433
457
 
434
- el.removeEventListener('pointerup', onPointerUp, {
458
+ rootEl.removeEventListener('pointerup', onPointerUp, {
435
459
  capture: true,
436
460
  })
437
461
  })
@@ -1,22 +1,54 @@
1
1
  <template>
2
+ <div
3
+ v-if="proxyMode || globalProxyMode"
4
+ class="bk bk-field-list-proxy"
5
+ :class="[
6
+ {
7
+ 'bk-is-visible': proxyVisible,
8
+ },
9
+ 'bk-is-' +
10
+ (dropAlignment || (nestingLevel === 0 ? 'vertical' : 'horizontal')),
11
+ ]"
12
+ >
13
+ <div
14
+ ref="root"
15
+ class="bk-field-list-proxy-list bk-draggable-list-container"
16
+ v-bind="fieldAttributes"
17
+ >
18
+ <BlokkliItem
19
+ v-for="(item, i) in list"
20
+ :key="item.uuid + fieldListType"
21
+ :uuid="item.uuid"
22
+ :bundle="item.bundle"
23
+ :is-new="item.isNew"
24
+ :options="item.options"
25
+ :props="item.props"
26
+ is-editing
27
+ :index="i"
28
+ :parent-type="isNested ? entity.bundle : ''"
29
+ data-editing="true"
30
+ data-element-type="existing"
31
+ :data-sortli-id="item.uuid"
32
+ :data-uuid="item.uuid"
33
+ :data-host-type="entity.type"
34
+ :data-host-bundle="entity.bundle"
35
+ :data-host-uuid="entity.uuid"
36
+ :data-item-bundle="item.bundle"
37
+ :data-host-field-name="name"
38
+ :data-host-field-list-type="fieldListType"
39
+ :data-is-nested="isNested"
40
+ :data-is-new="item.isNew"
41
+ :data-entity-type="runtimeConfig.itemEntityType"
42
+ :data-bk-is-muted="isMuted(item)"
43
+ />
44
+ </div>
45
+ </div>
2
46
  <Component
3
47
  :is="tag"
48
+ v-else
4
49
  ref="root"
5
- class="bk-draggable-list-container"
6
- :data-field-name="name"
7
- :data-field-label="fieldConfig?.label"
8
- :data-field-is-nested="isNested"
9
- :data-host-entity-type="entity.type"
10
- :data-host-entity-uuid="entity.uuid"
11
- :data-host-entity-bundle="entity.bundle"
12
- :data-field-key="fieldKey"
13
- :data-field-drop-alignment="dropAlignment"
14
- :data-allowed-fragments="
15
- allowedFragments ? allowedFragments.join(',') : undefined
16
- "
17
- :data-field-allowed-bundles="allowedBundles"
18
- :data-field-list-type="fieldListType"
19
- :data-field-cardinality="fieldConfig?.cardinality"
50
+ :class="['bk-draggable-list-container', attrs.class]"
51
+ v-bind="fieldAttributes"
20
52
  >
21
53
  <BlokkliItem
22
54
  v-for="(item, i) in list"
@@ -42,31 +74,71 @@
42
74
  :data-is-nested="isNested"
43
75
  :data-is-new="item.isNew"
44
76
  :data-entity-type="runtimeConfig.itemEntityType"
77
+ :data-bk-is-muted="isMuted(item)"
45
78
  />
46
79
  </Component>
47
80
  </template>
48
81
 
49
82
  <script lang="ts" setup>
50
- import { computed, useBlokkli, ref, onMounted, onBeforeUnmount } from '#imports'
83
+ import {
84
+ computed,
85
+ useBlokkli,
86
+ ref,
87
+ onMounted,
88
+ onBeforeUnmount,
89
+ useAttrs,
90
+ provide,
91
+ watch,
92
+ } from '#imports'
51
93
  import type { FieldListItem, EntityContext, FieldConfig } from '#blokkli/types'
52
94
  import type { BlokkliFragmentName } from '#blokkli/definitions'
53
95
  import BlokkliItem from './../BlokkliItem.vue'
96
+ import { isVisibleByOptions } from '#blokkli/helpers/runtimeHelpers'
97
+ import {
98
+ INJECT_FIELD_PROXY_MODE,
99
+ INJECT_IS_EDITING,
100
+ } from '#blokkli/helpers/symbols'
54
101
 
55
- const { dom, types, runtimeConfig } = useBlokkli()
102
+ const { dom, types, runtimeConfig, selection } = useBlokkli()
56
103
 
57
104
  const root = ref<HTMLElement | null>(null)
58
105
 
59
- const props = defineProps<{
60
- name: string
61
- fieldKey: string
62
- list: FieldListItem[]
63
- entity: EntityContext
64
- tag?: string
65
- isNested: boolean
66
- fieldListType: string
67
- allowedFragments?: BlokkliFragmentName[]
68
- dropAlignment?: 'vertical' | 'horizontal'
69
- }>()
106
+ const props = withDefaults(
107
+ defineProps<{
108
+ name: string
109
+ fieldKey: string
110
+ list: FieldListItem[]
111
+ entity: EntityContext
112
+ language?: string
113
+ tag?: string
114
+ isNested: boolean
115
+ fieldListType: string
116
+ allowedFragments?: BlokkliFragmentName[]
117
+ dropAlignment?: 'vertical' | 'horizontal'
118
+ proxyMode?: boolean
119
+ globalProxyMode?: boolean
120
+ nestingLevel: number
121
+ }>(),
122
+ {
123
+ tag: 'div',
124
+ allowedFragments: undefined,
125
+ dropAlignment: undefined,
126
+ language: undefined,
127
+ },
128
+ )
129
+
130
+ const attrs = useAttrs()
131
+
132
+ provide(INJECT_FIELD_PROXY_MODE, props.proxyMode)
133
+ provide(INJECT_IS_EDITING, true)
134
+
135
+ const proxyVisible = computed(
136
+ () =>
137
+ props.proxyMode &&
138
+ (selection.uuids.value.length ||
139
+ selection.isDragging.value ||
140
+ selection.isMultiSelecting.value),
141
+ )
70
142
 
71
143
  const fieldConfig = computed<FieldConfig>(() => {
72
144
  const match = types.getFieldConfig(
@@ -98,13 +170,51 @@ const allowedBundles = computed<string>(() => {
98
170
  return bundles.join(',')
99
171
  })
100
172
 
173
+ const fieldAttributes = computed(() => {
174
+ return {
175
+ 'data-field-name': props.name,
176
+ 'data-field-label': fieldConfig.value.label,
177
+ 'data-field-is-nested': props.isNested,
178
+ 'data-bk-nesting-level': props.nestingLevel,
179
+ 'data-host-entity-type': props.entity.type,
180
+ 'data-host-entity-uuid': props.entity.uuid,
181
+ 'data-host-entity-bundle': props.entity.bundle,
182
+ 'data-field-key': props.fieldKey,
183
+ 'data-field-drop-alignment': props.dropAlignment,
184
+ 'data-allowed-fragments': props.allowedFragments
185
+ ? props.allowedFragments.join(',')
186
+ : undefined,
187
+ 'data-field-allowed-bundles': allowedBundles.value,
188
+ 'data-field-list-type': props.fieldListType,
189
+ 'data-field-cardinality': fieldConfig.value.cardinality,
190
+ }
191
+ })
192
+
193
+ // @TODO: This should be handled differently to prevent constant updates in the
194
+ // component when the options change.
195
+ // Ideally this is handled as an overlay on top of the blocks, similar to how
196
+ // selection or multi-select works.
197
+ function isMuted(item?: FieldListItem) {
198
+ return !isVisibleByOptions(item, props.language)
199
+ }
200
+
201
+ watch(root, function (newRoot) {
202
+ if (newRoot) {
203
+ dom.updateFieldElement(props.entity, props.name, newRoot)
204
+ }
205
+ })
206
+
101
207
  onMounted(() => {
102
208
  if (root.value) {
103
- dom.registerField(props.entity.uuid, props.name, root.value)
209
+ dom.registerField(props.entity, props.name, root.value)
104
210
  }
105
211
  })
106
212
 
107
213
  onBeforeUnmount(() => {
108
- dom.unregisterField(props.entity.uuid, props.name)
214
+ dom.unregisterField(props.entity, props.name)
215
+ })
216
+
217
+ defineOptions({
218
+ inheritAttrs: false,
109
219
  })
110
220
  </script>
@@ -70,6 +70,7 @@ import {
70
70
  INJECT_EDIT_CONTEXT,
71
71
  INJECT_EDIT_FIELD_LIST_COMPONENT,
72
72
  INJECT_EDIT_LOGGER,
73
+ INJECT_GLOBAL_PROXY_MODE,
73
74
  INJECT_IS_EDITING,
74
75
  } from '#blokkli/helpers/symbols'
75
76
  import type { AdapterContext } from '#blokkli/adapter'
@@ -211,4 +212,7 @@ provide<BlokkliApp>(INJECT_APP, {
211
212
  dropAreas,
212
213
  debug,
213
214
  })
215
+
216
+ const isProxyMode = computed(() => ui.isProxyMode.value)
217
+ provide(INJECT_GLOBAL_PROXY_MODE, isProxyMode)
214
218
  </script>
@@ -154,6 +154,9 @@ function onMouseLeave() {
154
154
  }
155
155
 
156
156
  const onWheel = (e: WheelEvent) => {
157
+ if (e.ctrlKey || e.metaKey) {
158
+ return
159
+ }
157
160
  if (ui.addListOrientation.value === 'horizontal' && e.deltaX) {
158
161
  e.stopPropagation()
159
162
  return
@@ -0,0 +1,111 @@
1
+ <template>
2
+ <div
3
+ ref="overviewEl"
4
+ class="bk bk-artboard-overview"
5
+ @touchstart.stop
6
+ @pointerdown.stop
7
+ @mousedown.stop
8
+ @mousemove.stop
9
+ >
10
+ <div ref="overviewArtboardEl" class="bk-artboard-overview-artboard">
11
+ <canvas ref="canvas" />
12
+ </div>
13
+ <div class="bk-artboard-overview-visible">
14
+ <button ref="overviewVisibleEl" />
15
+ </div>
16
+ </div>
17
+ </template>
18
+
19
+ <script setup lang="ts">
20
+ import { type Artboard, type ArtboardPlugin, overview } from 'artboard-deluxe'
21
+ import { onBeforeUnmount, onMounted, ref, useBlokkli, computed } from '#imports'
22
+ import onBlokkliEvent from '#blokkli/helpers/composables/onBlokkliEvent'
23
+
24
+ const props = defineProps<{
25
+ artboard: Artboard
26
+ }>()
27
+
28
+ const { theme, dom, ui, selection } = useBlokkli()
29
+
30
+ const overviewFillColor = computed(() => {
31
+ return theme.getColorString('mono', '500', 0.2)
32
+ })
33
+
34
+ const selectedColor = computed(() => {
35
+ return theme.getColorString('accent', '700', 1)
36
+ })
37
+
38
+ const overviewEl = ref<HTMLDivElement>()
39
+ const overviewArtboardEl = ref<HTMLDivElement>()
40
+ const overviewVisibleEl = ref<HTMLDivElement>()
41
+ const canvas = ref<HTMLCanvasElement>()
42
+
43
+ let pluginOverview: ArtboardPlugin | null = null
44
+
45
+ function updateCanvas() {
46
+ const ctx = canvas.value?.getContext('2d')
47
+
48
+ if (!ctx || !canvas.value || !overviewArtboardEl.value) {
49
+ return
50
+ }
51
+
52
+ const rect = overviewArtboardEl.value.getBoundingClientRect()
53
+
54
+ canvas.value.width = rect.width
55
+ canvas.value.height = rect.height
56
+
57
+ ctx.clearRect(0, 0, rect.width, rect.height)
58
+
59
+ const rects = Object.entries(dom.getBlockRects())
60
+
61
+ const scale = rect.width / ui.artboardSize.value.width
62
+
63
+ ctx.fillStyle = overviewFillColor.value
64
+ ctx.lineWidth = 1
65
+ ctx.strokeStyle = selectedColor.value
66
+
67
+ for (let i = 0; i < rects.length; i++) {
68
+ const [uuid, blockRect] = rects[i]
69
+ ctx.fillRect(
70
+ Math.round(blockRect.x * scale),
71
+ Math.round(blockRect.y * scale),
72
+ Math.round(blockRect.width * scale),
73
+ Math.round(blockRect.height * scale),
74
+ )
75
+ if (selection.isBlockSelected(uuid)) {
76
+ ctx.strokeRect(
77
+ Math.round(blockRect.x * scale) + 0.5,
78
+ Math.round(blockRect.y * scale) + 0.5,
79
+ Math.round(blockRect.width * scale),
80
+ Math.round(blockRect.height * scale),
81
+ )
82
+ }
83
+ }
84
+ }
85
+
86
+ onBlokkliEvent('animationFrame', updateCanvas)
87
+
88
+ onMounted(() => {
89
+ if (overviewEl.value && overviewArtboardEl.value && overviewVisibleEl.value) {
90
+ pluginOverview = props.artboard.addPlugin(
91
+ overview({
92
+ element: overviewEl.value,
93
+ artboardElement: overviewArtboardEl.value,
94
+ visibleAreaElement: overviewVisibleEl.value,
95
+ padding: 20,
96
+ autoHeight: true,
97
+ }),
98
+ )
99
+ }
100
+ })
101
+
102
+ onBeforeUnmount(() => {
103
+ if (pluginOverview) {
104
+ props.artboard.removePlugin(pluginOverview)
105
+ }
106
+ })
107
+
108
+ defineOptions({
109
+ name: 'ArtboardOverview',
110
+ })
111
+ </script>
@@ -0,0 +1,47 @@
1
+ <template>
2
+ <Teleport to="body">
3
+ <div
4
+ class="bk bk-artboard-scrollbar"
5
+ :class="'bk-orientation-' + orientation"
6
+ >
7
+ <div ref="el">
8
+ <button ref="thumb" />
9
+ </div>
10
+ </div>
11
+ </Teleport>
12
+ </template>
13
+
14
+ <script setup lang="ts">
15
+ import { type Artboard, type ArtboardPlugin, scrollbar } from 'artboard-deluxe'
16
+ import { onBeforeUnmount, onMounted, ref } from '#imports'
17
+
18
+ const props = defineProps<{
19
+ artboard: Artboard
20
+ orientation: 'x' | 'y'
21
+ }>()
22
+
23
+ const el = ref<HTMLDivElement>()
24
+ const thumb = ref<HTMLButtonElement>()
25
+ let scrollbarPlugin: ArtboardPlugin | null = null
26
+
27
+ onMounted(() => {
28
+ if (el.value && thumb.value) {
29
+ const plugin = scrollbar({
30
+ element: el.value,
31
+ thumbElement: thumb.value,
32
+ orientation: props.orientation,
33
+ })
34
+ scrollbarPlugin = props.artboard.addPlugin(plugin)
35
+ }
36
+ })
37
+
38
+ onBeforeUnmount(() => {
39
+ if (scrollbarPlugin) {
40
+ props.artboard.removePlugin(scrollbarPlugin)
41
+ }
42
+ })
43
+
44
+ defineOptions({
45
+ name: 'ArtboardScrollbar',
46
+ })
47
+ </script>