diff --git a/src/entities/Font/ui/FontVirtualList/FontVirtualList.svelte b/src/entities/Font/ui/FontVirtualList/FontVirtualList.svelte index ec899d9..984b89a 100644 --- a/src/entities/Font/ui/FontVirtualList/FontVirtualList.svelte +++ b/src/entities/Font/ui/FontVirtualList/FontVirtualList.svelte @@ -4,8 +4,12 @@ - Handles font registration with the manager --> - - {#snippet children(scope)} - {@render children(scope)} - {/snippet} - +{#key isLoading} +
+ {#if isLoading} +
+ {#each Array(5) as _, i} +
+
+ + +
+ +
+ {/each} +
+ {:else} + + {#snippet children(scope)} + {@render children(scope)} + {/snippet} + + {/if} +
+{/key} diff --git a/src/features/DisplayFont/ui/FontSampler/FontSampler.svelte b/src/features/DisplayFont/ui/FontSampler/FontSampler.svelte index 453c23f..9d74d11 100644 --- a/src/features/DisplayFont/ui/FontSampler/FontSampler.svelte +++ b/src/features/DisplayFont/ui/FontSampler/FontSampler.svelte @@ -11,9 +11,9 @@ import { import { controlManager } from '$features/SetupFont'; import { ContentEditable, - IconButton, + // IconButton, } from '$shared/ui'; -import XIcon from '@lucide/svelte/icons/x'; +// import XIcon from '@lucide/svelte/icons/x'; interface Props { /** @@ -75,14 +75,16 @@ function removeSample() { - - {#snippet icon({ className })} - - {/snippet} - +
diff --git a/src/routes/Page.svelte b/src/routes/Page.svelte index 6563b4f..87dde72 100644 --- a/src/routes/Page.svelte +++ b/src/routes/Page.svelte @@ -10,8 +10,8 @@ import ComparisonSlider from '$widgets/ComparisonSlider/ui/ComparisonSlider/Comp import { FontSearch } from '$widgets/FontSearch'; import { SampleList } from '$widgets/SampleList'; import CodeIcon from '@lucide/svelte/icons/code'; +import EyeIcon from '@lucide/svelte/icons/eye'; import LineSquiggleIcon from '@lucide/svelte/icons/line-squiggle'; -import ScanEyeIcon from '@lucide/svelte/icons/scan-eye'; import ScanSearchIcon from '@lucide/svelte/icons/search'; import type { Snippet } from 'svelte'; @@ -59,7 +59,7 @@ function handleTitleStatusChanged(index: number, isPast: boolean, title?: Snippe
{#snippet icon({ className })} - + {/snippet} {#snippet title({ className })}

diff --git a/src/shared/shadcn/ui/spinner/index.ts b/src/shared/shadcn/ui/spinner/index.ts new file mode 100644 index 0000000..2e459c6 --- /dev/null +++ b/src/shared/shadcn/ui/spinner/index.ts @@ -0,0 +1 @@ +export { default as Spinner } from './spinner.svelte'; diff --git a/src/shared/shadcn/ui/spinner/spinner.svelte b/src/shared/shadcn/ui/spinner/spinner.svelte new file mode 100644 index 0000000..e897fae --- /dev/null +++ b/src/shared/shadcn/ui/spinner/spinner.svelte @@ -0,0 +1,14 @@ + + + diff --git a/src/shared/ui/Loader/Loader.stories.svelte b/src/shared/ui/Loader/Loader.stories.svelte new file mode 100644 index 0000000..37739e3 --- /dev/null +++ b/src/shared/ui/Loader/Loader.stories.svelte @@ -0,0 +1,33 @@ + + + + + diff --git a/src/shared/ui/Loader/Loader.svelte b/src/shared/ui/Loader/Loader.svelte new file mode 100644 index 0000000..2789f74 --- /dev/null +++ b/src/shared/ui/Loader/Loader.svelte @@ -0,0 +1,77 @@ + + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + {message} + +
diff --git a/src/shared/ui/Skeleton/Skeleton.stories.svelte b/src/shared/ui/Skeleton/Skeleton.stories.svelte new file mode 100644 index 0000000..426efb8 --- /dev/null +++ b/src/shared/ui/Skeleton/Skeleton.stories.svelte @@ -0,0 +1,41 @@ + + + +
+
+
+ + +
+ +
+
+
diff --git a/src/shared/ui/Skeleton/Skeleton.svelte b/src/shared/ui/Skeleton/Skeleton.svelte new file mode 100644 index 0000000..0e28399 --- /dev/null +++ b/src/shared/ui/Skeleton/Skeleton.svelte @@ -0,0 +1,27 @@ + + + +
+
diff --git a/src/shared/ui/VirtualList/VirtualList.svelte b/src/shared/ui/VirtualList/VirtualList.svelte index d4e2f05..b0de643 100644 --- a/src/shared/ui/VirtualList/VirtualList.svelte +++ b/src/shared/ui/VirtualList/VirtualList.svelte @@ -101,13 +101,25 @@ interface Props { * @template T - The type of items in the list */ children: Snippet< - [{ item: T; index: number; isFullyVisible: boolean; isPartiallyVisible: boolean; proximity: number }] + [ + { + item: T; + index: number; + isFullyVisible: boolean; + isPartiallyVisible: boolean; + proximity: number; + }, + ] >; /** * Whether to use the window as the scroll container. * @default false */ useWindowScroll?: boolean; + /** + * Flag to show loading state + */ + isLoading?: boolean; } let { @@ -120,6 +132,7 @@ let { onNearBottom, children, useWindowScroll = false, + isLoading = false, }: Props = $props(); // Reference to the ScrollArea viewport element for attaching the virtualizer diff --git a/src/shared/ui/index.ts b/src/shared/ui/index.ts index a8429cb..c5161a2 100644 --- a/src/shared/ui/index.ts +++ b/src/shared/ui/index.ts @@ -1,27 +1,12 @@ -/** - * Shared UI components exports - * - * Exports all shared UI components and their types - */ - -import CheckboxFilter from './CheckboxFilter/CheckboxFilter.svelte'; -import ComboControl from './ComboControl/ComboControl.svelte'; -import ComboControlV2 from './ComboControlV2/ComboControlV2.svelte'; -import ContentEditable from './ContentEditable/ContentEditable.svelte'; -import ExpandableWrapper from './ExpandableWrapper/ExpandableWrapper.svelte'; -import IconButton from './IconButton/IconButton.svelte'; -import SearchBar from './SearchBar/SearchBar.svelte'; -import Section from './Section/Section.svelte'; -import VirtualList from './VirtualList/VirtualList.svelte'; - -export { - CheckboxFilter, - ComboControl, - ComboControlV2, - ContentEditable, - ExpandableWrapper, - IconButton, - SearchBar, - Section, - VirtualList, -}; +export { default as CheckboxFilter } from './CheckboxFilter/CheckboxFilter.svelte'; +export { default as ComboControl } from './ComboControl/ComboControl.svelte'; +// ComboControlV2 might vary, assuming pattern holds or I'll fix later if build fails +export { default as ComboControlV2 } from './ComboControlV2/ComboControlV2.svelte'; +export { default as ContentEditable } from './ContentEditable/ContentEditable.svelte'; +export { default as ExpandableWrapper } from './ExpandableWrapper/ExpandableWrapper.svelte'; +export { default as IconButton } from './IconButton/IconButton.svelte'; +export { default as Loader } from './Loader/Loader.svelte'; +export { default as SearchBar } from './SearchBar/SearchBar.svelte'; +export { default as Section } from './Section/Section.svelte'; +export { default as Skeleton } from './Skeleton/Skeleton.svelte'; +export { default as VirtualList } from './VirtualList/VirtualList.svelte'; diff --git a/src/widgets/ComparisonSlider/model/stores/comparisonStore.svelte.ts b/src/widgets/ComparisonSlider/model/stores/comparisonStore.svelte.ts index 503900f..f6ae670 100644 --- a/src/widgets/ComparisonSlider/model/stores/comparisonStore.svelte.ts +++ b/src/widgets/ComparisonSlider/model/stores/comparisonStore.svelte.ts @@ -136,6 +136,10 @@ class ComparisonStore { return !!this.#fontA && !!this.#fontB; } + get isLoading() { + return this.#isRestoring; + } + /** * Public initializer (optional, as constructor starts it) * Kept for compatibility if manual re-init is needed diff --git a/src/widgets/ComparisonSlider/ui/ComparisonSlider/ComparisonSlider.svelte b/src/widgets/ComparisonSlider/ui/ComparisonSlider/ComparisonSlider.svelte index 2120233..bcd3eef 100644 --- a/src/widgets/ComparisonSlider/ui/ComparisonSlider/ComparisonSlider.svelte +++ b/src/widgets/ComparisonSlider/ui/ComparisonSlider/ComparisonSlider.svelte @@ -15,8 +15,10 @@ import { createTypographyControl, } from '$shared/lib'; import type { LineData } from '$shared/lib'; +import { Loader } from '$shared/ui'; import { comparisonStore } from '$widgets/ComparisonSlider/model'; import { Spring } from 'svelte/motion'; +import { fade } from 'svelte/transition'; import CharacterSlot from './components/CharacterSlot.svelte'; import ControlsWrapper from './components/ControlsWrapper.svelte'; import Labels from './components/Labels.svelte'; @@ -26,6 +28,8 @@ import SliderLine from './components/SliderLine.svelte'; const fontA = $derived(comparisonStore.fontA); const fontB = $derived(comparisonStore.fontB); +const isLoading = $derived(comparisonStore.isLoading || !comparisonStore.isReady); + let container: HTMLElement | undefined = $state(); let controlsWrapperElement = $state(null); let measureCanvas: HTMLCanvasElement | undefined = $state(); @@ -164,31 +168,33 @@ $effect(() => {

{/snippet} -{#if fontA && fontB} - - + + -
-
- +
+
+ + {#if isLoading} + + {:else}
{#each charComparison.lines as line, lineIndex}
{
-
+ {/if} +
+ {#if fontA && fontB && !isLoading} { {sizeControl} {heightControl} /> -
-{/if} + {/if} +
diff --git a/src/widgets/ComparisonSlider/ui/ComparisonSlider/components/ControlsWrapper.svelte b/src/widgets/ComparisonSlider/ui/ComparisonSlider/components/ControlsWrapper.svelte index c23106b..7dd0fe7 100644 --- a/src/widgets/ComparisonSlider/ui/ComparisonSlider/components/ControlsWrapper.svelte +++ b/src/widgets/ComparisonSlider/ui/ComparisonSlider/components/ControlsWrapper.svelte @@ -12,6 +12,7 @@ import { ComboControlV2 } from '$shared/ui'; import { ExpandableWrapper } from '$shared/ui'; import AArrowUP from '@lucide/svelte/icons/a-arrow-up'; import { Spring } from 'svelte/motion'; +import { fade } from 'svelte/transition'; interface Props { /** @@ -121,6 +122,8 @@ $effect(() => { translateX({xSpring.current}px) rotateZ({rotateSpring.current}deg) " + in:fade={{ duration: 300, delay: 300 }} + out:fade={{ duration: 300, delay: 300 }} > { /** @@ -60,6 +61,8 @@ function selectFontB(font: UnifiedFont) {
e.stopPropagation())} + in:fade={{ duration: 300, delay: 300 }} + out:fade={{ duration: 300, delay: 300 }} >
- +
diff --git a/src/widgets/SampleList/ui/SampleList/SampleList.svelte b/src/widgets/SampleList/ui/SampleList/SampleList.svelte index 112f3fb..9ffad72 100644 --- a/src/widgets/SampleList/ui/SampleList/SampleList.svelte +++ b/src/widgets/SampleList/ui/SampleList/SampleList.svelte @@ -50,11 +50,9 @@ const displayRange = $derived.by(() => { const loadedCount = Math.min(offset + limit, total); return `Showing ${loadedCount} of ${total} fonts`; }); - -{#if unifiedFontStore.isFetching || unifiedFontStore.isLoading} - (Loading...) -{/if} +const isLoading = $derived(unifiedFontStore.isFetching || unifiedFontStore.isLoading); + { itemHeight={280} useWindowScroll={true} weight={controlManager.weight} + {isLoading} > - {#snippet children({ item: font, isFullyVisible, isPartiallyVisible, proximity, index })} + {#snippet children({ + item: font, + isFullyVisible, + isPartiallyVisible, + proximity, + index, +})}