From c0bdb555518c147ad98571b298568746a37f5635 Mon Sep 17 00:00:00 2001 From: lgmarchi Date: Wed, 27 Nov 2024 17:43:34 -0300 Subject: [PATCH 1/3] fix(files): refactor file selection to use a dynamic input element --- src/routes/files/+page.svelte | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/routes/files/+page.svelte b/src/routes/files/+page.svelte index bc64e5e17..ccf4a16d6 100644 --- a/src/routes/files/+page.svelte +++ b/src/routes/files/+page.svelte @@ -376,8 +376,7 @@ search_component.select_first() } - const onFileSelected = async (e: Event) => { - const target = e.target as HTMLInputElement + const onFileSelected = async (target: HTMLInputElement) => { if (target && target.files) { for (let i = 0; i < target.files.length; i++) { const file = target.files[i] @@ -627,12 +626,24 @@ hook="button-upload-file" icon tooltip={$_("files.upload")} - on:click={async () => { - filesToUpload?.click() + on:click={() => { + const input = document.createElement("input") + input.type = "file" + input.multiple = true + input.style.display = "none" + + input.addEventListener("change", _ => { + const files = input.files + onFileSelected(input) + }) + + document.body.appendChild(input) + input.click() + document.body.removeChild(input) }}> - onFileSelected(e)} bind:this={filesToUpload} /> + {/if} From f876227e5b320effd9441d1a88c4eb3f5cb94f44 Mon Sep 17 00:00:00 2001 From: lgmarchi Date: Thu, 28 Nov 2024 12:36:22 -0300 Subject: [PATCH 2/3] feat(context-menu): enhance touch support and position calculation for mobile on context menu --- src/lib/components/ui/ContextMenu.svelte | 102 ++++++++++++++++++----- 1 file changed, 83 insertions(+), 19 deletions(-) diff --git a/src/lib/components/ui/ContextMenu.svelte b/src/lib/components/ui/ContextMenu.svelte index 2c6d4e833..b93069ebd 100644 --- a/src/lib/components/ui/ContextMenu.svelte +++ b/src/lib/components/ui/ContextMenu.svelte @@ -9,12 +9,14 @@ import { clickoutside } from "@svelte-put/clickoutside" import { Appearance } from "$lib/enums" import type { ContextItem } from "$lib/types" - import { createEventDispatcher, tick } from "svelte" + import { createEventDispatcher, onDestroy, onMount, tick } from "svelte" import { log } from "$lib/utils/Logger" let visible: boolean = false let coords: [number, number] = [0, 0] let context: HTMLElement + let touchTimeout: number | undefined + let slotContainer: HTMLElement export let items: ContextItem[] = [] export let hook: string = "" @@ -25,38 +27,87 @@ close_context = undefined } - function calculatePos(evt: MouseEvent): [number, number] { - if (context === undefined) return [evt.clientX, evt.clientY] + function calculatePos(evt: MouseEvent | TouchEvent): [number, number] { + if (!context) { + if (evt instanceof MouseEvent) { + return [evt.clientX, evt.clientY] + } else if (evt instanceof TouchEvent) { + const touch = evt.touches[0] + return [touch.clientX, touch.clientY] + } + return [0, 0] + } + const { width, height } = context.getBoundingClientRect() - let offsetX = evt.pageX - let offsetY = evt.pageY - let screenWidth = evt.view!.innerWidth - let screenHeight = evt.view!.innerHeight - let overFlowX = screenWidth < width + offsetX - let overFlowY = screenHeight < height + offsetY - let topX = overFlowX ? Math.max(5, screenWidth - width - 5) : Math.max(5, offsetX) - if (screenHeight - offsetY < height + 30) { - let adjustedY = offsetY - height - let topY = Math.max(5, adjustedY) - return [topX, topY] + + let offsetX: number, offsetY: number, screenWidth: number, screenHeight: number + + if (evt instanceof MouseEvent) { + offsetX = evt.pageX + offsetY = evt.pageY + screenWidth = evt.view!.innerWidth + screenHeight = evt.view!.innerHeight + } else if (evt instanceof TouchEvent) { + const touch = evt.touches[0] + const targetElement = touch.target as HTMLElement + + const doc = targetElement.ownerDocument! + const win = doc.defaultView! + + offsetX = touch.pageX + offsetY = touch.pageY + screenWidth = win.innerWidth + screenHeight = win.innerHeight } else { - let topY = Math.max(5, overFlowY ? offsetY - height : offsetY) - return [topX, topY] + return [0, 0] } + + // Calculate overflow + const overFlowX = screenWidth < width + offsetX + const overFlowY = screenHeight < height + offsetY + + // Adjust X position + const topX = overFlowX ? Math.max(5, screenWidth - width - 5) : Math.max(5, offsetX) + + // Adjust Y position + const topY = screenHeight - offsetY < height + 30 ? Math.max(5, offsetY - height) : Math.max(5, overFlowY ? offsetY - height : offsetY) + + return [topX, topY] } - async function openContext(evt: MouseEvent) { + async function openContext(evt: MouseEvent | TouchEvent) { if (close_context !== undefined) { close_context() } close_context = () => (visible = false) + evt.preventDefault() visible = true - coords = [evt.clientX, evt.clientY] + + if (evt instanceof MouseEvent) { + coords = [evt.clientX, evt.clientY] + } else if (evt instanceof TouchEvent) { + const touch = evt.touches[0] + coords = [touch.clientX, touch.clientY] + } + await tick() coords = calculatePos(evt) } + function handleTouchStart(evt: TouchEvent) { + touchTimeout = window.setTimeout(() => { + openContext(evt) + }, 350) + } + + function handleTouchEnd() { + if (touchTimeout !== undefined) { + clearTimeout(touchTimeout) + touchTimeout = undefined + } + } + function handleItemClick(e: MouseEvent, item: ContextItem) { e.stopPropagation() log.info(`Clicked ${item.text}`) @@ -66,9 +117,22 @@ }) onClose(customEvent) } + + onMount(() => { + // Add event listeners for mobile + slotContainer.addEventListener("touchstart", handleTouchStart) + slotContainer.addEventListener("touchend", handleTouchEnd) + }) + + onDestroy(() => { + slotContainer.removeEventListener("touchstart", handleTouchStart) + slotContainer.removeEventListener("touchend", handleTouchEnd) + }) - +
+ +
{#if visible}
From 091c46d5ed9edf8dc23133db034351b8c8a0836e Mon Sep 17 00:00:00 2001 From: lgmarchi Date: Thu, 28 Nov 2024 12:42:14 -0300 Subject: [PATCH 3/3] fix(context-menu): prevent text selection during touch interactions --- src/lib/components/ui/ContextMenu.svelte | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/components/ui/ContextMenu.svelte b/src/lib/components/ui/ContextMenu.svelte index b93069ebd..998aafebd 100644 --- a/src/lib/components/ui/ContextMenu.svelte +++ b/src/lib/components/ui/ContextMenu.svelte @@ -96,6 +96,8 @@ } function handleTouchStart(evt: TouchEvent) { + document.body.style.userSelect = "none" + touchTimeout = window.setTimeout(() => { openContext(evt) }, 350)