2026-02-01 11:55:46 +03:00
|
|
|
<!--
|
|
|
|
|
Component: SampleList
|
|
|
|
|
Renders a list of fonts in a virtualized list to improve performance.
|
2026-02-07 18:39:52 +03:00
|
|
|
- Includes pagination with auto-loading when scrolling near the bottom.
|
|
|
|
|
- Provides a typography menu for font setup.
|
2026-02-01 11:55:46 +03:00
|
|
|
-->
|
|
|
|
|
<script lang="ts">
|
|
|
|
|
import {
|
|
|
|
|
FontListItem,
|
|
|
|
|
FontVirtualList,
|
|
|
|
|
unifiedFontStore,
|
|
|
|
|
} from '$entities/Font';
|
|
|
|
|
import { FontSampler } from '$features/DisplayFont';
|
2026-02-07 18:39:52 +03:00
|
|
|
import {
|
|
|
|
|
TypographyMenu,
|
|
|
|
|
controlManager,
|
|
|
|
|
} from '$features/SetupFont';
|
2026-02-01 11:55:46 +03:00
|
|
|
|
|
|
|
|
let text = $state('The quick brown fox jumps over the lazy dog...');
|
2026-02-07 18:39:52 +03:00
|
|
|
let wrapper = $state<HTMLDivElement | null>(null);
|
|
|
|
|
// Binds to the actual window height
|
|
|
|
|
let innerHeight = $state(0);
|
|
|
|
|
// Is the component above the middle of the viewport?
|
|
|
|
|
let isAboveMiddle = $state(false);
|
|
|
|
|
|
|
|
|
|
const isLoading = $derived(unifiedFontStore.isFetching || unifiedFontStore.isLoading);
|
2026-02-01 11:55:46 +03:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Load more fonts by moving to the next page
|
|
|
|
|
*/
|
|
|
|
|
function loadMore() {
|
2026-02-02 12:20:19 +03:00
|
|
|
if (
|
|
|
|
|
!unifiedFontStore.pagination.hasMore
|
|
|
|
|
|| unifiedFontStore.isFetching
|
|
|
|
|
) {
|
2026-02-01 11:55:46 +03:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
unifiedFontStore.nextPage();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Handle scroll near bottom - auto-load next page
|
|
|
|
|
*
|
|
|
|
|
* Triggered by VirtualList when the user scrolls within 5 items of the end
|
|
|
|
|
* of the loaded items. Only fetches if there are more pages available.
|
|
|
|
|
*/
|
|
|
|
|
function handleNearBottom(_lastVisibleIndex: number) {
|
|
|
|
|
const { hasMore } = unifiedFontStore.pagination;
|
|
|
|
|
|
|
|
|
|
// VirtualList already checks if we're near the bottom of loaded items
|
|
|
|
|
if (hasMore && !unifiedFontStore.isFetching) {
|
|
|
|
|
loadMore();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Calculate display range for pagination info
|
|
|
|
|
*/
|
|
|
|
|
const displayRange = $derived.by(() => {
|
|
|
|
|
const { offset, limit, total } = unifiedFontStore.pagination;
|
|
|
|
|
const loadedCount = Math.min(offset + limit, total);
|
|
|
|
|
return `Showing ${loadedCount} of ${total} fonts`;
|
|
|
|
|
});
|
|
|
|
|
|
2026-02-07 18:39:52 +03:00
|
|
|
function checkPosition() {
|
|
|
|
|
if (!wrapper) return;
|
|
|
|
|
|
|
|
|
|
const rect = wrapper.getBoundingClientRect();
|
|
|
|
|
const viewportMiddle = innerHeight / 2;
|
|
|
|
|
|
|
|
|
|
isAboveMiddle = rect.top < viewportMiddle;
|
|
|
|
|
}
|
2026-02-06 12:05:29 +03:00
|
|
|
</script>
|
2026-02-01 11:55:46 +03:00
|
|
|
|
2026-02-07 18:39:52 +03:00
|
|
|
<svelte:window
|
|
|
|
|
bind:innerHeight
|
|
|
|
|
onscroll={checkPosition}
|
|
|
|
|
onresize={checkPosition}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<div bind:this={wrapper}>
|
|
|
|
|
<FontVirtualList
|
|
|
|
|
items={unifiedFontStore.fonts}
|
|
|
|
|
total={unifiedFontStore.pagination.total}
|
|
|
|
|
onNearBottom={handleNearBottom}
|
|
|
|
|
itemHeight={220}
|
|
|
|
|
useWindowScroll={true}
|
|
|
|
|
weight={controlManager.weight}
|
|
|
|
|
{isLoading}
|
|
|
|
|
>
|
|
|
|
|
{#snippet children({
|
2026-02-06 12:05:29 +03:00
|
|
|
item: font,
|
|
|
|
|
isFullyVisible,
|
|
|
|
|
isPartiallyVisible,
|
|
|
|
|
proximity,
|
|
|
|
|
index,
|
|
|
|
|
})}
|
2026-02-07 18:39:52 +03:00
|
|
|
<FontListItem {font} {isFullyVisible} {isPartiallyVisible} {proximity}>
|
|
|
|
|
<FontSampler {font} bind:text {index} />
|
|
|
|
|
</FontListItem>
|
|
|
|
|
{/snippet}
|
|
|
|
|
</FontVirtualList>
|
|
|
|
|
|
2026-02-09 16:49:56 +03:00
|
|
|
<TypographyMenu
|
|
|
|
|
class="fixed bottom-4 sm:bottom-5 right-4 sm:left-1/2 sm:right-[unset] sm:-translate-x-1/2"
|
|
|
|
|
hidden={!isAboveMiddle}
|
|
|
|
|
/>
|
2026-02-07 18:39:52 +03:00
|
|
|
</div>
|