Rebounce navigation events

This commit is contained in:
2026-01-30 12:26:36 +01:00
parent 5ce6b0c417
commit d3d26c6ae1
8 changed files with 56 additions and 26 deletions

View File

@@ -501,7 +501,7 @@ input[type="color"]:focus {
color: var(--input_text);
text-decoration: none;
background: var(--input_hover_background);
box-shadow: 0px 0px 0px 1px var(--highlight_color);
box-shadow: 0px 0px 0px 1px var(--highlight_color) !important;
}
button:active,
@@ -515,8 +515,7 @@ input[type="color"]:active {
}
.button_highlight {
background: var(--highlight_background) !important;
color: var(--highlight_text_color) !important;
box-shadow: 0px 0px 0px 1px var(--highlight_color) !important;
}
.button_red {

View File

@@ -53,12 +53,19 @@ export class FSNavigator {
}
}
last_requested_path: string = ""
navigate = async (path: string, push_history: boolean) => {
if (path === this.last_requested_path) {
console.debug("FSNavigator: Requested path is equal to current path. Debouncing")
return
}
this.last_requested_path = path
if (path[0] !== "/") {
path = "/" + path
}
console.debug("Navigating to path", path, push_history)
console.debug("FSNavigator: Navigating to path", path, push_history)
try {
loading_start()

View File

@@ -69,7 +69,7 @@ const file_event: FileActionHandler = (action: FileAction, index: number, orig:
if (navigator.maxTouchPoints && navigator.maxTouchPoints > 0) {
select_node(index)
} else {
file_menu.open(nav.children[index], orig.target)
file_menu.open(nav.children[index], orig.target, orig)
}
break
case FileAction.Edit:
@@ -86,7 +86,7 @@ const file_event: FileActionHandler = (action: FileAction, index: number, orig:
select_node(index)
break
case FileAction.Menu:
file_menu.open(nav.children[index], orig.target)
file_menu.open(nav.children[index], orig.target, orig)
break
}
}

View File

@@ -19,16 +19,23 @@ let {
let dialog: Dialog = $state()
let node: FSNode = $state(null)
export const open = async (n: FSNode, target: EventTarget) => {
export const open = async (n: FSNode, target: EventTarget, event: Event) => {
node = n
let el: HTMLElement = (target as Element).closest("button")
if (el === null) {
el = (target as Element).closest("a")
}
// Wait for the view to update, so the dialog gets the proper measurements
await tick()
dialog.open(el.getBoundingClientRect())
let el: HTMLElement = (target as Element).closest("button")
if (el !== null) {
dialog.open(el.getBoundingClientRect())
return
}
if (event instanceof MouseEvent) {
dialog.open(event)
return
}
console.error("Cannot find suitable target for spawning dialog window")
}
const delete_node = async () => {

View File

@@ -4,7 +4,7 @@ let { children }: {
} = $props();
let dialog: HTMLDialogElement = $state()
export const open = (button_rect: DOMRect) => {
export const open = (origin: DOMRect|MouseEvent) => {
// Show the window so we can get the location
dialog.showModal()
@@ -15,10 +15,18 @@ export const open = (button_rect: DOMRect) => {
const max_left = window.innerWidth - dialog_rect.width - edge_offset
const max_top = window.innerHeight - dialog_rect.height - edge_offset
// Position the dialog in horizontally in the center of the button and
// verticially below it
const min_left = Math.max((button_rect.left + (button_rect.width/2)) - (dialog_rect.width/2), edge_offset)
const min_top = Math.max(button_rect.bottom, edge_offset)
let min_left: number, min_top: number
if (origin instanceof DOMRect) {
// Position the dialog in horizontally in the center of the button and
// verticially below it
min_left = Math.max((origin.left + (origin.width/2)) - (dialog_rect.width/2), edge_offset)
min_top = Math.max(origin.bottom, edge_offset)
} else if (origin instanceof MouseEvent) {
// Place the dialog at the bottom right of the mouse pointer, like
// regular context menus
min_left = Math.max(origin.clientX, edge_offset)
min_top = Math.max(origin.clientY, edge_offset)
}
// Place the window
dialog.style.left = Math.round(Math.min(min_left, max_left)) + "px"
@@ -35,6 +43,7 @@ export const close = () => {
// the dialog itself then the click was on the dialog background
const click = (e: MouseEvent) => {
if (e.target === dialog) {
e.preventDefault()
dialog.close()
}
}
@@ -42,7 +51,7 @@ const click = (e: MouseEvent) => {
<!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
<dialog bind:this={dialog} onclick={click}>
<dialog bind:this={dialog} onclick={click} oncontextmenu={click}>
{@render children?.()}
</dialog>

View File

@@ -63,7 +63,11 @@ onMount(async () => {
let current_page: Tab = $state(null)
const load_page = (pathname: string, history: boolean): boolean => {
console.debug("Navigating to page", pathname, "log history:", history)
console.debug(
"Page router: Navigating to page", pathname,
"current path", window.location.pathname,
"log history:", history,
)
const path_decoded = decodeURIComponent(pathname)
let page_by_path: Tab = null

View File

@@ -1,6 +1,7 @@
<script lang="ts">
import { global_navigator } from "filesystem/FSNavigator";
import { fs_encode_path, FSNode } from "lib/FilesystemAPI.svelte";
import { highlight_current_page } from "lib/HighlightCurrentPage";
import { onMount } from "svelte";
let siblings: FSNode[] = $state([])
@@ -25,9 +26,9 @@ onMount(() => {
<div class="row">
<a class="button" href="/d{fs_encode_path(node.path)}">
{#if node.is_shared()}
<i class="icon">folder_shared</i>
<i class="icon small">folder_shared</i>
{:else}
<i class="icon">folder</i>
<i class="icon small">folder</i>
{/if}
<span>{node.name}</span>
</a>
@@ -52,11 +53,13 @@ onMount(() => {
{#each siblings as node}
{#if !node.is_hidden()}
<div class="row">
<a class="button" href="/d{fs_encode_path(node.path)}">
{#if node.type === "dir"}
<i class="icon">folder</i>
<a class="button" href="/d{fs_encode_path(node.path)}" use:highlight_current_page>
{#if node.is_shared()}
<i class="icon small">folder_shared</i>
{:else if node.type === "dir"}
<i class="icon small">folder</i>
{:else}
<i class="icon">image</i>
<i class="icon small">image</i>
{/if}
<span>{node.name}</span>
</a>

View File

@@ -21,6 +21,7 @@ export default defineConfig({
outDir: builddir,
emptyOutDir: true,
minify: production,
sourcemap: !production,
lib: {
entry: "src/wrap.js",
name: "fnx_web",