fix bug where touch events prevented focusing inputs, change input type of file transfer reception data

This commit is contained in:
marlon-erler
2026-08-30 23:34:45 +02:00
parent 6a014fddfb
commit f6511f4523
7 changed files with 203 additions and 1921 deletions
+51 -38
View File
@@ -279,28 +279,32 @@ export function localeCompare(a: string, b: string): number {
}
// ui
export function implementPinchZoom(targetSelector: string) {
export function implementPinchZoom(canvas: HTMLElement) {
let pinching = false;
let lastElement: HTMLElement|undefined = undefined;
let lastElement: HTMLElement | undefined = undefined;
let initialDistance,
currentZoom, initialZoom,
currentX, currentY,
initialX, initialY,
initialTouchX, initialTouchY;
let initialDistance: number,
currentZoom: number,
initialZoom: number,
currentX: number,
currentY: number,
initialX: number,
initialY: number,
initialTouchX: number,
initialTouchY: number;
function reset() {
initialDistance = 0;
initialDistance = 0;
currentZoom = 1;
initialZoom = 1;
currentZoom = 1;
initialZoom = 1;
currentX = 0;
currentY = 0;
initialX = 0;
initialY = 0;
initialTouchX = 0;
initialTouchY = 0;
currentX = 0;
currentY = 0;
initialX = 0;
initialY = 0;
initialTouchX = 0;
initialTouchY = 0;
}
reset();
@@ -308,30 +312,28 @@ export function implementPinchZoom(targetSelector: string) {
const MIN = 0.25;
const MAX = 5;
const element = (): HTMLElement => canvas.querySelector(targetSelector);
const parent = (): HTMLElement => element().parentElement;
const element = (): HTMLElement => canvas.querySelector(".zoom");
const distance = (e: TouchEvent) =>
Math.hypot(
e.touches[0].pageX - e.touches[1].pageX,
e.touches[0].pageY - e.touches[1].pageY,
);
Math.hypot(
e.touches[0].pageX - e.touches[1].pageX,
e.touches[0].pageY - e.touches[1].pageY,
);
const point = (e: TouchEvent, direction: "x" | "y", i: number) =>
e.touches[i][direction == "x" ? "clientX" : "clientY"];
e.touches[i][direction == "x" ? "clientX" : "clientY"];
const midpoint = (e: TouchEvent, direction: "x" | "y") =>
(point(e, direction, 0) + point(e, direction, 1)) / 2;
(point(e, direction, 0) + point(e, direction, 1)) / 2;
function apply(factor: number, offset: [number, number]) {
if (factor < MIN) return apply(MIN, offset);
if (factor > MAX) return apply(5, offset);
if (factor < MIN) return apply(MIN, offset);
if (factor > MAX) return apply(5, offset);
const [x, y] = offset;
element().style.transform = `scale(${(Math.floor(factor * 100) / 100).toString()}) translate(${x}px, ${y}px)`;
currentX = x;
currentY = y;
currentZoom = factor;
const [x, y] = offset;
element().style.transform = `scale(${(Math.floor(factor * 100) / 100).toString()}) translate(${x}px, ${y}px)`;
currentX = x;
currentY = y;
currentZoom = factor;
}
const canvas = document.body;
canvas.addEventListener("touchstart", (event: TouchEvent) => {
(document.activeElement as HTMLElement).blur();
@@ -357,8 +359,10 @@ export function implementPinchZoom(targetSelector: string) {
canvas.addEventListener("touchmove", (event: TouchEvent) => {
if (!pinching) {
apply(currentZoom, [
initialX + (((event.touches[0].clientX - initialTouchX)/initialZoom)),
initialY + ((event.touches[0].clientY - initialTouchY)/initialZoom),
initialX +
(event.touches[0].clientX - initialTouchX) / initialZoom,
initialY +
(event.touches[0].clientY - initialTouchY) / initialZoom,
]);
return;
}
@@ -369,15 +373,24 @@ export function implementPinchZoom(targetSelector: string) {
const midY = midpoint(event, "y");
const ratio = currentDistance / initialDistance;
const difference = currentDistance - initialDistance;
apply(initialZoom * ratio, [initialX + (midX - difference - initialTouchX)/initialZoom, initialY + (midY - difference - initialTouchY)/initialZoom]);
apply(initialZoom * ratio, [
initialX + (midX - difference - initialTouchX) / initialZoom,
initialY + (midY - difference - initialTouchY) / initialZoom,
]);
});
canvas.addEventListener("wheel", (event: WheelEvent) => {
event.preventDefault();
if (!event.shiftKey) {
apply(currentZoom, [currentX - event.deltaX, currentY - event.deltaY]);
apply(currentZoom, [
currentX - event.deltaX,
currentY - event.deltaY,
]);
return;
};
apply(currentZoom + (event.deltaY < 0 ? 1 : -1) * 0.1, [initialX, initialY]);
}
apply(currentZoom + (event.deltaY < 0 ? 1 : -1) * 0.1, [
initialX,
initialY,
]);
});
canvas.addEventListener("scroll", (event: MouseEvent) => {
event.preventDefault();
+113 -110
View File
@@ -45,122 +45,125 @@ export function BoardPage(
}),
);
const wrapper = <div class="content main-content" children:set={pages}></div>
Utility.implementPinchZoom(wrapper);
// task modal
const taskSettingsModal = React.createProxyState(
[boardViewModel.selectedTaskViewModel],
() => {
if (boardViewModel.selectedTaskViewModel.value == undefined) {
return <div></div>;
} else {
ViewController.setFocusWithDelay();
[boardViewModel.selectedTaskViewModel],
() => {
if (boardViewModel.selectedTaskViewModel.value == undefined) {
return <div></div>;
} else {
ViewController.setFocusWithDelay();
return TaskSettingsModal(
coreViewModel,
boardViewModel.selectedTaskViewModel.value,
);
}
},
return TaskSettingsModal(
coreViewModel,
boardViewModel.selectedTaskViewModel.value,
);
}
},
);
return (
<div class="pane">
<div class="toolbar">
<span>
<button
class="ghost board-close-button"
aria-label={
coreViewModel.translations.chatPage.task
.closeBoardButtonAudioLabel
}
on:click={boardViewModel.close}
>
<span class="icon">arrow_back</span>
</button>
<button
class="ghost board-toggle-button inset-outline"
aria-label={
coreViewModel.translations.chatPage.task
.toggleBoardButtonAudioLabel
}
on:click={
boardViewModel.taskPageViewModel.toggleBoardList
}
toggle:selected={
boardViewModel.taskPageViewModel.isShowingBoadList
}
>
<span class="icon">dock_to_right</span>
</button>
<button
class="ghost"
aria-label={
coreViewModel.translations.chatPage.task
.boardSettingsHeadline
}
on:click={boardViewModel.showSettings}
>
<span class="icon">settings</span>
</button>
</span>
<span class="scroll-h ribbon">
{BoardViewToggleButton(
coreViewModel.translations.chatPage.task
.listViewButtonAudioLabel,
"view_list",
BoardPageTypes.List,
boardViewModel,
)}
{BoardViewToggleButton(
coreViewModel.translations.chatPage.task
.kanbanViewButtonAudioLabel,
"view_kanban",
BoardPageTypes.Kanban,
boardViewModel,
)}
{BoardViewToggleButton(
coreViewModel.translations.chatPage.task
.statusViewButtonAudioLabel,
"grid_view",
BoardPageTypes.StatusGrid,
boardViewModel,
)}
</span>
<span>
<button
class="ghost inset-outline"
aria-label={
coreViewModel.translations.chatPage.task
.filterTasksButtonAudioLabel
}
on:click={boardViewModel.showFilterModal}
toggle:selected={boardViewModel.isFilterActive}
>
<span class="icon">filter_alt</span>
</button>
<button
class="ghost"
aria-label={
coreViewModel.translations.chatPage.task
.createTaskButtonAudioLabel
}
on:click={boardViewModel.createTask}
>
<span class="icon">add</span>
</button>
</span>
</div>
<div class="pane">
<div class="toolbar">
<span>
<button
class="ghost board-close-button"
aria-label={
coreViewModel.translations.chatPage.task
.closeBoardButtonAudioLabel
}
on:click={boardViewModel.close}
>
<span class="icon">arrow_back</span>
</button>
<button
class="ghost board-toggle-button inset-outline"
aria-label={
coreViewModel.translations.chatPage.task
.toggleBoardButtonAudioLabel
}
on:click={
boardViewModel.taskPageViewModel.toggleBoardList
}
toggle:selected={
boardViewModel.taskPageViewModel.isShowingBoadList
}
>
<span class="icon">dock_to_right</span>
</button>
<button
class="ghost"
aria-label={
coreViewModel.translations.chatPage.task
.boardSettingsHeadline
}
on:click={boardViewModel.showSettings}
>
<span class="icon">settings</span>
</button>
</span>
<span class="scroll-h ribbon">
{BoardViewToggleButton(
coreViewModel.translations.chatPage.task
.listViewButtonAudioLabel,
"view_list",
BoardPageTypes.List,
boardViewModel,
)}
{BoardViewToggleButton(
coreViewModel.translations.chatPage.task
.kanbanViewButtonAudioLabel,
"view_kanban",
BoardPageTypes.Kanban,
boardViewModel,
)}
{BoardViewToggleButton(
coreViewModel.translations.chatPage.task
.statusViewButtonAudioLabel,
"grid_view",
BoardPageTypes.StatusGrid,
boardViewModel,
)}
</span>
<span>
<button
class="ghost inset-outline"
aria-label={
coreViewModel.translations.chatPage.task
.filterTasksButtonAudioLabel
}
on:click={boardViewModel.showFilterModal}
toggle:selected={boardViewModel.isFilterActive}
>
<span class="icon">filter_alt</span>
</button>
<button
class="ghost"
aria-label={
coreViewModel.translations.chatPage.task
.createTaskButtonAudioLabel
}
on:click={boardViewModel.createTask}
>
<span class="icon">add</span>
</button>
</span>
</div>
<div class="content main-content" children:set={pages}></div>
{wrapper}
{BoardSettingsModal(coreViewModel, boardViewModel)}
{SearchModal(
coreViewModel,
boardViewModel.searchViewModel,
coreViewModel.translations.chatPage.task.filterTasksHeadline,
TaskViewModelToEntry,
boardViewModel.isPresentingFilterModal,
)}
<div children:set={taskSettingsModal}></div>
</div>
);
{BoardSettingsModal(coreViewModel, boardViewModel)}
{SearchModal(
coreViewModel,
boardViewModel.searchViewModel,
coreViewModel.translations.chatPage.task.filterTasksHeadline,
TaskViewModelToEntry,
boardViewModel.isPresentingFilterModal,
)}
<div children:set={taskSettingsModal}></div>
</div>
);
}
+3 -1
View File
@@ -475,7 +475,8 @@ function TransferDataInputModal(
.transferChannelHeadline
}
</span>
<input
<input
type="number"
on:enter={
fileTransferViewModel.prepareReceivingData
}
@@ -496,6 +497,7 @@ function TransferDataInputModal(
}
</span>
<input
type="number"
on:enter={
fileTransferViewModel.prepareReceivingData
}
+1 -1
View File
@@ -9,7 +9,7 @@ import ConnectionModel from "../../Model/Global/connectionModel";
import ChatListModel from "../../Model/Chat/chatListModel";
export default class CoreViewModel {
readonly BUILD = "Build 26.08.30.E";
readonly BUILD = "Build 26.08.30.F2";
translations: Translations;