Inline DateGroupActionLib logic into asset-date-group-selection-aware

• Remove DateGroupActionLib dependency and inline all selection logic
• Implement proper selection state predicates instead of placeholder true values
• Move keyboard event handlers and selection logic directly into component
This commit is contained in:
midzelis 2025-08-14 20:46:22 +00:00
parent 93a5351031
commit 4f441e6682

View File

@ -9,8 +9,8 @@
import AssetDateGroupComp from '$lib/components/photos-page/asset-date-group-comp.svelte';
import { DayGroup } from '$lib/managers/timeline-manager/day-group.svelte';
import { onMount } from 'svelte';
import { DateGroupActionLib } from './date-group-actions-lib.svelte';
import { assetsSnapshot } from '$lib/managers/timeline-manager/utils.svelte';
import { searchStore } from '$lib/stores/search.svelte';
let { isUploading } = uploadAssetsStore;
@ -40,14 +40,20 @@
onScrollToTop,
}: Props = $props();
const actionLib = new DateGroupActionLib();
let lastAssetMouseEvent: TimelineAsset | null = $state(null);
let shiftKeyIsDown = $state(false);
let isEmpty = $derived(timelineManager.isInitialized && timelineManager.months.length === 0);
onMount(() => {
actionLib.assetInteraction = assetInteraction;
actionLib.timelineManager = timelineManager;
actionLib.singleSelect = singleSelect;
actionLib.onSelect = onSelect;
actionLib.onScrollToTop = onScrollToTop;
$effect(() => {
if (!lastAssetMouseEvent || !lastAssetMouseEvent) {
assetInteraction.clearAssetSelectionCandidates();
}
if (shiftKeyIsDown && lastAssetMouseEvent) {
void selectAssetCandidates(lastAssetMouseEvent);
}
if (isEmpty) {
assetInteraction.clearMultiselect();
}
});
const handleOnAssetOpen = (dayGroup: DayGroup, asset: TimelineAsset) => {
@ -60,7 +66,7 @@
// called when clicking asset with shift key pressed or with mouse
const handleAssetSelect = (dayGroup: DayGroup, asset: TimelineAsset) => {
void actionLib.onSelectAssets(asset);
void onSelectAssets(asset);
const assetsInDayGroup = dayGroup.getAssets();
const groupTitle = dayGroup.groupTitle;
@ -83,14 +89,163 @@
}
};
const handleSelectAsset = (asset: TimelineAsset) => {
if (!timelineManager.albumAssets.has(asset.id)) {
assetInteraction.selectAsset(asset);
}
};
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 handleOnHover = (dayGroup: DayGroup, asset: TimelineAsset) => {
if (assetInteraction.selectionActive) {
actionLib.onSelectAssetCandidates(asset);
void selectAssetCandidates(asset);
}
lastAssetMouseEvent = asset;
};
const handleDayGroupSelect = (dayGroup: DayGroup, assets: TimelineAsset[]) => {
const group = dayGroup.groupTitle;
if (assetInteraction.selectedGroup.has(group)) {
assetInteraction.removeGroupFromMultiselectGroup(group);
for (const asset of assets) {
assetInteraction.removeAssetFromMultiselectGroup(asset.id);
}
} else {
assetInteraction.addGroupToMultiselectGroup(group);
for (const asset of assets) {
handleSelectAsset(asset);
}
}
if (timelineManager.assetCount == assetInteraction.selectedAssets.length) {
isSelectingAllAssets.set(true);
} else {
isSelectingAllAssets.set(false);
}
};
const onSelectAssets = async (asset: TimelineAsset) => {
if (!asset) {
return;
}
onSelect(asset);
if (singleSelect) {
onScrollToTop();
return;
}
const rangeSelection = assetInteraction.assetSelectionCandidates.length > 0;
const deselect = assetInteraction.hasSelectedAsset(asset.id);
// Select/deselect already loaded assets
if (deselect) {
for (const candidate of assetInteraction.assetSelectionCandidates) {
assetInteraction.removeAssetFromMultiselectGroup(candidate.id);
}
assetInteraction.removeAssetFromMultiselectGroup(asset.id);
} else {
for (const candidate of assetInteraction.assetSelectionCandidates) {
handleSelectAsset(candidate);
}
handleSelectAsset(asset);
}
assetInteraction.clearAssetSelectionCandidates();
if (assetInteraction.assetSelectionStart && rangeSelection) {
let startBucket = timelineManager.getMonthGroupByAssetId(assetInteraction.assetSelectionStart.id);
let endBucket = timelineManager.getMonthGroupByAssetId(asset.id);
if (startBucket === null || endBucket === null) {
return;
}
// Select/deselect assets in range (start,end)
let started = false;
for (const monthGroup of timelineManager.months) {
if (monthGroup === endBucket) {
break;
}
if (started) {
await timelineManager.loadMonthGroup(monthGroup.yearMonth);
for (const asset of monthGroup.assetsIterator()) {
if (deselect) {
assetInteraction.removeAssetFromMultiselectGroup(asset.id);
} else {
handleSelectAsset(asset);
}
}
}
if (monthGroup === startBucket) {
started = true;
}
}
// Update date group selection in range [start,end]
started = false;
for (const monthGroup of timelineManager.months) {
if (monthGroup === startBucket) {
started = true;
}
if (started) {
// Split month group into day groups and check each group
for (const dayGroup of monthGroup.dayGroups) {
const dayGroupTitle = dayGroup.groupTitle;
if (dayGroup.getAssets().every((a) => assetInteraction.hasSelectedAsset(a.id))) {
assetInteraction.addGroupToMultiselectGroup(dayGroupTitle);
} else {
assetInteraction.removeGroupFromMultiselectGroup(dayGroupTitle);
}
}
}
if (monthGroup === endBucket) {
break;
}
}
}
assetInteraction.setAssetSelectionStart(deselect ? null : asset);
};
const selectAssetCandidates = async (endAsset: TimelineAsset) => {
if (!shiftKeyIsDown) {
return;
}
const startAsset = assetInteraction.assetSelectionStart;
if (!startAsset) {
return;
}
const assets = assetsSnapshot(await timelineManager.retrieveRange(startAsset, endAsset));
assetInteraction.setAssetSelectionCandidates(assets);
};
</script>
<svelte:document onkeydown={actionLib.onKeyDown} onkeyup={actionLib.onKeyUp} />
<svelte:document onkeydown={onKeyDown} onkeyup={onKeyUp} />
<AssetDateGroupComp
{isSelectionMode}
@ -103,9 +258,9 @@
onHover={handleOnHover}
onAssetOpen={handleOnAssetOpen}
onAssetSelect={handleAssetSelect}
onDayGroupSelect={actionLib.onDayGroupSelect}
isDayGroupSelected={(dayGroup: DayGroup) => true}
isAssetSelected={(asset) => true}
isAssetSelectionCandidate={(asset) => true}
isAssetDisabled={(asset) => true}
onDayGroupSelect={handleDayGroupSelect}
isDayGroupSelected={(dayGroup: DayGroup) => assetInteraction.selectedGroup.has(dayGroup.groupTitle)}
isAssetSelected={(asset) => assetInteraction.hasSelectedAsset(asset.id) || timelineManager.albumAssets.has(asset.id)}
isAssetSelectionCandidate={(asset) => assetInteraction.hasSelectionCandidate(asset.id)}
isAssetDisabled={(asset) => timelineManager.albumAssets.has(asset.id)}
></AssetDateGroupComp>