feat(FontSampler): refactor component to align it with new design

This commit is contained in:
Ilia Mashkov
2026-02-27 12:42:18 +03:00
parent 9983be650a
commit 12718593e3

View File

@@ -1,6 +1,7 @@
<!-- <!--
Component: FontSampler Component: FontSampler
Displays a sample text with a given font in a contenteditable element. 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"> <script lang="ts">
import { import {
@@ -11,78 +12,115 @@ import { controlManager } from '$features/SetupFont';
import { import {
ContentEditable, ContentEditable,
Footnote, Footnote,
// IconButton,
} from '$shared/ui'; } from '$shared/ui';
// import XIcon from '@lucide/svelte/icons/x'; import { fly } from 'svelte/transition';
interface Props { interface Props {
/** /** Font info */
* Font info
*/
font: UnifiedFont; font: UnifiedFont;
/** /** Editable sample text */
* Text to display
*/
text: string; text: string;
/** /** Position index — drives the staggered entrance delay */
* Index of the font sampler
*/
index?: number; index?: number;
/**
* Font settings
*/
fontSize?: number;
lineHeight?: number;
letterSpacing?: number;
} }
let { font, text = $bindable(), index = 0, ...restProps }: Props = $props(); let { font, text = $bindable(), index = 0 }: Props = $props();
const fontWeight = $derived(controlManager.weight); const fontWeight = $derived(controlManager.weight);
const fontSize = $derived(controlManager.renderedSize); const fontSize = $derived(controlManager.renderedSize);
const lineHeight = $derived(controlManager.height); const lineHeight = $derived(controlManager.height);
const letterSpacing = $derived(controlManager.spacing); const letterSpacing = $derived(controlManager.spacing);
// Adjust the property name to match your UnifiedFont type
const fontType = $derived((font as any).type ?? (font as any).category ?? '');
const stats = $derived([
{ label: 'SZ', value: `${fontSize}PX` },
{ label: 'WGT', value: `${fontWeight}` },
{ label: 'LH', value: lineHeight?.toFixed(2) },
{ label: 'LTR', value: `${letterSpacing}` },
]);
</script> </script>
<div <div
in:fly={{ y: 20, duration: 400, delay: index * 50 }}
class=" class="
w-full h-full rounded-xl sm:rounded-2xl group relative
bg-paper dark:bg-dark-card
border border-black/5 dark:border-white/10
hover:border-brand dark:hover:border-brand
hover:shadow-[5px_5px_0px_0px_theme('colors.brand')]
transition-all duration-200
overflow-hidden
flex flex-col flex flex-col
bg-background-80 min-h-60
border border-border-muted rounded-none
shadow-[0_1px_3px_rgba(0,0,0,0.04)]
relative overflow-hidden
" "
style:font-weight={fontWeight} style:font-weight={fontWeight}
> >
<div class="px-4 sm:px-5 md:px-6 py-2.5 sm:py-3 border-b border-border-subtle flex items-center justify-between"> <!-- ── Header bar ─────────────────────────────────────────────────── -->
<div class="flex items-center gap-2 sm:gap-2.5"> <div
<Footnote> class="
typeface_{String(index).padStart(3, '0')} flex items-center justify-between
</Footnote> px-4 sm:px-5 md:px-6 py-3 sm:py-4
<div class="w-px h-2 sm:h-2.5 bg-border-subtle"></div> border-b border-black/5 dark:border-white/10
<div class="font-bold text-foreground"> bg-paper dark:bg-dark-card
{font.name} "
</div>
</div>
<!--
<IconButton
onclick={removeSample}
class="w-5 h-5 rounded-full hover:bg-transparent flex items-center justify-center transition-colors group translate-x-1/2 cursor-pointer"
> >
{#snippet icon({ className })} <!-- Left: index · name · type badge -->
<XIcon class={className} /> <div class="flex items-center gap-2 sm:gap-4 min-w-0">
{/snippet} <span class="font-mono text-[0.625rem] tracking-widest text-neutral-400 uppercase leading-none shrink-0">
</IconButton> {String(index + 1).padStart(2, '0')}
--> </span>
<div class="w-px h-3 bg-black/10 dark:bg-white/10 shrink-0"></div>
<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}
<span
class="
hidden sm:inline-block shrink-0
px-2 py-0.5
border border-black/10 dark:border-white/10 rounded-full
text-[0.5rem] font-secondary font-medium uppercase
text-neutral-500 tracking-wide leading-none
"
>
{fontType}
</span>
{/if}
</div> </div>
<div class="p-4 sm:p-5 md:p-8 relative z-10"> <!-- Right: stats, hidden on mobile, fade in on group hover -->
<div class="hidden md:flex items-center gap-3 lg:gap-4 opacity-50 group-hover:opacity-100 transition-opacity duration-200 shrink-0 ml-4">
{#each stats as stat, i}
<div class="flex items-center gap-1">
<span class="font-mono text-[0.5rem] tracking-widest text-neutral-400 uppercase">
{stat.label}:
</span>
<span
class="font-mono text-[0.5rem] font-bold tracking-widest text-swiss-black dark:text-neutral-200 uppercase"
>
{stat.value}
</span>
</div>
{#if i < stats.length - 1}
<div class="w-px h-2 bg-black/10 dark:bg-white/10"></div>
{/if}
{/each}
</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={fontWeight}> <FontApplicator {font} weight={fontWeight}>
<ContentEditable <ContentEditable
bind:text bind:text
{...restProps}
{fontSize} {fontSize}
{lineHeight} {lineHeight}
{letterSpacing} {letterSpacing}
@@ -90,21 +128,26 @@ const letterSpacing = $derived(controlManager.spacing);
</FontApplicator> </FontApplicator>
</div> </div>
<div class="px-4 sm:px-5 md:px-6 py-1.5 sm:py-2 border-t border-border-subtle w-full flex flex-row gap-2 sm:gap-4 bg-background mt-auto"> <!-- ── Mobile stats footer (md:hidden — header stats take over above) -->
<Footnote class="text-[7px] sm:text-[8px] tracking-wider ml-auto"> <div class="md:hidden px-4 sm:px-5 py-1.5 sm:py-2 border-t border-black/5 dark:border-white/10 flex gap-2 sm:gap-4 bg-paper dark:bg-dark-card mt-auto">
SZ:{fontSize}PX {#each stats as stat, i}
</Footnote> <Footnote class="text-[0.4375rem] sm:text-[0.5rem] tracking-wider {i === 0 ? 'ml-auto' : ''}">
<div class="w-px h-2 sm:h-2.5 self-center bg-border-subtle hidden sm:block"></div> {stat.label}:{stat.value}
<Footnote class="text-[7px] sm:text-[8px] tracking-wider">
WGT:{fontWeight}
</Footnote>
<div class="w-px h-2 sm:h-2.5 self-center bg-border-subtle hidden sm:block"></div>
<Footnote class="text-[7px] sm:text-[8px] tracking-wider">
LH:{lineHeight?.toFixed(2)}
</Footnote>
<div class="w-px h-2 sm:h-2.5 self-center bg-border-subtle hidden sm:block"></div>
<Footnote class="text-[0.4375rem] sm:text-[0.5rem] tracking-wider">
LTR:{letterSpacing}
</Footnote> </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
w-full h-0.5 bg-brand
scale-x-0 group-hover:scale-x-100
transition-transform origin-left duration-300
"
>
</div> </div>
</div> </div>