From 839460726eb4c74112835a45c319c0c2d39af91f Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Mon, 1 Jun 2026 18:46:10 +0300 Subject: [PATCH] refactor(breadcrumb): replace scrollBreadcrumbsStore singleton with lazy accessor Convert the eager scrollBreadcrumbsStore singleton to getScrollBreadcrumbsStore() (+ __resetScrollBreadcrumbsStore for tests) and add a public destroy() that disconnects the IntersectionObserver and scroll listener. Update the feature barrel and consumers (BreadcrumbHeader, BreadcrumbHeaderSeeded, NavigationWrapper). --- src/features/Breadcrumb/index.ts | 2 +- .../store/scrollBreadcrumbsStore.svelte.ts | 21 +++++++++++++++++-- .../BreadcrumbHeader/BreadcrumbHeader.svelte | 3 ++- .../BreadcrumbHeaderSeeded.svelte | 4 +++- .../NavigationWrapper.svelte | 4 +++- 5 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/features/Breadcrumb/index.ts b/src/features/Breadcrumb/index.ts index 882b16e..b148cc3 100644 --- a/src/features/Breadcrumb/index.ts +++ b/src/features/Breadcrumb/index.ts @@ -26,8 +26,8 @@ */ export { + getScrollBreadcrumbsStore, type NavigationAction, - scrollBreadcrumbsStore, } from './model'; export { BreadcrumbHeader, diff --git a/src/features/Breadcrumb/model/store/scrollBreadcrumbsStore.svelte.ts b/src/features/Breadcrumb/model/store/scrollBreadcrumbsStore.svelte.ts index 74badbe..4183428 100644 --- a/src/features/Breadcrumb/model/store/scrollBreadcrumbsStore.svelte.ts +++ b/src/features/Breadcrumb/model/store/scrollBreadcrumbsStore.svelte.ts @@ -167,6 +167,13 @@ class ScrollBreadcrumbsStore { this.#detachScrollListener(); } + /** + * Tears down the observer and scroll listener. Call on store disposal. + */ + destroy(): void { + this.#disconnect(); + } + /** * All tracked items sorted by index */ @@ -272,7 +279,17 @@ export function createScrollBreadcrumbsStore(): ScrollBreadcrumbsStore { return new ScrollBreadcrumbsStore(); } +let _scrollBreadcrumbsStore: ScrollBreadcrumbsStore | undefined; + /** - * Singleton scroll breadcrumbs store instance + * App-wide scroll breadcrumbs store, created on first access. */ -export const scrollBreadcrumbsStore = createScrollBreadcrumbsStore(); +export function getScrollBreadcrumbsStore(): ScrollBreadcrumbsStore { + return (_scrollBreadcrumbsStore ??= createScrollBreadcrumbsStore()); +} + +// test-only reset, so specs don't share observer/scroll state +export function __resetScrollBreadcrumbsStore() { + _scrollBreadcrumbsStore?.destroy(); + _scrollBreadcrumbsStore = undefined; +} diff --git a/src/features/Breadcrumb/ui/BreadcrumbHeader/BreadcrumbHeader.svelte b/src/features/Breadcrumb/ui/BreadcrumbHeader/BreadcrumbHeader.svelte index e48dcf9..73b7aee 100644 --- a/src/features/Breadcrumb/ui/BreadcrumbHeader/BreadcrumbHeader.svelte +++ b/src/features/Breadcrumb/ui/BreadcrumbHeader/BreadcrumbHeader.svelte @@ -14,9 +14,10 @@ import { cubicOut } from 'svelte/easing'; import { slide } from 'svelte/transition'; import { type BreadcrumbItem, - scrollBreadcrumbsStore, + getScrollBreadcrumbsStore, } from '../../model'; +const scrollBreadcrumbsStore = getScrollBreadcrumbsStore(); const breadcrumbs = $derived(scrollBreadcrumbsStore.scrolledPastItems); const responsive = getContext('responsive'); diff --git a/src/features/Breadcrumb/ui/BreadcrumbHeader/BreadcrumbHeaderSeeded.svelte b/src/features/Breadcrumb/ui/BreadcrumbHeader/BreadcrumbHeaderSeeded.svelte index 792d86c..af9cc17 100644 --- a/src/features/Breadcrumb/ui/BreadcrumbHeader/BreadcrumbHeaderSeeded.svelte +++ b/src/features/Breadcrumb/ui/BreadcrumbHeader/BreadcrumbHeaderSeeded.svelte @@ -1,8 +1,10 @@