refactor(web): move keyboard state into a store (#29181)

This commit is contained in:
Timon
2026-06-18 16:29:26 +02:00
committed by GitHub
parent 735f52a321
commit 6d73bf4e36
6 changed files with 72 additions and 88 deletions

View File

@@ -44,7 +44,7 @@
brokenAssetClass?: ClassValue;
dimmed?: boolean;
albumUsers?: UserResponseDto[];
onClick?: (asset: TimelineAsset, event?: MouseEvent) => void;
onClick?: (asset: TimelineAsset) => void;
onPreview?: (asset: TimelineAsset) => void;
onSelect?: (asset: TimelineAsset) => void;
onMouseEvent?: (event: { isMouseOver: boolean; selectedGroupIndex: number }) => void;
@@ -93,12 +93,12 @@
}
};
const callClickHandlers = (e?: MouseEvent) => {
const callClickHandlers = () => {
if (selected) {
onIconClickedHandler(e);
onIconClickedHandler();
return;
}
onClick?.($state.snapshot(asset), e);
onClick?.($state.snapshot(asset));
};
const handleClick = (e: MouseEvent) => {
@@ -109,7 +109,7 @@
e.stopPropagation();
e.preventDefault();
callClickHandlers(e);
callClickHandlers();
};
const onMouseEnter = () => {

View File

@@ -13,6 +13,7 @@
import AssetDeleteConfirmModal from '$lib/modals/AssetDeleteConfirmModal.svelte';
import ShortcutsModal from '$lib/modals/ShortcutsModal.svelte';
import { Route } from '$lib/route';
import { keyboardManager } from '$lib/stores/keyboard-manager.svelte';
import { showDeleteModal } from '$lib/stores/preferences.store';
import { handlePromiseError } from '$lib/utils';
import { deleteAssets } from '$lib/utils/actions';
@@ -85,7 +86,6 @@
return top + pageHeaderOffset < window.bottom && top + geometry.getHeight(index) > window.top;
};
let shiftKeyIsDown = $state(false);
let lastAssetMouseEvent: TimelineAsset | null = $state(null);
let scrollTop = $state(0);
@@ -122,21 +122,6 @@
assetInteraction.selectAssets(assets.map((a) => toTimelineAsset(a)));
};
const onKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Shift') {
event.preventDefault();
shiftKeyIsDown = true;
}
};
const onKeyUp = (event: KeyboardEvent) => {
if (event.key === 'Shift') {
event.preventDefault();
shiftKeyIsDown = false;
}
};
const handleSelectAssets = (asset: TimelineAsset) => {
if (!asset) {
return;
@@ -168,7 +153,7 @@
};
const selectAssetCandidates = (endAsset: TimelineAsset) => {
if (!shiftKeyIsDown) {
if (!keyboardManager.shift) {
return;
}
@@ -188,7 +173,7 @@
};
const onSelectStart = (event: Event) => {
if (assetInteraction.selectionActive && shiftKeyIsDown) {
if (assetInteraction.selectionActive && keyboardManager.shift) {
event.preventDefault();
}
};
@@ -333,13 +318,13 @@
});
$effect(() => {
if (!shiftKeyIsDown) {
if (!keyboardManager.shift) {
assetInteraction.clearCandidates();
}
});
$effect(() => {
if (shiftKeyIsDown && lastAssetMouseEvent) {
if (keyboardManager.shift && lastAssetMouseEvent) {
selectAssetCandidates(lastAssetMouseEvent);
}
});
@@ -351,13 +336,7 @@
});
</script>
<svelte:document
onkeydown={onKeyDown}
onkeyup={onKeyUp}
onselectstart={onSelectStart}
use:shortcuts={shortcutList}
onscroll={() => updateSlidingWindow()}
/>
<svelte:document onselectstart={onSelectStart} use:shortcuts={shortcutList} onscroll={() => updateSlidingWindow()} />
{#if assets.length > 0}
<div

View File

@@ -19,6 +19,7 @@
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
import type { TimelineAsset, TimelineManagerOptions, ViewportTopMonth } from '$lib/managers/timeline-manager/types';
import { assetsSnapshot } from '$lib/managers/timeline-manager/utils.svelte';
import { keyboardManager } from '$lib/stores/keyboard-manager.svelte';
import { mediaQueryManager } from '$lib/stores/media-query-manager.svelte';
import { isAssetViewerRoute, navigate } from '$lib/utils/navigation';
import { getTimes, type ScrubberListener } from '$lib/utils/timeline-util';
@@ -59,7 +60,6 @@
groupTitle: string,
asset: TimelineAsset,
) => void,
event?: MouseEvent,
) => void;
}
@@ -371,21 +371,6 @@
let lastAssetMouseEvent: TimelineAsset | null = $state(null);
let shiftKeyIsDown = $state(false);
const onKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Shift') {
event.preventDefault();
shiftKeyIsDown = true;
}
};
const onKeyUp = (event: KeyboardEvent) => {
if (event.key === 'Shift') {
event.preventDefault();
shiftKeyIsDown = false;
}
};
const handleSelectAssetCandidates = (asset: TimelineAsset | null) => {
if (asset) {
void selectAssetCandidates(asset);
@@ -486,7 +471,7 @@
};
const selectAssetCandidates = async (endAsset: TimelineAsset) => {
if (!shiftKeyIsDown) {
if (!keyboardManager.shift) {
return;
}
@@ -506,13 +491,13 @@
});
$effect(() => {
if (!shiftKeyIsDown) {
if (!keyboardManager.shift) {
assetInteraction.clearCandidates();
}
});
$effect(() => {
if (shiftKeyIsDown && lastAssetMouseEvent) {
if (keyboardManager.shift && lastAssetMouseEvent) {
void selectAssetCandidates(lastAssetMouseEvent);
}
});
@@ -561,8 +546,6 @@
};
</script>
<svelte:document onkeydown={onKeyDown} onkeyup={onKeyUp} />
<HotModuleReload
onAfterUpdate={() => {
const asset = page.url.searchParams.get('at');
@@ -600,12 +583,12 @@
onScrubKeyDown={(evt) => {
evt.preventDefault();
let amount = 50;
if (shiftKeyIsDown) {
if (keyboardManager.shift) {
amount = 500;
}
if (evt.key === 'ArrowUp') {
amount = -amount;
if (shiftKeyIsDown) {
if (keyboardManager.shift) {
scrollableElement?.scrollBy({ top: amount, behavior: 'smooth' });
}
} else if (evt.key === 'ArrowDown') {
@@ -686,9 +669,9 @@
{asset}
{albumUsers}
{groupIndex}
onClick={(asset, event) => {
onClick={(asset) => {
if (typeof onThumbnailClick === 'function') {
onThumbnailClick(asset, timelineManager, timelineDay, _onClick, event);
onThumbnailClick(asset, timelineManager, timelineDay, _onClick);
} else {
_onClick(timelineManager, timelineDay.getAssets(), timelineDay.groupTitle, asset);
}

View File

@@ -15,6 +15,7 @@
import NavigateToDateModal from '$lib/modals/NavigateToDateModal.svelte';
import ShortcutsModal from '$lib/modals/ShortcutsModal.svelte';
import { Route } from '$lib/route';
import { keyboardManager } from '$lib/stores/keyboard-manager.svelte';
import { showDeleteModal } from '$lib/stores/preferences.store';
import { searchStore } from '$lib/stores/search.svelte';
import { handlePromiseError } from '$lib/utils';
@@ -73,32 +74,8 @@
assetInteraction.clear();
};
let shiftKeyIsDown = $state(false);
const onKeyDown = (event: KeyboardEvent) => {
if (searchStore.isSearchEnabled) {
return;
}
if (event.key === 'Shift') {
event.preventDefault();
shiftKeyIsDown = true;
}
};
const onKeyUp = (event: KeyboardEvent) => {
if (searchStore.isSearchEnabled) {
return;
}
if (event.key === 'Shift') {
event.preventDefault();
shiftKeyIsDown = false;
}
};
const onSelectStart = (e: Event) => {
if (assetInteraction.selectionActive && shiftKeyIsDown) {
if (!searchStore.isSearchEnabled && assetInteraction.selectionActive && keyboardManager.shift) {
e.preventDefault();
}
};
@@ -171,4 +148,4 @@
});
</script>
<svelte:document onkeydown={onKeyDown} onkeyup={onKeyUp} onselectstart={onSelectStart} use:shortcuts={shortcutList} />
<svelte:document onselectstart={onSelectStart} use:shortcuts={shortcutList} />

View File

@@ -0,0 +1,47 @@
class KeyboardManager {
#shift = $state(false);
#ctrl = $state(false);
#meta = $state(false);
#alt = $state(false);
constructor() {
if (globalThis.window === undefined) {
return;
}
globalThis.addEventListener('keydown', this.#update);
globalThis.addEventListener('keyup', this.#update);
globalThis.addEventListener('blur', this.#clear);
}
get shift() {
return this.#shift;
}
get ctrl() {
return this.#ctrl;
}
get meta() {
return this.#meta;
}
get alt() {
return this.#alt;
}
#update = (event: KeyboardEvent) => {
this.#shift = event.shiftKey;
this.#ctrl = event.ctrlKey;
this.#meta = event.metaKey;
this.#alt = event.altKey;
};
#clear = () => {
this.#shift = false;
this.#ctrl = false;
this.#meta = false;
this.#alt = false;
};
}
export const keyboardManager = new KeyboardManager();

View File

@@ -11,6 +11,7 @@
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import GeolocationPointPickerModal from '$lib/modals/GeolocationPointPickerModal.svelte';
import GeolocationUpdateConfirmModal from '$lib/modals/GeolocationUpdateConfirmModal.svelte';
import { keyboardManager } from '$lib/stores/keyboard-manager.svelte';
import type { LatLng } from '$lib/types';
import { setQueryValue } from '$lib/utils/navigation';
import { toTimelineAsset } from '$lib/utils/timeline-util';
@@ -118,13 +119,10 @@
groupTitle: string,
asset: TimelineAsset,
) => void,
event?: MouseEvent,
) => {
if (event?.shiftKey) {
if (keyboardManager.shift) {
onClick(timelineManager, timelineDay.getAssets(), timelineDay.groupTitle, asset);
return;
}
if (hasGps(asset)) {
} else if (hasGps(asset)) {
locationUpdated = true;
setTimeout(() => {
locationUpdated = false;