teqo-backup/src/components/cards/CardNameTileCanvas.tsx
Francisco Solla 8e0366d3ba feat(S14): cards personalizados — ajustes de fidelidade e experiência
A home abre o mesmo compositor do /cards ali mesmo (dialog no desktop,
drawer no mobile) e perde o CTA: os três modelos viram botões e nada
navega. A seção sobe para imediatamente antes da captura de novidades.
O nome no card e no tile alinha à esquerda com o SOU (x=213) via leftX
+ drawCardName extraído; o tile passa a desenhar SEU NOME com o próprio
pipeline real (canvas transparente sobre a base). Modal sem o vão
interno (p-0 sm:p-0) e ajuda legível no tema campaign-site
(--muted-foreground). Remove a variante link e o cardModelHref sem call
site. Unit atualizado (+ drawCardName) e e2e com alinhamento por pixel;
sem migration, sem PII em rede.
2026-09-12 21:01:15 -03:00

58 lines
1.6 KiB
TypeScript

'use client'
import { useEffect, useRef } from 'react'
import { ensureCardFont } from '@/components/cards/cardCanvas'
import { CARD_NAME_TILE_PLACEHOLDER } from '@/components/cards/cardCopy'
import type { CardModel } from '@/lib/cardModels'
import { fitCardName } from '@/lib/cardNameFit'
import { createCardMeasure, drawCardName } from '@/lib/cardRender'
/**
* S14 — the name-model tile preview: a transparent canvas over the base image
* that draws `SEU NOME` with the exact composer pipeline (same fit, same slot,
* same left border), so the tile cannot drift from the generated card. Draws
* only after the real font loads — measuring with the fallback would lie.
*/
export const CardNameTileCanvas = ({
model,
fontFamily,
}: {
model: CardModel
fontFamily: string
}) => {
const canvasRef = useRef<HTMLCanvasElement>(null)
useEffect(() => {
let cancelled = false
const draw = async () => {
const fontReady = await ensureCardFont(fontFamily)
if (!fontReady || cancelled) return
const ctx = canvasRef.current?.getContext('2d')
if (!ctx) return
const fit = fitCardName(CARD_NAME_TILE_PLACEHOLDER, createCardMeasure(ctx, fontFamily))
if (!fit.ok) return
ctx.clearRect(0, 0, model.width, model.height)
drawCardName(ctx, { fit, fontFamily })
}
void draw()
return () => {
cancelled = true
}
}, [model.width, model.height, fontFamily])
return (
<canvas
ref={canvasRef}
width={model.width}
height={model.height}
aria-hidden="true"
className="absolute inset-0 h-full w-full"
/>
)
}