fleeting-garden/src/page/vibe-navigator.ts

85 lines
2.5 KiB
TypeScript

import { activeVibe, applyVibeSettings, rememberActiveVibeSelection } from '../settings';
import { queryRequiredElement } from '../utils/dom';
import { getCurrentUriVibeId, writeCurrentVibeUri } from '../vibe-uri';
import { getVibeById, VIBE_PRESETS, type VibeId } from '../vibes';
interface VibeSelection {
source: string;
userGesture: boolean;
vibeId: VibeId;
vibeName: string;
}
interface VibeNavigatorOptions {
onChange: (selection: VibeSelection) => void;
}
export class VibeNavigator {
private readonly abortController = new AbortController();
private readonly previousButton = queryRequiredElement(
'.previous-vibe',
HTMLButtonElement
);
private readonly nextButton = queryRequiredElement('.next-vibe', HTMLButtonElement);
public constructor(private readonly options: VibeNavigatorOptions) {
rememberActiveVibeSelection();
writeCurrentVibeUri(activeVibe.id, 'replace');
const { signal } = this.abortController;
this.previousButton.addEventListener(
'click',
() => this.select(-1, 'previous-button'),
{ signal }
);
this.nextButton.addEventListener('click', () => this.select(1, 'next-button'), {
signal,
});
window.addEventListener('popstate', this.selectFromCurrentUri, { signal });
}
public destroy(): void {
this.abortController.abort();
}
private select(offset: number, source: string): void {
const current = VIBE_PRESETS.findIndex((vibe) => vibe.id === activeVibe.id);
const currentIndex = current >= 0 ? current : 0;
const vibe =
VIBE_PRESETS[(currentIndex + VIBE_PRESETS.length + offset) % VIBE_PRESETS.length];
const activePreset = applyVibeSettings(vibe);
writeCurrentVibeUri(activePreset.id, 'push');
this.notifyChange(activePreset, source, true);
}
private readonly selectFromCurrentUri = (): void => {
const vibeId = getCurrentUriVibeId();
if (!vibeId || vibeId === activeVibe.id) {
writeCurrentVibeUri(activeVibe.id, 'replace');
return;
}
const vibe = getVibeById(vibeId);
if (!vibe) {
writeCurrentVibeUri(activeVibe.id, 'replace');
return;
}
const activePreset = applyVibeSettings(vibe);
writeCurrentVibeUri(activePreset.id, 'replace');
this.notifyChange(activePreset, 'uri-popstate', false);
};
private notifyChange(
activePreset: typeof activeVibe,
source: string,
userGesture: boolean
): void {
this.options.onChange({
userGesture,
vibeId: activePreset.id,
vibeName: activePreset.name,
source,
});
}
}