Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(Mobile): Fix context menu on storage with long press #895

Merged
merged 20 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
c0bdb55
fix(files): refactor file selection to use a dynamic input element
lgmarchi Nov 27, 2024
99d3770
Merge remote-tracking branch 'origin/dev' into fix-storage-on-mobile
lgmarchi Nov 27, 2024
72774cf
Merge branch 'dev' into fix-storage-on-mobile
stavares843 Nov 27, 2024
8bca11a
Merge branch 'dev' into fix-storage-on-mobile
stavares843 Nov 27, 2024
43468ec
Merge branch 'dev' into fix-storage-on-mobile
stavares843 Nov 27, 2024
104cc35
Merge branch 'dev' into fix-storage-on-mobile
stavares843 Nov 27, 2024
42f8562
Merge remote-tracking branch 'origin/dev' into fix-storage-on-mobile
lgmarchi Nov 28, 2024
37aac17
Merge remote-tracking branch 'origin/fix-storage-on-mobile' into fix-…
lgmarchi Nov 28, 2024
f876227
feat(context-menu): enhance touch support and position calculation fo…
lgmarchi Nov 28, 2024
091c46d
fix(context-menu): prevent text selection during touch interactions
lgmarchi Nov 28, 2024
ebd2c44
Merge branch 'dev' into fix-context-menu-on-storage
lgmarchi Nov 28, 2024
0617c49
Merge branch 'dev' into fix-context-menu-on-storage
stavares843 Nov 30, 2024
d184f3b
Merge branch 'dev' into fix-context-menu-on-storage
lgmarchi Dec 2, 2024
6737285
Merge branch 'dev' into fix-context-menu-on-storage
phillsatellite Dec 2, 2024
7f438cf
Merge branch 'dev' into fix-context-menu-on-storage
stavares843 Dec 2, 2024
4d2a9a3
Merge branch 'dev' into fix-context-menu-on-storage
phillsatellite Dec 3, 2024
84375d1
Merge branch 'dev' into fix-context-menu-on-storage
luisecm Dec 3, 2024
e9b9abb
Merge branch 'dev' into fix-context-menu-on-storage
stavares843 Dec 4, 2024
f667a96
Merge branch 'dev' into fix-context-menu-on-storage
lgmarchi Dec 4, 2024
413befd
Merge branch 'dev' into fix-context-menu-on-storage
lgmarchi Dec 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 85 additions & 19 deletions src/lib/components/ui/ContextMenu.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ""

Expand All @@ -25,38 +27,89 @@
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) {
document.body.style.userSelect = "none"

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}`)
Expand All @@ -66,9 +119,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)
})
</script>

<slot name="content" open={openContext} />
<div bind:this={slotContainer}>
<slot name="content" open={openContext} />
</div>
{#if visible}
<div id="context-menu" data-cy={hook} bind:this={context} use:clickoutside on:clickoutside={onClose} style={`left: ${coords[0]}px; top: ${coords[1]}px;`}>
<slot name="items" close={onClose}></slot>
Expand Down
21 changes: 16 additions & 5 deletions src/routes/files/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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)
}}>
<Icon icon={Shape.Plus} />
</Button>
<input data-cy="input=upload-files" style="display:none" multiple type="file" on:change={e => onFileSelected(e)} bind:this={filesToUpload} />

<ProgressButton appearance={Appearance.Alt} icon={Shape.ArrowsUpDown} />
{/if}
</svelte:fragment>
Expand Down
Loading