@blokkli/editor 1.0.3 → 1.1.0

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/README.md +0 -6
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +344 -11
  4. package/dist/runtime/adapter/drupal/graphql/base.graphql +2 -1
  5. package/dist/runtime/adapter/drupal/graphqlMiddleware.js +4 -1
  6. package/dist/runtime/adapter/index.d.ts +12 -1
  7. package/dist/runtime/blokkliPlugins/Sidebar/Detached/index.vue +41 -39
  8. package/dist/runtime/blokkliPlugins/Sidebar/index.vue +10 -16
  9. package/dist/runtime/blokkliPlugins/ViewOption/index.vue +2 -0
  10. package/dist/runtime/components/BlokkliField.vue +76 -18
  11. package/dist/runtime/components/BlokkliItem.vue +34 -6
  12. package/dist/runtime/components/BlokkliProvider.vue +18 -1
  13. package/dist/runtime/components/Edit/BlockProxy/index.vue +102 -0
  14. package/dist/runtime/components/Edit/DragInteractions/index.vue +49 -25
  15. package/dist/runtime/components/Edit/DraggableList.vue +139 -29
  16. package/dist/runtime/components/Edit/EditProvider.vue +4 -0
  17. package/dist/runtime/components/Edit/Features/AddList/index.vue +3 -0
  18. package/dist/runtime/components/Edit/Features/Artboard/Overview/index.vue +111 -0
  19. package/dist/runtime/components/Edit/Features/Artboard/Scrollbar/index.vue +47 -0
  20. package/dist/runtime/components/Edit/Features/Artboard/index.vue +301 -9
  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 +84 -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 +4 -2
  56. package/dist/runtime/helpers/domProvider.js +42 -19
  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 +39 -3
  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
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <Teleport to="body">
3
- <slot :color="active?.color" :label="active?.label" />
3
+ <slot :color="activeColorHex" :label="active?.label" />
4
4
  </Teleport>
5
5
  </template>
6
6
 
@@ -41,9 +41,15 @@ const props = defineProps<{
41
41
  isTouch: boolean
42
42
  }>()
43
43
 
44
+ const MIN_GAP = 20
45
+
44
46
  enum RectRenderType {
45
- FIELD,
46
47
  DROP_AREA,
48
+ FIELD_1,
49
+ FIELD_2,
50
+ FIELD_3,
51
+ FIELD_4,
52
+ ACTIVE_AREA,
47
53
  }
48
54
 
49
55
  type Orientation = 'horizontal' | 'vertical'
@@ -66,7 +72,7 @@ type FieldRect = Rectangle & {
66
72
 
67
73
  type DrawnRect = Rectangle & {
68
74
  id: string
69
- type: 'field' | 'drop-area'
75
+ type: 'field' | 'drop-area' | 'active-area'
70
76
  label: string
71
77
  color: string
72
78
  colorAlpha: string
@@ -293,7 +299,7 @@ function getGapSize(orientation: Orientation, element: HTMLElement): number {
293
299
  }
294
300
  }
295
301
 
296
- return 30
302
+ return MIN_GAP
297
303
  }
298
304
 
299
305
  const fieldChildCache: Record<string, FieldRectChild[]> = {}
@@ -358,6 +364,15 @@ const buildChildren = (
358
364
  continue
359
365
  }
360
366
 
367
+ // Get the rect of the block. Use a cached one if possible.
368
+ const elRect =
369
+ dom.getBlockRect(uuid) ||
370
+ ui.getAbsoluteElementRect(el.getBoundingClientRect())
371
+
372
+ // Calculate the offset to the parent. We can not use el.offsetTop/el.offsetLeft here because the value could be 0.
373
+ const elOffsetTop = elRect.y - field.y
374
+ const elOffsetLeft = elRect.x - field.x
375
+
361
376
  // Last element.
362
377
  if (isLast) {
363
378
  const id = buildChildId(field.field, uuid, 'last', uuid)
@@ -365,18 +380,18 @@ const buildChildren = (
365
380
  childrenForUuid.push({
366
381
  id,
367
382
  width: field.width,
368
- height: field.gap,
383
+ height: MIN_GAP,
369
384
  x: 0,
370
- y: el.offsetTop + el.scrollHeight,
385
+ y: elOffsetTop + el.scrollHeight,
371
386
  label: field.label,
372
387
  })
373
388
  } else {
374
389
  childrenForUuid.push({
375
390
  id,
376
- width: field.gap,
391
+ width: MIN_GAP,
377
392
  height: el.offsetHeight,
378
- x: el.offsetLeft + el.offsetWidth,
379
- y: el.offsetTop,
393
+ x: elOffsetLeft + el.offsetWidth + (field.gap - MIN_GAP) / 2,
394
+ y: elOffsetTop,
380
395
  label: field.label,
381
396
  })
382
397
  }
@@ -397,18 +412,20 @@ const buildChildren = (
397
412
  childrenForUuid.push({
398
413
  id,
399
414
  width: field.width,
400
- height: field.gap,
415
+ height: MIN_GAP,
401
416
  x: 0,
402
- y: el.offsetTop - field.gap / 2,
417
+ y: elOffsetTop - field.gap / 2,
403
418
  label: field.label,
404
419
  })
405
420
  } else {
406
421
  childrenForUuid.push({
407
422
  id,
408
- width: field.gap,
409
- height: Math.max(el.offsetHeight, 30),
410
- x: Math.max(el.offsetLeft - field.gap, -field.gap),
411
- y: el.offsetTop,
423
+ width: MIN_GAP,
424
+ height: Math.max(el.offsetHeight, MIN_GAP),
425
+ x:
426
+ Math.max(elOffsetLeft - field.gap, -field.gap) +
427
+ (field.gap - MIN_GAP) / 2,
428
+ y: elOffsetTop,
412
429
  label: field.label,
413
430
  })
414
431
  }
@@ -495,7 +512,7 @@ const buildEmptyChild = (
495
512
  x: 0,
496
513
  y: 0,
497
514
  width: fieldWidth,
498
- height: fieldHeight > 30 ? 0 : 30,
515
+ height: Math.max(fieldHeight, MIN_GAP),
499
516
  label: getInsertText(field),
500
517
  }
501
518
  }
@@ -524,8 +541,9 @@ const buildFieldRect = (key: string): FieldRect | undefined => {
524
541
  }
525
542
  const x = rect.x
526
543
  const y = rect.y
527
- const height = Math.max(rect.height, 30)
528
- const width = Math.max(rect.width, 30)
544
+ const width = Math.max(rect.width, MIN_GAP)
545
+
546
+ const height = Math.max(rect.height, MIN_GAP)
529
547
  const emptyChild = buildEmptyChild(
530
548
  field,
531
549
  childElements,
@@ -533,7 +551,8 @@ const buildFieldRect = (key: string): FieldRect | undefined => {
533
551
  width,
534
552
  height,
535
553
  )
536
- const gap = Math.max(getGapSize(orientation, field.element), 30)
554
+
555
+ const gap = Math.max(getGapSize(orientation, field.element), MIN_GAP)
537
556
 
538
557
  const fieldRect = {
539
558
  key: field.key,
@@ -566,8 +585,8 @@ const buildDropAreaRect = (area: DropArea): Rectangle => {
566
585
  const dropAreaRect: Rectangle = {
567
586
  x: rect.x,
568
587
  y: rect.y,
569
- width: Math.max(rect.width, 30),
570
- height: Math.max(rect.height, 30),
588
+ width: Math.max(rect.width, MIN_GAP),
589
+ height: Math.max(rect.height, MIN_GAP),
571
590
  }
572
591
 
573
592
  cachedDropAreaRects[area.id] = dropAreaRect
@@ -579,6 +598,17 @@ const colorTealAlpha = rgbaToString(theme.teal.value.normal, 0.7)
579
598
  const colorAccent = rgbaToString(theme.accent.value[800])
580
599
  const colorAccentAlpha = rgbaToString(theme.accent.value[800], 0.7)
581
600
 
601
+ function getRectType(field: BlokkliFieldElement): RectRenderType {
602
+ if (field.nestingLevel >= 3) {
603
+ return RectRenderType.FIELD_4
604
+ } else if (field.nestingLevel >= 2) {
605
+ return RectRenderType.FIELD_3
606
+ } else if (field.nestingLevel >= 1) {
607
+ return RectRenderType.FIELD_2
608
+ }
609
+ return RectRenderType.FIELD_1
610
+ }
611
+
582
612
  class DropTargetRectangleBufferCollector extends RectangleBufferCollector<DrawnRect> {
583
613
  getBufferInfo(): { info: BufferInfo | null; hasChanged: boolean } {
584
614
  const visibleFields = dom.getVisibleFields()
@@ -589,6 +619,7 @@ class DropTargetRectangleBufferCollector extends RectangleBufferCollector<DrawnR
589
619
  for (let i = 0; i < visibleFields.length; i++) {
590
620
  const key = visibleFields[i]
591
621
  const fieldRect = buildFieldRect(key)
622
+
592
623
  if (!fieldRect) {
593
624
  continue
594
625
  }
@@ -598,6 +629,7 @@ class DropTargetRectangleBufferCollector extends RectangleBufferCollector<DrawnR
598
629
  if (this.added.has(child.id)) {
599
630
  continue
600
631
  }
632
+ const type = getRectType(fieldRect.field)
601
633
  this.addRectangle(
602
634
  {
603
635
  id: child.id,
@@ -611,7 +643,8 @@ class DropTargetRectangleBufferCollector extends RectangleBufferCollector<DrawnR
611
643
  height: child.height,
612
644
  field: fieldRect,
613
645
  },
614
- RectRenderType.FIELD,
646
+ type,
647
+ true,
615
648
  )
616
649
  }
617
650
  }
@@ -641,6 +674,7 @@ class DropTargetRectangleBufferCollector extends RectangleBufferCollector<DrawnR
641
674
  height: areaRect.height,
642
675
  },
643
676
  RectRenderType.DROP_AREA,
677
+ false,
644
678
  )
645
679
  }
646
680
 
@@ -659,6 +693,9 @@ class DropTargetRectangleBufferCollector extends RectangleBufferCollector<DrawnR
659
693
  const rects = Object.values(this.rects)
660
694
  for (let i = 0; i < rects.length; i++) {
661
695
  const rect = rects[i]
696
+ if (rect.type === 'active-area') {
697
+ continue
698
+ }
662
699
  if (intersects(box, rect)) {
663
700
  candidates.push(rect)
664
701
  }
@@ -678,6 +715,9 @@ class DropTargetRectangleBufferCollector extends RectangleBufferCollector<DrawnR
678
715
 
679
716
  for (let i = 0; i < rects.length; i++) {
680
717
  const rect = rects[i]
718
+ if (rect.type === 'active-area') {
719
+ continue
720
+ }
681
721
  if (isInsideRect(coord.x, coord.y, rect)) {
682
722
  return rect
683
723
  }
@@ -689,14 +729,91 @@ class DropTargetRectangleBufferCollector extends RectangleBufferCollector<DrawnR
689
729
 
690
730
  const collector = new DropTargetRectangleBufferCollector(gl)
691
731
 
732
+ // Add a rectangle that we will use to display the hovered field area.
733
+ // The vertex shader will dynamically transform the quad to match the currently hovered field area.
734
+ collector.addRectangle(
735
+ {
736
+ id: 'active-hover-rect',
737
+ type: 'active-area',
738
+ label: 'Field Area',
739
+ color: 'red',
740
+ colorAlpha: 'red',
741
+ x: 0,
742
+ y: 0,
743
+ width: ui.artboardSize.value.width,
744
+ height: ui.artboardSize.value.height,
745
+ },
746
+ RectRenderType.ACTIVE_AREA,
747
+ false,
748
+ )
749
+
750
+ const fieldColors = computed(() => {
751
+ return {
752
+ '0': theme.accent.value[900],
753
+ '1': theme.accent.value[400],
754
+ '2': theme.accent.value[600],
755
+ '3': theme.accent.value[500],
756
+ }
757
+ })
758
+
759
+ function getColorForField(field?: FieldRect | null) {
760
+ const nestingLevel = field?.field.nestingLevel || 0
761
+ if (nestingLevel >= 3) {
762
+ return fieldColors.value[3]
763
+ } else if (nestingLevel >= 2) {
764
+ return fieldColors.value[2]
765
+ } else if (nestingLevel >= 1) {
766
+ return fieldColors.value[1]
767
+ }
768
+ return fieldColors.value[0]
769
+ }
770
+
771
+ const activeColorRgb = computed(() => {
772
+ if (active.value?.type === 'drop-area') {
773
+ return theme.teal.value.normal
774
+ }
775
+ return getColorForField(active.value?.field)
776
+ })
777
+
778
+ const activeColorHex = computed(() => {
779
+ if (activeColorRgb.value) {
780
+ return rgbaToString(activeColorRgb.value)
781
+ }
782
+ return ''
783
+ })
784
+
785
+ const activeHoverField = ref<FieldRect | null>(null)
786
+
787
+ const activeHoverRect = computed(() => {
788
+ if (!activeHoverField.value) {
789
+ return [0, 0, 0, 0]
790
+ }
791
+
792
+ const outset = activeHoverField.value.field.nestingLevel === 0 ? 0 : 20
793
+
794
+ return [
795
+ activeHoverField.value.x - outset,
796
+ activeHoverField.value.y - outset,
797
+ activeHoverField.value.width + 2 * outset,
798
+ activeHoverField.value.height + 2 * outset,
799
+ ]
800
+ })
801
+
802
+ const activeHoverColor = computed(() => {
803
+ return getColorForField(activeHoverField.value)
804
+ })
805
+
692
806
  const uniforms = computed(() => {
693
807
  const index = active.value?.index
694
808
  return {
695
- u_color_field_active: toShaderColor(theme.accent.value[700]),
696
- u_color_field_default: toShaderColor(theme.mono.value[400]),
697
- u_color_area_active: toShaderColor(theme.teal.value.normal),
698
- u_color_area_default: toShaderColor(theme.teal.value.normal),
809
+ u_color_field_0: toShaderColor(fieldColors.value[0]),
810
+ u_color_field_1: toShaderColor(fieldColors.value[1]),
811
+ u_color_field_2: toShaderColor(fieldColors.value[2]),
812
+ u_color_field_3: toShaderColor(fieldColors.value[3]),
813
+ u_color_hover_area: toShaderColor(activeHoverColor.value),
814
+ u_color_area: toShaderColor(theme.teal.value.normal),
699
815
  u_active_rect_id: index === undefined ? -1 : index,
816
+ u_active_hover_rect: activeHoverRect.value,
700
817
  }
701
818
  })
702
819
 
@@ -716,6 +833,45 @@ function toCanvasSpaceCoordinates(x: number, y: number): Coord {
716
833
  }
717
834
  }
718
835
 
836
+ function setHoveredFieldArea(box: Rectangle, mouse: Coord) {
837
+ if (active.value?.field) {
838
+ if (activeHoverField.value?.key !== active.value.field.key) {
839
+ activeHoverField.value = active.value.field
840
+ }
841
+ return
842
+ }
843
+ const fields = Object.values(fieldCache)
844
+
845
+ let highestNestingLevel = 0
846
+ let candidate: FieldRect | null = null
847
+
848
+ for (let i = 0; i < fields.length; i++) {
849
+ const field = fields[i]
850
+ if (!field.canAddChildren) {
851
+ continue
852
+ }
853
+ if (
854
+ isInsideRect(mouse.x, mouse.y, field) &&
855
+ field.field.nestingLevel >= highestNestingLevel
856
+ ) {
857
+ candidate = field
858
+ highestNestingLevel = field.field.nestingLevel
859
+ continue
860
+ }
861
+ if (
862
+ intersects(box, field) &&
863
+ field.field.nestingLevel >= highestNestingLevel
864
+ ) {
865
+ highestNestingLevel = field.field.nestingLevel
866
+ candidate = field
867
+ }
868
+ }
869
+
870
+ if (candidate && candidate.key !== activeHoverField.value?.key) {
871
+ activeHoverField.value = candidate
872
+ }
873
+ }
874
+
719
875
  onBlokkliEvent('canvas:draw', () => {
720
876
  const scale = ui.artboardScale.value
721
877
  const offset = { ...ui.artboardOffset.value }
@@ -734,10 +890,8 @@ onBlokkliEvent('canvas:draw', () => {
734
890
  animation.setSharedUniforms(gl, programInfo)
735
891
  }
736
892
 
737
- const isInsideClipped = cursorIsInsideClipped()
738
-
739
893
  if (!props.isTouch) {
740
- if (isInsideClipped) {
894
+ if (cursorIsInsideClipped()) {
741
895
  const closest = collector.getClosestIntersectingRect(
742
896
  dragBox.value,
743
897
  mouseAbsolute,
@@ -753,6 +907,7 @@ onBlokkliEvent('canvas:draw', () => {
753
907
 
754
908
  // WebGL rendering.
755
909
  if (programInfo && gl) {
910
+ setHoveredFieldArea(dragBox.value, mouseAbsolute)
756
911
  setUniforms(programInfo, uniforms.value)
757
912
 
758
913
  // Nothing to draw.
@@ -10,18 +10,31 @@ uniform float u_offset_x;
10
10
  uniform float u_offset_y;
11
11
  uniform vec2 u_resolution;
12
12
  uniform float u_active_rect_id;
13
- uniform vec3 u_color_field_default;
14
- uniform vec3 u_color_field_active;
15
- uniform vec3 u_color_area_default;
16
- uniform vec3 u_color_area_active;
13
+ uniform vec3 u_color_field_0;
14
+ uniform vec3 u_color_field_1;
15
+ uniform vec3 u_color_field_2;
16
+ uniform vec3 u_color_field_3;
17
+ uniform vec3 u_color_area;
18
+ uniform vec3 u_color_hover_area;
19
+ uniform vec4 u_active_hover_rect;
17
20
  uniform float u_dpi;
18
21
 
19
22
  varying vec4 v_quad;
20
23
  varying float v_intersecting;
21
- varying vec3 v_color_default;
22
- varying vec3 v_color_active;
24
+ varying float v_is_hover_area;
25
+ varying vec3 v_color;
26
+
27
+ vec4 getQuad() {
28
+ if (a_rect_type >= 5.0) {
29
+ // Return the quad coming in via uniform.
30
+ return u_active_hover_rect;
31
+ }
32
+ // Use the provided quad.
33
+ return a_quad;
34
+ }
23
35
 
24
36
  void main() {
37
+ vec4 quad = getQuad();
25
38
  vec2 offsetPosition = a_position * u_scale;
26
39
  offsetPosition.x += u_offset_x;
27
40
  offsetPosition.y += u_offset_y;
@@ -38,20 +51,27 @@ void main() {
38
51
 
39
52
  // Transform the quad.
40
53
  vec4 transformed_quad = vec4(
41
- (a_quad.x * u_scale + u_offset_x) * u_dpi,
42
- (u_resolution.y - a_quad.y * u_scale - a_quad.w * u_scale - u_offset_y) *
43
- u_dpi,
44
- a_quad.z * u_scale * u_dpi,
45
- a_quad.w * u_scale * u_dpi
54
+ (quad.x * u_scale + u_offset_x) * u_dpi,
55
+ (u_resolution.y - quad.y * u_scale - quad.w * u_scale - u_offset_y) * u_dpi,
56
+ quad.z * u_scale * u_dpi,
57
+ quad.w * u_scale * u_dpi
46
58
  );
47
59
  v_quad = transformed_quad;
48
60
 
61
+ v_is_hover_area = a_rect_type >= 5.0 ? 1.0 : 0.0;
62
+
49
63
  // Set correct colors based on type.
50
- if (a_rect_type < 0.5) {
51
- v_color_default = u_color_field_default;
52
- v_color_active = u_color_field_active;
64
+ if (a_rect_type < 1.0) {
65
+ v_color = u_color_area;
66
+ } else if (a_rect_type < 2.0) {
67
+ v_color = u_color_field_0;
68
+ } else if (a_rect_type < 3.0) {
69
+ v_color = u_color_field_1;
70
+ } else if (a_rect_type < 4.0) {
71
+ v_color = u_color_field_2;
72
+ } else if (a_rect_type < 5.0) {
73
+ v_color = u_color_field_3;
53
74
  } else {
54
- v_color_default = u_color_area_default;
55
- v_color_active = u_color_area_active;
75
+ v_color = u_color_hover_area;
56
76
  }
57
77
  }
@@ -376,6 +376,10 @@ onBlokkliEvent('dragging:start', (e) => {
376
376
  document.addEventListener('pointermove', onMouseMove, { capture: true })
377
377
  }
378
378
  }
379
+
380
+ // Before showing the drop targets we update all currently visible rects to
381
+ // ensure the user sees the correct drop targets right away.
382
+ dom.updateVisibleRects()
379
383
  dragItems.value = e.items
380
384
  })
381
385
 
@@ -38,14 +38,6 @@ function onClick(items: DraggableExistingBlock[]) {
38
38
  )
39
39
  }
40
40
 
41
- const findField = (uuid: string): BlokkliFieldElement | undefined => {
42
- try {
43
- return dom.getBlockField(uuid)
44
- } catch (_e) {
45
- // Noop.
46
- }
47
- }
48
-
49
41
  const canDuplicate = computed<boolean>(() => {
50
42
  if (state.editMode.value !== 'editing') {
51
43
  return false
@@ -57,11 +49,7 @@ const canDuplicate = computed<boolean>(() => {
57
49
  const selectedCount = selection.blocks.value.length
58
50
  for (let i = 0; i < selectedCount; i++) {
59
51
  const block = selection.blocks.value[i]
60
- const field = findField(block.uuid)
61
- if (!field) {
62
- return false
63
- }
64
-
52
+ const field = dom.getBlockField(block.uuid)
65
53
  const count = state.getFieldBlockCount(field.key)
66
54
 
67
55
  // Early return if the field is already full.
@@ -3,8 +3,8 @@
3
3
  id="mask"
4
4
  v-model="isActive"
5
5
  :label="$t('maskToggle', 'Toggle non-editable areas')"
6
- :title-on="$t('maskShow', 'Show non-editable areas')"
7
- :title-off="$t('maskHide', 'Hide non-editable areas')"
6
+ :title-on="$t('maskHide', 'Hide non-editable areas')"
7
+ :title-off="$t('maskShow', 'Show non-editable areas')"
8
8
  :tour-text="
9
9
  $t(
10
10
  'maskTourText',
@@ -12,7 +12,7 @@
12
12
  icon="history"
13
13
  weight="-100"
14
14
  >
15
- <div class="bk bk-history bk-control" @wheel.capture.stop>
15
+ <div class="bk bk-history bk-control">
16
16
  <ul v-if="mapped.length">
17
17
  <li
18
18
  v-for="item in mapped"
@@ -15,7 +15,7 @@ const { $t, state } = useBlokkli()
15
15
  const props = defineProps<{
16
16
  label: string
17
17
  property: string
18
- modelValue: string
18
+ modelValue?: string
19
19
  }>()
20
20
 
21
21
  const emit = defineEmits(['update:modelValue'])
@@ -25,7 +25,7 @@ const checked = computed({
25
25
  return props.modelValue === '1'
26
26
  },
27
27
  set(v: any) {
28
- emit('update:modelValue', v ? '1' : '')
28
+ emit('update:modelValue', v ? '1' : '0')
29
29
  },
30
30
  })
31
31
 
@@ -1,10 +1,10 @@
1
1
  <template>
2
2
  <div
3
3
  class="bk-blokkli-item-options-checkboxes"
4
- :class="{ 'bk-is-active': isOpen }"
4
+ :class="{ 'bk-is-active': isOpen, 'bk-is-grouped': isGrouped }"
5
5
  >
6
- <button @click="isOpen = !isOpen">
7
- <span>{{ label }}</span>
6
+ <button v-if="!isGrouped" @click="isOpen = !isOpen">
7
+ <span>{{ visibleLabel }}</span>
8
8
  <div>
9
9
  <template v-if="checked.length < 4">
10
10
  <span v-for="item in checked" :key="item" class="bk-pill">{{
@@ -15,20 +15,20 @@
15
15
  </div>
16
16
  <Icon name="caret" />
17
17
  </button>
18
- <div v-if="isOpen">
18
+ <div v-if="isOpen || isGrouped">
19
19
  <label
20
- v-for="option in mappedOptions"
21
- :key="option.key"
20
+ v-for="option in options"
21
+ :key="option.value"
22
22
  class="bk-blokkli-item-options-checkbox"
23
23
  >
24
24
  <input
25
25
  v-model="checked"
26
26
  type="checkbox"
27
27
  class="peer"
28
- :value="option.key"
28
+ :value="option.value"
29
29
  />
30
30
  <div />
31
- <span>{{ option.value }}</span>
31
+ <span>{{ option.label }}</span>
32
32
  </label>
33
33
  </div>
34
34
  </div>
@@ -38,21 +38,23 @@
38
38
  import { computed, ref, useBlokkli } from '#imports'
39
39
  import { Icon } from '#blokkli/components'
40
40
  import defineCommands from '#blokkli/helpers/composables/defineCommands'
41
+ import { BK_VISIBLE_LANGUAGES } from '#blokkli/helpers/symbols'
41
42
 
42
43
  const { $t, state } = useBlokkli()
43
44
 
44
45
  const props = defineProps<{
45
46
  label: string
46
47
  property: string
47
- modelValue: string
48
- options: Record<string, string>
48
+ modelValue?: string
49
+ options: { value: string; label: string }[]
50
+ isGrouped?: boolean
49
51
  }>()
50
52
 
51
53
  const emit = defineEmits(['update:modelValue'])
52
54
 
53
55
  const isOpen = ref(false)
54
56
 
55
- const optionOrder = computed(() => Object.keys(props.options))
57
+ const optionOrder = computed(() => props.options.map((v) => v.value))
56
58
 
57
59
  const checked = computed<string[]>({
58
60
  get() {
@@ -72,6 +74,13 @@ const checked = computed<string[]>({
72
74
  },
73
75
  })
74
76
 
77
+ const visibleLabel = computed(() => {
78
+ if (props.property === BK_VISIBLE_LANGUAGES && !checked.value.length) {
79
+ return $t('optionBkVisibleLanguagesAll', 'All languages')
80
+ }
81
+ return props.label
82
+ })
83
+
75
84
  const toggle = (key: string) => {
76
85
  if (!checked.value.includes(key)) {
77
86
  checked.value = [...checked.value, key]
@@ -80,43 +89,37 @@ const toggle = (key: string) => {
80
89
  }
81
90
  }
82
91
 
83
- const mappedOptions = computed(() => {
84
- return Object.entries(props.options).map(([key, value]) => {
85
- return { key, value }
86
- })
87
- })
88
-
89
92
  defineCommands(() => {
90
93
  if (state.editMode.value !== 'editing') {
91
94
  return
92
95
  }
93
- return optionOrder.value.map((key) => {
94
- if (checked.value.includes(key)) {
96
+ return props.options.map((option) => {
97
+ if (checked.value.includes(option.value)) {
95
98
  return {
96
- id: 'options:' + props.property + ':select:' + key,
99
+ id: 'options:' + props.property + ':select:' + option.value,
97
100
  label: $t(
98
101
  'optionsCommand.unselectCheckboxValue',
99
102
  'Unselect "@value" in "@option"',
100
103
  )
101
104
  .replace('@option', props.label)
102
- .replace('@value', props.options[key]),
105
+ .replace('@value', option.label),
103
106
  group: 'selection',
104
107
  icon: 'form',
105
- callback: () => toggle(key),
108
+ callback: () => toggle(option.value),
106
109
  }
107
110
  }
108
111
 
109
112
  return {
110
- id: 'options:' + props.property + ':unselect:' + key,
113
+ id: 'options:' + props.property + ':unselect:' + option.value,
111
114
  label: $t(
112
115
  'optionsCommand.selectCheckboxValue',
113
116
  'Select "@value" in "@option"',
114
117
  )
115
118
  .replace('@option', props.label)
116
- .replace('@value', props.options[key]),
119
+ .replace('@value', option.label),
117
120
  group: 'selection',
118
121
  icon: 'form',
119
- callback: () => toggle(key),
122
+ callback: () => toggle(option.value),
120
123
  }
121
124
  })
122
125
  })
@@ -16,7 +16,7 @@ import { computed } from '#imports'
16
16
 
17
17
  const props = defineProps<{
18
18
  label: string
19
- modelValue: string
19
+ modelValue?: string
20
20
  }>()
21
21
 
22
22
  const emit = defineEmits(['update:modelValue'])