Skip to content

Commit fae6155

Browse files
committed
fix(texteditor): align formatbar on touchscreens correctly
1 parent 6b399ae commit fae6155

File tree

1 file changed

+59
-11
lines changed
  • contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/toolbar

1 file changed

+59
-11
lines changed

contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/toolbar/MobileFormattingBar.vue

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
class="floating-panel"
55
role="toolbar"
66
:aria-label="textFormattingToolbar$()"
7+
:style="{ bottom: keyboardOffset + 'px' }"
78
>
89
<div
910
class="fixed-actions"
@@ -106,7 +107,7 @@
106107

107108
<script>
108109
109-
import { defineComponent, ref } from 'vue';
110+
import { defineComponent, ref, onMounted, onUnmounted, inject } from 'vue';
110111
import { useToolbarActions } from '../../composables/useToolbarActions';
111112
import { useFormatControls } from '../../composables/useFormatControls';
112113
import { getTipTapEditorStrings } from '../../TipTapEditorStrings';
@@ -118,6 +119,8 @@
118119
components: { ToolbarButton, ToolbarDivider },
119120
setup(props, { emit }) {
120121
const isExpanded = ref(true);
122+
const keyboardOffset = ref(0);
123+
const editor = inject('editor');
121124
122125
const {
123126
collapseFormattingBar$,
@@ -137,8 +140,62 @@
137140
isExpanded.value = !isExpanded.value;
138141
};
139142
143+
// Keyboard detection and positioning
144+
onMounted(() => {
145+
if (editor.value) {
146+
// Use a timeout to allow the keyboard to start appearing
147+
setTimeout(() => {
148+
const { from } = editor.value.state.selection;
149+
editor.value.view.dom.querySelector(`[pos="${from}"]`)?.scrollIntoView({
150+
behavior: 'smooth',
151+
block: 'nearest',
152+
});
153+
}, 150);
154+
}
155+
156+
const vk = navigator.virtualKeyboard;
157+
158+
const updatePositionWithVisualViewport = () => {
159+
if (window.visualViewport) {
160+
const visibleHeight = window.visualViewport.height;
161+
const totalHeight = window.innerHeight;
162+
163+
if (totalHeight - visibleHeight > 50) {
164+
keyboardOffset.value = totalHeight - visibleHeight;
165+
} else {
166+
keyboardOffset.value = 0;
167+
}
168+
}
169+
};
170+
171+
if (vk) {
172+
// Use the new VirtualKeyboard API if available
173+
vk.overlaysContent = true;
174+
const onGeometryChange = () => {
175+
const { height } = vk.boundingRect;
176+
keyboardOffset.value = height > 0 ? height : 0;
177+
};
178+
vk.addEventListener('geometrychange', onGeometryChange);
179+
onUnmounted(() => {
180+
vk.removeEventListener('geometrychange', onGeometryChange);
181+
vk.overlaysContent = false;
182+
});
183+
} else if (window.visualViewport) {
184+
// Fallback to visualViewport for iOS Safari
185+
window.visualViewport.addEventListener('resize', updatePositionWithVisualViewport);
186+
updatePositionWithVisualViewport(); // Initial check
187+
188+
onUnmounted(() => {
189+
if (window.visualViewport) {
190+
window.visualViewport.removeEventListener('resize', updatePositionWithVisualViewport);
191+
}
192+
});
193+
}
194+
});
195+
140196
return {
141197
isExpanded,
198+
keyboardOffset,
142199
textActions,
143200
listActions,
144201
scriptActions,
@@ -166,9 +223,7 @@
166223
.floating-panel {
167224
position: fixed;
168225
right: 0;
169-
170-
/* Use visual viewport bottom instead of layout viewport */
171-
bottom: calc(100vh - 100dvh);
226+
bottom: 0; /* Will be overridden by JavaScript */
172227
left: 0;
173228
z-index: 100;
174229
display: flex;
@@ -178,13 +233,6 @@
178233
animation: slide-up 0.2s ease-out;
179234
}
180235
181-
/* Fallback for older browsers */
182-
@supports not (height: 100dvh) {
183-
.floating-panel {
184-
bottom: 0;
185-
}
186-
}
187-
188236
@keyframes slide-up {
189237
from {
190238
opacity: 0;

0 commit comments

Comments
 (0)