Files
frontend-svelte/src/features/DisplayFont/ui/FontSampler/FontSampler.svelte

171 lines
5.7 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!--
Component: FontSampler
Displays a sample text with a given font in a contenteditable element.
Visual design matches FontCard: sharp corners, red hover accent, header stats.
-->
<script lang="ts">
import {
FontApplicator,
type UnifiedFont,
} from '$entities/Font';
import { typographySettingsStore } from '$features/SetupFont/model';
import {
Badge,
ContentEditable,
Divider,
Footnote,
Stat,
} from '$shared/ui';
import { fly } from 'svelte/transition';
interface Props {
/**
* Font info
*/
font: UnifiedFont;
/**
* Sample text
*/
text: string;
/**
* Position index
* @default 0
*/
index?: number;
}
let { font, text = $bindable(), index = 0 }: Props = $props();
// Adjust the property name to match your UnifiedFont type
const fontType = $derived((font as any).type ?? (font as any).category ?? '');
// Extract provider badge with fallback
const providerBadge = $derived(
font.providerBadge
?? (font.provider === 'google' ? 'Google Fonts' : 'Fontshare'),
);
const stats = $derived([
{ label: 'SZ', value: `${typographySettingsStore.renderedSize}PX` },
{ label: 'WGT', value: `${typographySettingsStore.weight}` },
{ label: 'LH', value: typographySettingsStore.height?.toFixed(2) },
{ label: 'LTR', value: `${typographySettingsStore.spacing}` },
]);
</script>
<div
in:fly={{ y: 20, duration: 400, delay: index * 50 }}
class="
group relative
w-full h-full
bg-paper dark:bg-dark-card
border border-subtle
hover:border-brand dark:hover:border-brand
hover:shadow-brand/10
hover:shadow-[5px_5px_0px_0px]
transition-all duration-200
overflow-hidden
flex flex-col
min-h-60
rounded-none
"
style:font-weight={typographySettingsStore.weight}
>
<!-- ── Header bar ─────────────────────────────────────────────────── -->
<div
class="
flex items-center justify-between
px-4 sm:px-5 md:px-6 py-3 sm:py-4
border-b border-subtle
bg-paper dark:bg-dark-card
"
>
<!-- Left: index · name · type badge · provider badge -->
<div class="flex items-center gap-2 sm:gap-4 min-w-0 shrink-0">
<span class="font-mono text-2xs tracking-widest text-neutral-400 uppercase leading-none shrink-0">
{String(index + 1).padStart(2, '0')}
</span>
<Divider orientation="vertical" class="h-3 shrink-0" />
<span
class="font-primary font-bold text-sm text-swiss-black dark:text-neutral-200 leading-none tracking-tight uppercase truncate"
>
{font.name}
</span>
{#if fontType}
<Badge size="xs" variant="default" nowrap>
{fontType}
</Badge>
{/if}
<!-- Provider badge -->
{#if providerBadge}
<Badge size="xs" variant="default" nowrap data-provider={font.provider}>
{providerBadge}
</Badge>
{/if}
</div>
<!-- Right: stats, hidden on mobile, fade in on group hover -->
<div
class="
flex-1 min-w-0
hidden md:block @container
opacity-50 group-hover:opacity-100
transition-opacity duration-200 ml-4
"
>
<!-- Switches: narrow → 2×2, wide enough → 1 row -->
<div
class="
max-w-64 ml-auto
grid grid-cols-2 gap-x-3 gap-y-2
@[160px]:grid-cols-4 @[160px]:gap-y-0
items-center
"
>
{#each stats as stat}
<Stat label={stat.label} value={stat.value} />
{/each}
</div>
</div>
</div>
<!-- ── Main content area ──────────────────────────────────────────── -->
<div class="flex-1 p-4 sm:p-5 md:p-8 flex items-center overflow-hidden bg-paper dark:bg-dark-card relative z-10">
<FontApplicator {font} weight={typographySettingsStore.weight}>
<ContentEditable
bind:text
fontSize={typographySettingsStore.renderedSize}
lineHeight={typographySettingsStore.height}
letterSpacing={typographySettingsStore.spacing}
/>
</FontApplicator>
</div>
<!-- ── Mobile stats footer (md:hidden — header stats take over above) -->
<div class="md:hidden px-4 sm:px-5 py-1.5 sm:py-2 border-t border-subtle flex gap-2 sm:gap-4 bg-paper dark:bg-dark-card mt-auto">
{#each stats as stat, i}
<Footnote class="text-5xs sm:text-4xs tracking-wider {i === 0 ? 'ml-auto' : ''}">
{stat.label}:{stat.value}
</Footnote>
{#if i < stats.length - 1}
<div class="w-px h-2 sm:h-2.5 self-center bg-black/10 dark:bg-white/10 hidden sm:block"></div>
{/if}
{/each}
</div>
<!-- ── Red hover line ─────────────────────────────────────────────── -->
<div
class="
absolute bottom-0 left-0 right-0
w-full h-0.5 bg-brand
scale-x-0 group-hover:scale-x-100
transition-transform cubic-bezier(0.25, 0.1, 0.25, 1) origin-left duration-400
z-10
"
>
</div>
</div>