feat: keyboard navigation to timeline (#17798)

* feat: improve focus

* feat: keyboard nav

* feat: improve focus

* typo

* test

* fix test

* lint

* bad merge

* lint

* inadvertent

* lint

* fix: flappy e2e test

* bad merge and fix tests

* use modulus in loop

* tests

* react to modal dialog refactor

* regression due to deferLayout

* Review comments

* Re-use change-date instead of new component

* bad merge

* Review comments

* rework moveFocus

* lint

* Fix outline

* use Date

* Finish up removing/reducing date parsing

* lint

* title

* strings

* Rework dates, rework earlier/later algorithm

* bad merge

* fix tests

* Fix race in scroll comp

* consolidate scroll methods

* Review comments

* console.log

* Edge cases in scroll compensation

* edge case, optimizations

* review comments

* lint

* lint

* More edge cases

* lint

---------

Co-authored-by: mertalev <101130780+mertalev@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
Min Idzelis 2025-05-28 09:55:14 -04:00 committed by GitHub
parent b5593823a2
commit f029910dc7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 1077 additions and 598 deletions

View file

@ -10,15 +10,13 @@
type TimelineAsset,
} from '$lib/stores/assets-store.svelte';
import { navigate } from '$lib/utils/navigation';
import { getDateLocaleString } from '$lib/utils/timeline-util';
import { mdiCheckCircle, mdiCircleOutline } from '@mdi/js';
import { fly, scale } from 'svelte/transition';
import Thumbnail from '../assets/thumbnail/thumbnail.svelte';
import { flip } from 'svelte/animate';
import { uploadAssetsStore } from '$lib/stores/upload';
import { flip } from 'svelte/animate';
let { isUploading } = uploadAssetsStore;
@ -34,6 +32,7 @@
onSelect: ({ title, assets }: { title: string; assets: TimelineAsset[] }) => void;
onSelectAssets: (asset: TimelineAsset) => void;
onSelectAssetCandidates: (asset: TimelineAsset | null) => void;
onScrollCompensation: (compensation: { heightDelta?: number; scrollTop?: number }) => void;
}
let {
@ -47,6 +46,7 @@
onSelect,
onSelectAssets,
onSelectAssetCandidates,
onScrollCompensation,
}: Props = $props();
let isMouseOverGroup = $state(false);
@ -84,7 +84,7 @@
assetInteraction.removeGroupFromMultiselectGroup(groupTitle);
}
if (assetStore.getAssets().length == assetInteraction.selectedAssets.length) {
if (assetStore.count == assetInteraction.selectedAssets.length) {
isSelectingAllAssets.set(true);
} else {
isSelectingAllAssets.set(false);
@ -103,9 +103,16 @@
function filterIntersecting<R extends { intersecting: boolean }>(intersectable: R[]) {
return intersectable.filter((int) => int.intersecting);
}
$effect.root(() => {
if (assetStore.scrollCompensation.bucket === bucket) {
onScrollCompensation(assetStore.scrollCompensation);
assetStore.clearScrollCompensation();
}
});
</script>
{#each filterIntersecting(bucket.dateGroups) as dateGroup, groupIndex (dateGroup.date)}
{#each filterIntersecting(bucket.dateGroups) as dateGroup, groupIndex (dateGroup.day)}
{@const absoluteWidth = dateGroup.left}
<!-- svelte-ignore a11y_no_static_element_interactions -->
@ -146,7 +153,7 @@
</div>
{/if}
<span class="w-full truncate first-letter:capitalize ms-2.5" title={getDateLocaleString(dateGroup.date)}>
<span class="w-full truncate first-letter:capitalize ms-2.5" title={dateGroup.groupTitle}>
{dateGroup.groupTitle}
</span>
</div>
@ -158,7 +165,7 @@
style:height={dateGroup.height + 'px'}
style:width={dateGroup.width + 'px'}
>
{#each filterIntersecting(dateGroup.intersetingAssets) as intersectingAsset (intersectingAsset.id)}
{#each filterIntersecting(dateGroup.intersectingAssets) as intersectingAsset (intersectingAsset.id)}
{@const position = intersectingAsset.position!}
{@const asset = intersectingAsset.asset!}