fix bug where touch events prevented focusing inputs, change input type of file transfer reception data
This commit is contained in:
@@ -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
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user