@blokkli/editor 2.0.0-alpha.15 → 2.0.0-alpha.17
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.
- package/dist/module.json +1 -1
- package/dist/module.mjs +330 -93
- package/dist/modules/drupal/graphql/base/fragment.blokkliProps.graphql +1 -1
- package/dist/modules/drupal/graphql/features/comments.graphql +11 -8
- package/dist/modules/drupal/runtime/adapter/index.js +2 -2
- package/dist/runtime/blokkliPlugins/ItemAction/index.vue +1 -3
- package/dist/runtime/components/Blocks/FromLibrary/index.vue +4 -2
- package/dist/runtime/components/BlokkliEditable.vue +22 -4
- package/dist/runtime/components/BlokkliProvider.vue +29 -20
- package/dist/runtime/components/BlokkliProvider.vue.d.ts +2 -1
- package/dist/runtime/components/Edit/Actions/index.vue +9 -4
- package/dist/runtime/components/Edit/AnimationCanvas/index.vue +420 -25
- package/dist/runtime/components/Edit/ArtboardTooltip/index.vue +80 -0
- package/dist/runtime/components/Edit/ArtboardTooltip/index.vue.d.ts +32 -0
- package/dist/runtime/components/Edit/Banner/index.vue +51 -0
- package/dist/runtime/components/Edit/Banner/index.vue.d.ts +18 -0
- package/dist/runtime/components/Edit/Dialog/index.vue +3 -0
- package/dist/runtime/components/Edit/Dialog/index.vue.d.ts +2 -0
- package/dist/runtime/components/Edit/EditIndicator.vue +118 -44
- package/dist/runtime/components/Edit/EditIndicator.vue.d.ts +3 -0
- package/dist/runtime/components/Edit/EditProvider.vue +79 -22
- package/dist/runtime/components/Edit/EditProvider.vue.d.ts +2 -0
- package/dist/runtime/components/Edit/Features/Analyze/Overlay/index.vue +19 -20
- package/dist/runtime/components/Edit/Features/BlockAddList/index.vue +1 -1
- package/dist/runtime/components/Edit/Features/CommandPalette/index.vue +2 -0
- package/dist/runtime/components/Edit/Features/Comments/AddForm/index.vue +35 -20
- package/dist/runtime/components/Edit/Features/Comments/AddForm/index.vue.d.ts +5 -3
- package/dist/runtime/components/Edit/Features/Comments/CommentInput/index.vue +29 -0
- package/dist/runtime/components/Edit/Features/Comments/CommentInput/index.vue.d.ts +13 -0
- package/dist/runtime/components/Edit/Features/Comments/Overlay/Item/index.vue +22 -16
- package/dist/runtime/components/Edit/Features/Comments/Overlay/Item/index.vue.d.ts +1 -0
- package/dist/runtime/components/Edit/Features/Comments/Overlay/index.vue +15 -6
- package/dist/runtime/components/Edit/Features/Comments/index.vue +20 -8
- package/dist/runtime/components/Edit/Features/Debug/Rects/index.vue +26 -35
- package/dist/runtime/components/Edit/Features/Debug/Renderer.vue +240 -0
- package/dist/runtime/components/Edit/Features/Debug/Renderer.vue.d.ts +6 -0
- package/dist/runtime/components/Edit/Features/Debug/index.vue +4 -165
- package/dist/runtime/components/Edit/Features/DraggingOverlay/DragItems/index.vue +1 -1
- package/dist/runtime/components/Edit/Features/DraggingOverlay/DropTargets/fragment.glsl +7 -1
- package/dist/runtime/components/Edit/Features/DraggingOverlay/DropTargets/index.vue +62 -39
- package/dist/runtime/components/Edit/Features/DraggingOverlay/DropTargets/vertex.glsl +1 -0
- package/dist/runtime/components/Edit/Features/Edit/index.vue +1 -1
- package/dist/runtime/components/Edit/Features/EditableField/Overlay/Frame/index.vue +63 -3
- package/dist/runtime/components/Edit/Features/EditableField/Overlay/Plaintext/index.vue +13 -9
- package/dist/runtime/components/Edit/Features/EditableField/Overlay/index.vue +17 -76
- package/dist/runtime/components/Edit/Features/EditableField/index.vue +1 -1
- package/dist/runtime/components/Edit/Features/History/index.vue +5 -2
- package/dist/runtime/components/Edit/Features/Hover/Overlay/fragment.glsl +139 -0
- package/dist/runtime/components/Edit/Features/Hover/Overlay/index.vue +270 -0
- package/dist/runtime/components/Edit/Features/Hover/Overlay/index.vue.d.ts +6 -0
- package/dist/runtime/components/Edit/Features/Hover/Overlay/vertex.glsl +117 -0
- package/dist/runtime/components/Edit/Features/Hover/index.vue +25 -0
- package/dist/runtime/components/Edit/Features/Library/LibraryDialog/index.vue +19 -27
- package/dist/runtime/components/Edit/Features/Library/ReusableDialog/index.vue +27 -23
- package/dist/runtime/components/Edit/Features/Library/index.vue +2 -1
- package/dist/runtime/components/Edit/Features/MultiSelect/Overlay/index.vue +34 -27
- package/dist/runtime/components/Edit/Features/MultiSelect/index.vue +2 -4
- package/dist/runtime/components/Edit/Features/Options/Form/Item.vue +6 -1
- package/dist/runtime/components/Edit/Features/Options/Form/index.vue +1 -0
- package/dist/runtime/components/Edit/Features/Ownership/Renderer.vue +35 -0
- package/dist/runtime/components/Edit/Features/Ownership/Renderer.vue.d.ts +6 -0
- package/dist/runtime/components/Edit/Features/Ownership/index.vue +7 -25
- package/dist/runtime/components/Edit/Features/ProxyView/index.vue +5 -1
- package/dist/runtime/components/Edit/Features/Publish/Dialog/Summary.vue +2 -2
- package/dist/runtime/components/Edit/Features/Publish/Dialog/index.vue +17 -7
- package/dist/runtime/components/Edit/Features/Selection/AddButtons/Overlay/index.vue +39 -74
- package/dist/runtime/components/Edit/Features/Selection/AddButtons/Overlay/index.vue.d.ts +4 -2
- package/dist/runtime/components/Edit/Features/Selection/AddButtons/Renderer/fragment.glsl +106 -0
- package/dist/runtime/components/Edit/Features/Selection/AddButtons/Renderer/index.vue +417 -0
- package/dist/runtime/components/Edit/Features/Selection/AddButtons/Renderer/index.vue.d.ts +32 -0
- package/dist/runtime/components/Edit/Features/Selection/AddButtons/Renderer/vertex.glsl +102 -0
- package/dist/runtime/components/Edit/Features/Selection/AddButtons/index.vue +33 -106
- package/dist/runtime/components/Edit/Features/Selection/Overlay/index.vue +88 -29
- package/dist/runtime/components/Edit/Features/Selection/Overlay/index.vue.d.ts +2 -0
- package/dist/runtime/components/Edit/Features/Selection/Overlay/vertex.glsl +11 -2
- package/dist/runtime/components/Edit/Features/Selection/index.vue +26 -19
- package/dist/runtime/components/Edit/Features/Translations/Banner/index.vue +17 -11
- package/dist/runtime/components/Edit/Features/Translations/index.vue +13 -16
- package/dist/runtime/components/Edit/Form/Text/index.vue +2 -1
- package/dist/runtime/components/Edit/Form/Text/index.vue.d.ts +1 -0
- package/dist/runtime/components/Edit/Indicators/index.vue +1 -1
- package/dist/runtime/components/Edit/Konami/Game/index.vue +5 -5
- package/dist/runtime/components/Edit/index.d.ts +5 -3
- package/dist/runtime/components/Edit/index.js +8 -4
- package/dist/runtime/composables/defineBlokkli.js +5 -3
- package/dist/runtime/css/output.css +1 -1
- package/dist/runtime/helpers/animationProvider.d.ts +34 -1
- package/dist/runtime/helpers/animationProvider.js +175 -48
- package/dist/runtime/helpers/composables/defineRenderer.d.ts +8 -0
- package/dist/runtime/helpers/composables/defineRenderer.js +8 -0
- package/dist/runtime/helpers/composables/useDelayedIntersectionObserver.d.ts +1 -1
- package/dist/runtime/helpers/composables/useDelayedIntersectionObserver.js +3 -2
- package/dist/runtime/helpers/composables/useStickyToolbar.d.ts +4 -1
- package/dist/runtime/helpers/composables/useStickyToolbar.js +53 -35
- package/dist/runtime/helpers/dom/index.d.ts +1 -0
- package/dist/runtime/helpers/domProvider.d.ts +46 -0
- package/dist/runtime/helpers/domProvider.js +101 -7
- package/dist/runtime/helpers/editableProvider.d.ts +14 -0
- package/dist/runtime/helpers/editableProvider.js +144 -0
- package/dist/runtime/helpers/stateProvider.d.ts +6 -2
- package/dist/runtime/helpers/stateProvider.js +66 -3
- package/dist/runtime/helpers/storageProvider.d.ts +3 -2
- package/dist/runtime/helpers/storageProvider.js +6 -2
- package/dist/runtime/helpers/symbols.d.ts +1 -0
- package/dist/runtime/helpers/symbols.js +1 -0
- package/dist/runtime/helpers/uiProvider.d.ts +8 -1
- package/dist/runtime/helpers/uiProvider.js +34 -2
- package/dist/runtime/helpers/webgl/index.d.ts +11 -2
- package/dist/runtime/helpers/webgl/index.js +162 -7
- package/dist/runtime/plugins/blokkliEditable.js +74 -3
- package/dist/runtime/types/index.d.ts +13 -1
- package/package.json +1 -1
- package/dist/runtime/components/Edit/DragInteractions/index.vue +0 -401
- package/dist/runtime/components/Edit/Features/Selection/AddButtons/AddButtonsField.vue +0 -54
- package/dist/runtime/components/Edit/Features/Selection/AddButtons/AddButtonsField.vue.d.ts +0 -14
- /package/dist/runtime/components/Edit/{DragInteractions → Features/Hover}/index.vue.d.ts +0 -0
|
@@ -1,26 +1,15 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<ArtboardTooltip
|
|
3
3
|
v-if="loaded"
|
|
4
|
-
|
|
5
|
-
:
|
|
6
|
-
|
|
4
|
+
id="editable"
|
|
5
|
+
:title
|
|
6
|
+
:anchor-el="element"
|
|
7
|
+
placement-y="top"
|
|
8
|
+
class="bk-editable-field"
|
|
9
|
+
close-icon="check"
|
|
10
|
+
@close="save"
|
|
7
11
|
>
|
|
8
12
|
<form ref="form" class="bk-editable-field-input" @submit.prevent="close">
|
|
9
|
-
<div class="bk bk-editable-field-buttons">
|
|
10
|
-
<h3>
|
|
11
|
-
<ItemIcon :bundle="itemBundle" />
|
|
12
|
-
<span>{{ title }}</span>
|
|
13
|
-
</h3>
|
|
14
|
-
<button @click.prevent="cancel">
|
|
15
|
-
<Icon name="close" />
|
|
16
|
-
<span>{{ $t("cancel", "Cancel") }}</span>
|
|
17
|
-
</button>
|
|
18
|
-
<button :disabled="!hasChanged" type="submit" @click.prevent="save">
|
|
19
|
-
<Icon name="save" />
|
|
20
|
-
<span>{{ $t("save", "Save") }}</span>
|
|
21
|
-
</button>
|
|
22
|
-
</div>
|
|
23
|
-
|
|
24
13
|
<div ref="input">
|
|
25
14
|
<InputContenteditable
|
|
26
15
|
v-if="config.type === 'markup'"
|
|
@@ -50,21 +39,24 @@
|
|
|
50
39
|
/>
|
|
51
40
|
</div>
|
|
52
41
|
|
|
53
|
-
<div
|
|
42
|
+
<div class="bk bk-editable-field-info">
|
|
43
|
+
<button :disabled="!hasChanged" @click.prevent="cancel">
|
|
44
|
+
{{ $t("editableFieldDiscard", "Discard") }}
|
|
45
|
+
</button>
|
|
54
46
|
<div v-if="errorText" class="bk-editable-field-info-error">
|
|
55
47
|
{{ errorText }}
|
|
56
48
|
</div>
|
|
57
|
-
<div class="bk-editable-field-info-count">
|
|
49
|
+
<div v-if="!isMarkup" class="bk-editable-field-info-count">
|
|
58
50
|
<span>{{ count }}</span>
|
|
59
51
|
<span v-if="maxlength >= 1"> / {{ maxlength }}</span>
|
|
60
52
|
</div>
|
|
61
53
|
</div>
|
|
62
54
|
</form>
|
|
63
|
-
</
|
|
55
|
+
</ArtboardTooltip>
|
|
64
56
|
</template>
|
|
65
57
|
|
|
66
58
|
<script setup>
|
|
67
|
-
import {
|
|
59
|
+
import { ArtboardTooltip } from "#blokkli/components";
|
|
68
60
|
import {
|
|
69
61
|
computed,
|
|
70
62
|
ref,
|
|
@@ -74,12 +66,12 @@ import {
|
|
|
74
66
|
useBlokkli,
|
|
75
67
|
nextTick
|
|
76
68
|
} from "#imports";
|
|
77
|
-
import { falsy
|
|
69
|
+
import { falsy } from "#blokkli/helpers";
|
|
78
70
|
import InputPlaintext from "./Plaintext/index.vue";
|
|
79
71
|
import InputContenteditable from "./Contenteditable/index.vue";
|
|
80
72
|
import InputFrame from "./Frame/index.vue";
|
|
81
73
|
import onBlokkliEvent from "#blokkli/helpers/composables/onBlokkliEvent";
|
|
82
|
-
const { eventBus,
|
|
74
|
+
const { eventBus, selection, state, adapter, $t, types } = useBlokkli();
|
|
83
75
|
const props = defineProps({
|
|
84
76
|
fieldName: { type: String, required: true },
|
|
85
77
|
host: { type: Object, required: true },
|
|
@@ -102,41 +94,13 @@ const save = () => {
|
|
|
102
94
|
};
|
|
103
95
|
onBlokkliEvent("window:clickAway", save);
|
|
104
96
|
const getElement = () => props.element;
|
|
105
|
-
const alignment = computed(() => {
|
|
106
|
-
if (props.element) {
|
|
107
|
-
const style2 = window.getComputedStyle(props.element);
|
|
108
|
-
if (style2.textAlign === "left" || style2.textAlign === "center" || style2.textAlign === "right") {
|
|
109
|
-
return style2.textAlign;
|
|
110
|
-
} else if (style2.textAlign === "start") {
|
|
111
|
-
return "left";
|
|
112
|
-
} else if (style2.textAlign === "end") {
|
|
113
|
-
return "right";
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
return "center";
|
|
117
|
-
});
|
|
118
97
|
const scrollHeight = ref(0);
|
|
119
98
|
const loaded = ref(false);
|
|
120
99
|
const originalText = ref(props.value || "");
|
|
121
100
|
const modelValue = ref("");
|
|
122
|
-
const width = ref(320);
|
|
123
101
|
const inputStyle = ref({});
|
|
124
102
|
const form = ref(null);
|
|
125
|
-
const root = ref(null);
|
|
126
103
|
const input = ref(null);
|
|
127
|
-
const x = ref(0);
|
|
128
|
-
const y = ref(0);
|
|
129
|
-
const style = computed(() => {
|
|
130
|
-
if (ui.isMobile.value) {
|
|
131
|
-
return {};
|
|
132
|
-
} else {
|
|
133
|
-
return {
|
|
134
|
-
width: width.value + "px",
|
|
135
|
-
top: y.value + "px",
|
|
136
|
-
left: x.value + "px"
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
104
|
const hasChanged = computed(() => modelValue.value !== originalText.value);
|
|
141
105
|
const itemBundle = computed(() => {
|
|
142
106
|
if ("itemBundle" in props.host) {
|
|
@@ -238,29 +202,6 @@ const focusInput = (el) => {
|
|
|
238
202
|
focusInput(iframe.contentDocument);
|
|
239
203
|
}
|
|
240
204
|
};
|
|
241
|
-
const onAnimationFrame = () => {
|
|
242
|
-
if (ui.isMobile.value) {
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
const elementRect = props.element.getBoundingClientRect();
|
|
246
|
-
const height = form.value?.scrollHeight || 100;
|
|
247
|
-
const newWidth = Math.min(Math.max(elementRect.width, 360), 1e3);
|
|
248
|
-
const ideal = findIdealRectPosition(
|
|
249
|
-
ui.viewportBlockingRects.value,
|
|
250
|
-
{
|
|
251
|
-
x: alignment.value === "left" ? elementRect.x : elementRect.x + (Math.max(elementRect.width, 360) - newWidth) / 2,
|
|
252
|
-
y: elementRect.y - height - 20,
|
|
253
|
-
height,
|
|
254
|
-
width: newWidth
|
|
255
|
-
},
|
|
256
|
-
ui.visibleViewportPadded.value
|
|
257
|
-
);
|
|
258
|
-
x.value = ideal.x;
|
|
259
|
-
y.value = ideal.y + height;
|
|
260
|
-
width.value = newWidth;
|
|
261
|
-
};
|
|
262
|
-
onAnimationFrame();
|
|
263
|
-
onBlokkliEvent("animationFrame", onAnimationFrame);
|
|
264
205
|
onMounted(() => {
|
|
265
206
|
const el = getElement();
|
|
266
207
|
if (props.isComponent) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Teleport to="body">
|
|
3
|
-
<BlokkliTransition name="
|
|
3
|
+
<BlokkliTransition name="caret-tooltip" :enabled="hasTransition">
|
|
4
4
|
<Overlay v-if="editable" v-bind="editable" :key="key" @close="close" />
|
|
5
5
|
</BlokkliTransition>
|
|
6
6
|
</Teleport>
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
id="history"
|
|
4
4
|
v-slot="{ scrolledToEnd }"
|
|
5
5
|
:title="$t('history', 'History')"
|
|
6
|
+
edit-only
|
|
6
7
|
:tour-text="
|
|
7
8
|
$t(
|
|
8
9
|
'historyTourText',
|
|
@@ -74,9 +75,11 @@ const { state, $t, ui, selection, eventBus } = useBlokkli();
|
|
|
74
75
|
const { mutations, currentMutationIndex, mutateWithLoadingState } = state;
|
|
75
76
|
const mutationsCount = computed(() => mutations.value.length);
|
|
76
77
|
const useMouseForHistory = computed(() => settings.value.useMouseButtons);
|
|
77
|
-
const canUndo = computed(
|
|
78
|
+
const canUndo = computed(
|
|
79
|
+
() => currentMutationIndex.value >= 0 && state.canEdit.value
|
|
80
|
+
);
|
|
78
81
|
const canRedo = computed(
|
|
79
|
-
() => currentMutationIndex.value < mutationsCount.value - 1
|
|
82
|
+
() => currentMutationIndex.value < mutationsCount.value - 1 && state.canEdit.value
|
|
80
83
|
);
|
|
81
84
|
const selectionAtHistoryIndex = /* @__PURE__ */ new Map();
|
|
82
85
|
function updateCurrentHistorySelection() {
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
precision highp float;
|
|
2
|
+
|
|
3
|
+
varying vec4 v_quad;
|
|
4
|
+
varying vec4 v_rect_radius;
|
|
5
|
+
varying vec2 v_rect_size;
|
|
6
|
+
varying vec2 v_rect_center;
|
|
7
|
+
varying float v_rect_type;
|
|
8
|
+
varying vec2 v_quad_artboard_pos;
|
|
9
|
+
|
|
10
|
+
uniform float u_dpi;
|
|
11
|
+
uniform float u_scale;
|
|
12
|
+
uniform float u_offset_x;
|
|
13
|
+
uniform float u_offset_y;
|
|
14
|
+
uniform vec2 u_resolution;
|
|
15
|
+
uniform vec3 u_color_mono;
|
|
16
|
+
uniform vec3 u_color_accent;
|
|
17
|
+
uniform vec3 u_color_teal;
|
|
18
|
+
uniform vec3 u_color_white;
|
|
19
|
+
uniform vec3 u_color_lime;
|
|
20
|
+
|
|
21
|
+
int pseudoQuadrant(vec2 p) {
|
|
22
|
+
return int(floor(step(0.0, p.x) + 2.0 * step(0.0, -p.y)));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
float sdRoundBox(vec2 p, vec2 b, vec4 radii) {
|
|
26
|
+
int idx = pseudoQuadrant(p);
|
|
27
|
+
float cr;
|
|
28
|
+
if (idx == 0) cr = radii[0];
|
|
29
|
+
else if (idx == 1) cr = radii[1];
|
|
30
|
+
else if (idx == 2) cr = radii[3];
|
|
31
|
+
else cr = radii[2];
|
|
32
|
+
vec2 q = abs(p) - b + cr;
|
|
33
|
+
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - cr;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
void main() {
|
|
37
|
+
vec2 size = v_rect_size;
|
|
38
|
+
vec4 u_cornerRadii = min(v_rect_radius, min(size.x, size.y) / 2.0);
|
|
39
|
+
vec4 r = u_cornerRadii;
|
|
40
|
+
|
|
41
|
+
vec2 posRelativeToQuad = gl_FragCoord.xy - v_rect_center;
|
|
42
|
+
|
|
43
|
+
float mainDist = sdRoundBox(posRelativeToQuad, size / 2.0, r);
|
|
44
|
+
|
|
45
|
+
// For editable fields (type 2), render both fill and solid border
|
|
46
|
+
if (v_rect_type > 1.5 && v_rect_type < 2.5) {
|
|
47
|
+
float u_edgeSoftness = 1.0;
|
|
48
|
+
float borderThickness = 1.5 * u_dpi;
|
|
49
|
+
float u_borderSoftness = 1.0;
|
|
50
|
+
|
|
51
|
+
// Render fill
|
|
52
|
+
float fillAlpha = 1.0 - smoothstep(-u_edgeSoftness, 0.0, mainDist);
|
|
53
|
+
vec4 fillColor = vec4(u_color_teal, 0.2);
|
|
54
|
+
|
|
55
|
+
// Render solid border (non-dashed)
|
|
56
|
+
float borderAlpha =
|
|
57
|
+
1.0 - smoothstep(-u_borderSoftness, 0.0, abs(mainDist) - borderThickness);
|
|
58
|
+
vec4 borderColor = vec4(u_color_teal, 1.0);
|
|
59
|
+
|
|
60
|
+
// Combine fill and border
|
|
61
|
+
vec4 combined = mix(
|
|
62
|
+
vec4(fillColor.rgb, fillAlpha * fillColor.a),
|
|
63
|
+
borderColor,
|
|
64
|
+
borderAlpha * borderColor.a
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
gl_FragColor = combined;
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// For blocks (type 0, 1, and 3), render border
|
|
72
|
+
float borderThickness = 1.5 * u_dpi;
|
|
73
|
+
float u_borderSoftness = 1.0;
|
|
74
|
+
|
|
75
|
+
float borderAlpha =
|
|
76
|
+
1.0 - smoothstep(-u_borderSoftness, 0.0, abs(mainDist) - borderThickness);
|
|
77
|
+
|
|
78
|
+
// Select color based on type: 0 = mono, 1 = accent, 3 = white (inverted), 4 = lime (library)
|
|
79
|
+
vec3 color = u_color_mono;
|
|
80
|
+
if (v_rect_type > 3.5) {
|
|
81
|
+
color = u_color_lime;
|
|
82
|
+
} else if (v_rect_type > 2.5) {
|
|
83
|
+
color = u_color_white;
|
|
84
|
+
} else if (v_rect_type > 0.5) {
|
|
85
|
+
color = u_color_accent;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Apply dashed pattern for all blocks
|
|
89
|
+
// Calculate actual perimeter distance for proper dashing
|
|
90
|
+
vec2 halfSize = size / 2.0;
|
|
91
|
+
vec2 p = posRelativeToQuad;
|
|
92
|
+
vec2 absP = abs(p);
|
|
93
|
+
|
|
94
|
+
// Determine which edge/corner we're on and calculate perimeter distance
|
|
95
|
+
float perimeterDistance = 0.0;
|
|
96
|
+
|
|
97
|
+
// Check which edge we're closest to
|
|
98
|
+
float dx = absP.x - halfSize.x;
|
|
99
|
+
float dy = absP.y - halfSize.y;
|
|
100
|
+
|
|
101
|
+
if (dy > dx) {
|
|
102
|
+
// Top or bottom edge
|
|
103
|
+
if (p.y > 0.0) {
|
|
104
|
+
// Bottom edge: start at bottom-left, go right
|
|
105
|
+
perimeterDistance = size.x + size.y + (p.x + halfSize.x);
|
|
106
|
+
} else {
|
|
107
|
+
// Top edge: start at top-right, go left
|
|
108
|
+
perimeterDistance = size.x + (halfSize.x - p.x);
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
// Left or right edge
|
|
112
|
+
if (p.x > 0.0) {
|
|
113
|
+
// Right edge: start at top-right, go down
|
|
114
|
+
perimeterDistance = p.y + halfSize.y;
|
|
115
|
+
} else {
|
|
116
|
+
// Left edge: start at bottom-left, go up
|
|
117
|
+
perimeterDistance = size.x + size.y + size.x + (halfSize.y - p.y);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
float dashWidth = 7.0 * u_dpi;
|
|
122
|
+
float dashGap = 7.0 * u_dpi;
|
|
123
|
+
float dashCycle = dashWidth + dashGap;
|
|
124
|
+
|
|
125
|
+
float dashPosition = mod(perimeterDistance, dashCycle);
|
|
126
|
+
float dashFactor = step(dashPosition, dashWidth);
|
|
127
|
+
|
|
128
|
+
// Only show dashes
|
|
129
|
+
borderAlpha *= dashFactor;
|
|
130
|
+
|
|
131
|
+
vec4 borderColor = vec4(color, 1.0);
|
|
132
|
+
vec4 finalColor = mix(
|
|
133
|
+
vec4(0.0, 0.0, 0.0, 0.0),
|
|
134
|
+
borderColor,
|
|
135
|
+
borderAlpha * borderColor.a
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
gl_FragColor = finalColor;
|
|
139
|
+
}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div />
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script setup>
|
|
6
|
+
import onBlokkliEvent from "#blokkli/helpers/composables/onBlokkliEvent";
|
|
7
|
+
import defineRenderer from "#blokkli/helpers/composables/defineRenderer";
|
|
8
|
+
import { useBlokkli, computed, ref, watch } from "#imports";
|
|
9
|
+
import { setBuffersAndAttributes, drawBufferInfo, setUniforms } from "twgl.js";
|
|
10
|
+
import vs from "./vertex.glsl?raw";
|
|
11
|
+
import fs from "./fragment.glsl?raw";
|
|
12
|
+
import { RectangleBufferCollector } from "#blokkli/helpers/webgl";
|
|
13
|
+
import { toShaderColor, isInsideRect } from "#blokkli/helpers";
|
|
14
|
+
const props = defineProps({
|
|
15
|
+
gl: { type: null, required: true }
|
|
16
|
+
});
|
|
17
|
+
const { animation, theme, dom, selection, state, ui, editable } = useBlokkli();
|
|
18
|
+
const programInfo = animation.registerProgram("hover", props.gl, [vs, fs]);
|
|
19
|
+
const DEBUG = false;
|
|
20
|
+
const MAX_RECTS = 11;
|
|
21
|
+
function getDeepestUuid(uuids) {
|
|
22
|
+
if (uuids.length === 0) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
let deepestUuid = uuids[0];
|
|
26
|
+
let maxLevel = state.getNestingLevel(deepestUuid);
|
|
27
|
+
for (let i = 1; i < uuids.length; i++) {
|
|
28
|
+
const uuid = uuids[i];
|
|
29
|
+
const level = state.getNestingLevel(uuid);
|
|
30
|
+
if (level > maxLevel) {
|
|
31
|
+
maxLevel = level;
|
|
32
|
+
deepestUuid = uuid;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return deepestUuid;
|
|
36
|
+
}
|
|
37
|
+
function createHoverState() {
|
|
38
|
+
return {
|
|
39
|
+
positions: new Float32Array(MAX_RECTS * 4),
|
|
40
|
+
radii: new Float32Array(MAX_RECTS * 4),
|
|
41
|
+
types: new Float32Array(MAX_RECTS),
|
|
42
|
+
visible: new Float32Array(MAX_RECTS)
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const hoverState = createHoverState();
|
|
46
|
+
let previousHoveredUuids = [];
|
|
47
|
+
let previousDeepestUuid = null;
|
|
48
|
+
let previousEditableFieldRect = null;
|
|
49
|
+
const isHoveringEditableField = ref(false);
|
|
50
|
+
const isHoveringSelectedBlock = ref(false);
|
|
51
|
+
class HoverRectangleBufferCollector extends RectangleBufferCollector {
|
|
52
|
+
}
|
|
53
|
+
const collector = new HoverRectangleBufferCollector(props.gl);
|
|
54
|
+
for (let i = 0; i < MAX_RECTS; i++) {
|
|
55
|
+
collector.addRectangle(
|
|
56
|
+
{
|
|
57
|
+
id: `hover-rect-${i}`,
|
|
58
|
+
x: 0,
|
|
59
|
+
y: 0,
|
|
60
|
+
width: 100,
|
|
61
|
+
height: 100,
|
|
62
|
+
radius: [0, 0, 0, 0]
|
|
63
|
+
},
|
|
64
|
+
0
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
const bufferInfo = collector.createBufferInfo();
|
|
68
|
+
function resetHoverState() {
|
|
69
|
+
previousHoveredUuids = [];
|
|
70
|
+
previousDeepestUuid = null;
|
|
71
|
+
previousEditableFieldRect = null;
|
|
72
|
+
hoverState.visible.fill(0);
|
|
73
|
+
isHoveringEditableField.value = false;
|
|
74
|
+
isHoveringSelectedBlock.value = false;
|
|
75
|
+
}
|
|
76
|
+
watch(selection.isChangingOptions, (isChanging) => {
|
|
77
|
+
if (!isChanging) {
|
|
78
|
+
resetHoverState();
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
watch(selection.uuids, () => {
|
|
82
|
+
resetHoverState();
|
|
83
|
+
});
|
|
84
|
+
function updateHoverState(mouseX, mouseY, offset, scale, artboardSize) {
|
|
85
|
+
const artboardRect = {
|
|
86
|
+
x: offset.x,
|
|
87
|
+
y: offset.y,
|
|
88
|
+
width: artboardSize.width * scale,
|
|
89
|
+
height: artboardSize.height * scale
|
|
90
|
+
};
|
|
91
|
+
const isOutsideArtboard = mouseX < artboardRect.x || mouseX > artboardRect.x + artboardRect.width || mouseY < artboardRect.y || mouseY > artboardRect.y + artboardRect.height;
|
|
92
|
+
if (isOutsideArtboard) {
|
|
93
|
+
if (DEBUG || previousHoveredUuids.length > 0 || previousEditableFieldRect !== null) {
|
|
94
|
+
hoverState.visible.fill(0);
|
|
95
|
+
isHoveringEditableField.value = false;
|
|
96
|
+
isHoveringSelectedBlock.value = false;
|
|
97
|
+
if (!DEBUG) {
|
|
98
|
+
previousHoveredUuids = [];
|
|
99
|
+
previousDeepestUuid = null;
|
|
100
|
+
previousEditableFieldRect = null;
|
|
101
|
+
}
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
const artboardMouseX = mouseX / scale - offset.x / scale;
|
|
107
|
+
const artboardMouseY = mouseY / scale - offset.y / scale;
|
|
108
|
+
const hoveredUuids = [];
|
|
109
|
+
const visibleBlocks = dom.getVisibleBlocks();
|
|
110
|
+
for (let i = 0; i < visibleBlocks.length; i++) {
|
|
111
|
+
const uuid = visibleBlocks[i];
|
|
112
|
+
if (!uuid) continue;
|
|
113
|
+
const rawRect = dom.getBlockRect(uuid);
|
|
114
|
+
if (!rawRect) continue;
|
|
115
|
+
const rect = ui.getViewportRelativeRect(rawRect, scale, offset);
|
|
116
|
+
const blockRect = {
|
|
117
|
+
x: rect.x / scale - offset.x / scale,
|
|
118
|
+
y: rect.y / scale - offset.y / scale,
|
|
119
|
+
width: rect.width / scale,
|
|
120
|
+
height: rect.height / scale
|
|
121
|
+
};
|
|
122
|
+
if (isInsideRect(artboardMouseX, artboardMouseY, blockRect)) {
|
|
123
|
+
hoveredUuids.push(uuid);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const deepestUuid = getDeepestUuid(hoveredUuids);
|
|
127
|
+
const selectedUuids = selection.uuids.value;
|
|
128
|
+
const unselectedHoveredUuids = hoveredUuids.filter(
|
|
129
|
+
(uuid) => !selectedUuids.includes(uuid)
|
|
130
|
+
);
|
|
131
|
+
const hoveredChanged = unselectedHoveredUuids.length !== previousHoveredUuids.length || unselectedHoveredUuids.some(
|
|
132
|
+
(uuid, i) => uuid !== previousHoveredUuids[i]
|
|
133
|
+
) || deepestUuid !== previousDeepestUuid;
|
|
134
|
+
let hoveredEditableFieldRect = null;
|
|
135
|
+
const editableRects = editable.getVisible();
|
|
136
|
+
for (let i = 0; i < editableRects.length; i++) {
|
|
137
|
+
const editableRect = editableRects[i];
|
|
138
|
+
if (isInsideRect(artboardMouseX, artboardMouseY, editableRect)) {
|
|
139
|
+
hoveredEditableFieldRect = editableRect;
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (!hoveredChanged && !DEBUG) {
|
|
144
|
+
const editableFieldChanged = hoveredEditableFieldRect === null !== (previousEditableFieldRect === null) || hoveredEditableFieldRect && previousEditableFieldRect && (hoveredEditableFieldRect.x !== previousEditableFieldRect.x || hoveredEditableFieldRect.y !== previousEditableFieldRect.y || hoveredEditableFieldRect.width !== previousEditableFieldRect.width || hoveredEditableFieldRect.height !== previousEditableFieldRect.height);
|
|
145
|
+
if (!editableFieldChanged) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const shouldHighlightDeepest = deepestUuid && unselectedHoveredUuids.includes(deepestUuid);
|
|
150
|
+
hoverState.visible.fill(0);
|
|
151
|
+
const nestingMap = /* @__PURE__ */ new Map();
|
|
152
|
+
for (let i = 0; i < unselectedHoveredUuids.length; i++) {
|
|
153
|
+
const uuid = unselectedHoveredUuids[i];
|
|
154
|
+
const level = Math.min(state.getNestingLevel(uuid), 9);
|
|
155
|
+
if (!nestingMap.has(level)) {
|
|
156
|
+
nestingMap.set(level, uuid);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
for (const [level, uuid] of nestingMap) {
|
|
160
|
+
const rect = dom.getBlockRect(uuid);
|
|
161
|
+
const block = dom.findBlock(uuid);
|
|
162
|
+
if (!rect || !block) continue;
|
|
163
|
+
const el = dom.getDragElement(block);
|
|
164
|
+
if (!el) continue;
|
|
165
|
+
const style = theme.getDraggableStyle(el);
|
|
166
|
+
const isDeepest = shouldHighlightDeepest && uuid === deepestUuid;
|
|
167
|
+
hoverState.positions[level * 4 + 0] = rect.x;
|
|
168
|
+
hoverState.positions[level * 4 + 1] = rect.y;
|
|
169
|
+
hoverState.positions[level * 4 + 2] = rect.width;
|
|
170
|
+
hoverState.positions[level * 4 + 3] = rect.height;
|
|
171
|
+
hoverState.radii[level * 4 + 0] = style.radius[0];
|
|
172
|
+
hoverState.radii[level * 4 + 1] = style.radius[1];
|
|
173
|
+
hoverState.radii[level * 4 + 2] = style.radius[2];
|
|
174
|
+
hoverState.radii[level * 4 + 3] = style.radius[3];
|
|
175
|
+
let type = 0;
|
|
176
|
+
if (isDeepest) {
|
|
177
|
+
const isFromLibrary = state.fromLibraryUuids.value.includes(uuid);
|
|
178
|
+
if (isFromLibrary) {
|
|
179
|
+
type = 4;
|
|
180
|
+
} else {
|
|
181
|
+
type = style.isInverted ? 3 : 1;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
hoverState.types[level] = type;
|
|
185
|
+
hoverState.visible[level] = 1;
|
|
186
|
+
}
|
|
187
|
+
if (hoveredEditableFieldRect) {
|
|
188
|
+
const inset = 2;
|
|
189
|
+
hoverState.positions[10 * 4 + 0] = hoveredEditableFieldRect.x + inset;
|
|
190
|
+
hoverState.positions[10 * 4 + 1] = hoveredEditableFieldRect.y + inset;
|
|
191
|
+
hoverState.positions[10 * 4 + 2] = hoveredEditableFieldRect.width - inset * 2;
|
|
192
|
+
hoverState.positions[10 * 4 + 3] = hoveredEditableFieldRect.height - inset * 2;
|
|
193
|
+
hoverState.radii[10 * 4 + 0] = 0;
|
|
194
|
+
hoverState.radii[10 * 4 + 1] = 0;
|
|
195
|
+
hoverState.radii[10 * 4 + 2] = 0;
|
|
196
|
+
hoverState.radii[10 * 4 + 3] = 0;
|
|
197
|
+
hoverState.types[10] = 2;
|
|
198
|
+
hoverState.visible[10] = 1;
|
|
199
|
+
}
|
|
200
|
+
if (!DEBUG) {
|
|
201
|
+
previousHoveredUuids = unselectedHoveredUuids;
|
|
202
|
+
previousDeepestUuid = deepestUuid;
|
|
203
|
+
previousEditableFieldRect = hoveredEditableFieldRect;
|
|
204
|
+
}
|
|
205
|
+
isHoveringEditableField.value = hoveredEditableFieldRect !== null;
|
|
206
|
+
isHoveringSelectedBlock.value = hoveredUuids.some(
|
|
207
|
+
(uuid) => selectedUuids.includes(uuid)
|
|
208
|
+
);
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
const uniforms = computed(() => {
|
|
212
|
+
return {
|
|
213
|
+
u_color_mono: toShaderColor(theme.mono.value[300]),
|
|
214
|
+
u_color_accent: toShaderColor(theme.accent.value[600]),
|
|
215
|
+
u_color_teal: toShaderColor(theme.teal.value.normal),
|
|
216
|
+
u_color_white: toShaderColor([255, 255, 255]),
|
|
217
|
+
u_color_lime: toShaderColor(theme.lime.value.normal)
|
|
218
|
+
};
|
|
219
|
+
});
|
|
220
|
+
onBlokkliEvent("state:reloaded", () => {
|
|
221
|
+
resetHoverState();
|
|
222
|
+
});
|
|
223
|
+
onBlokkliEvent("ui:resized", () => {
|
|
224
|
+
resetHoverState();
|
|
225
|
+
});
|
|
226
|
+
defineRenderer("hover-overlay", {
|
|
227
|
+
zIndex: 200,
|
|
228
|
+
enabled: () => !selection.isChangingOptions.value,
|
|
229
|
+
cursor: () => {
|
|
230
|
+
if (isHoveringEditableField.value && state.editMode.value !== "readonly") {
|
|
231
|
+
return "text";
|
|
232
|
+
}
|
|
233
|
+
if (isHoveringSelectedBlock.value && state.editMode.value === "editing") {
|
|
234
|
+
return "grab";
|
|
235
|
+
}
|
|
236
|
+
return null;
|
|
237
|
+
},
|
|
238
|
+
render: (ctx) => {
|
|
239
|
+
if (!bufferInfo) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (!ui.openTooltip.value) {
|
|
243
|
+
updateHoverState(
|
|
244
|
+
ctx.mouseX,
|
|
245
|
+
ctx.mouseY,
|
|
246
|
+
ctx.artboardOffset,
|
|
247
|
+
ctx.artboardScale,
|
|
248
|
+
ctx.artboardSize
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
props.gl.useProgram(programInfo.program);
|
|
252
|
+
setUniforms(programInfo, uniforms.value);
|
|
253
|
+
setUniforms(programInfo, {
|
|
254
|
+
u_hover_positions: hoverState.positions,
|
|
255
|
+
u_hover_radii: hoverState.radii,
|
|
256
|
+
u_hover_types: hoverState.types,
|
|
257
|
+
u_hover_visible: hoverState.visible
|
|
258
|
+
});
|
|
259
|
+
animation.setSharedUniforms(props.gl, programInfo);
|
|
260
|
+
setBuffersAndAttributes(props.gl, programInfo, bufferInfo);
|
|
261
|
+
drawBufferInfo(props.gl, bufferInfo, props.gl.TRIANGLES);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
</script>
|
|
265
|
+
|
|
266
|
+
<script>
|
|
267
|
+
export default {
|
|
268
|
+
name: "HoverOverlay"
|
|
269
|
+
};
|
|
270
|
+
</script>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
declare const _default: import("vue").DefineComponent<{
|
|
2
|
+
gl: WebGLRenderingContext;
|
|
3
|
+
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{
|
|
4
|
+
gl: WebGLRenderingContext;
|
|
5
|
+
}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
6
|
+
export default _default;
|