Resolve errors with migrated components
This commit is contained in:
@@ -1,64 +1,148 @@
|
||||
"use client";
|
||||
|
||||
import { memo } from "react";
|
||||
import Image from "next/image";
|
||||
import QuoteDecor from "../QuoteDecor";
|
||||
import type { QuoteBlockViewProps } from "./QuoteBlock.types";
|
||||
|
||||
function QuoteBlockView({
|
||||
quoteId,
|
||||
className,
|
||||
quote,
|
||||
author,
|
||||
authorRole,
|
||||
authorImage,
|
||||
variant,
|
||||
className,
|
||||
source,
|
||||
quoteId,
|
||||
authorId,
|
||||
config,
|
||||
imageError,
|
||||
imageLoading,
|
||||
containerClasses,
|
||||
quoteClasses,
|
||||
authorClasses,
|
||||
authorRoleClasses,
|
||||
imageContainerClasses,
|
||||
currentAvatarSrc,
|
||||
onImageLoad,
|
||||
onImageError,
|
||||
}: QuoteBlockViewProps) {
|
||||
return (
|
||||
<blockquote
|
||||
id={quoteId}
|
||||
className={`${containerClasses} ${className}`}
|
||||
aria-label={author ? `Quote by ${author}` : "Quote"}
|
||||
<section
|
||||
className={`${config.container} ${className}`}
|
||||
aria-labelledby={quoteId}
|
||||
role="region"
|
||||
>
|
||||
<QuoteDecor variant={variant} />
|
||||
<div className="flex flex-col gap-[var(--spacing-scale-016)] md:gap-[var(--spacing-scale-024)]">
|
||||
<p className={quoteClasses}>{quote}</p>
|
||||
{(author || authorRole) && (
|
||||
<div className="flex items-center gap-[var(--spacing-scale-016)]">
|
||||
{authorImage && !imageError && (
|
||||
<div className={imageContainerClasses}>
|
||||
{imageLoading ? (
|
||||
<div className="w-full h-full bg-[var(--color-surface-default-secondary)] animate-pulse rounded-full" />
|
||||
) : (
|
||||
<Image
|
||||
src={authorImage}
|
||||
alt={author ? `${author}'s profile picture` : "Author"}
|
||||
width={48}
|
||||
height={48}
|
||||
className="rounded-full object-cover"
|
||||
onLoad={onImageLoad}
|
||||
onError={onImageError}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-col">
|
||||
{author && <cite className={authorClasses}>{author}</cite>}
|
||||
{authorRole && (
|
||||
<span className={authorRoleClasses}>{authorRole}</span>
|
||||
<div
|
||||
className={`${config.card} bg-[var(--color-surface-default-brand-darker-accent)] relative overflow-hidden`}
|
||||
>
|
||||
{/* Background with noise texture */}
|
||||
<div
|
||||
className="absolute inset-0 bg-[var(--color-surface-default-brand-darker-accent)]"
|
||||
style={{
|
||||
filter:
|
||||
'url(\'data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg"><defs><filter id="grain" filterUnits="objectBoundingBox" x="0" y="0" width="1" height="1" colorInterpolationFilters="sRGB"><feTurbulence type="fractalNoise" baseFrequency="0.4" numOctaves="3" seed="7" stitchTiles="stitch" result="noise"/><feColorMatrix in="noise" result="softNoise" type="matrix" values="0.8 0 0 0 0.3 0 0.6 0 0 0.2 0 0 1.0 0 0.4 0 0 0 0.25 0"/><feComposite in="softNoise" in2="SourceAlpha" operator="in" result="maskedNoise"/><feBlend in="SourceGraphic" in2="maskedNoise" mode="multiply"/></filter></defs></svg>#grain\')',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* DECORATIONS (behind content) */}
|
||||
{config.showDecor && (
|
||||
<QuoteDecor
|
||||
className="pointer-events-none absolute z-0
|
||||
left-0 top-0
|
||||
w-full h-full"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className={`flex flex-col ${config.gap} relative z-10`}>
|
||||
<div className={`flex flex-col ${config.avatarGap}`}>
|
||||
{/* Avatar with error handling */}
|
||||
<div className="relative">
|
||||
{!imageError ? (
|
||||
<Image
|
||||
src={currentAvatarSrc}
|
||||
alt={`Portrait of ${author}`}
|
||||
width={64}
|
||||
height={64}
|
||||
className={`filter sepia ${
|
||||
config.avatar
|
||||
} transition-opacity duration-300 ${
|
||||
imageLoading ? "opacity-0" : "opacity-100"
|
||||
}`}
|
||||
loading="lazy"
|
||||
onError={onImageError}
|
||||
onLoad={onImageLoad}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{/* Loading state */}
|
||||
{imageLoading && !imageError && (
|
||||
<div
|
||||
className={`absolute inset-0 bg-gray-200 animate-pulse rounded-full ${config.avatar}`}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Error state - show initials */}
|
||||
{imageError && (
|
||||
<div
|
||||
className={`flex items-center justify-center bg-gray-300 rounded-full ${config.avatar} text-gray-600 font-bold`}
|
||||
>
|
||||
<span className="text-sm md:text-base lg:text-lg xl:text-xl">
|
||||
{author
|
||||
.split(" ")
|
||||
.map((n) => n[0])
|
||||
.join("")
|
||||
.toUpperCase()}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<blockquote
|
||||
id={quoteId}
|
||||
aria-labelledby={authorId}
|
||||
className="relative"
|
||||
>
|
||||
<p
|
||||
data-qopen="“"
|
||||
data-qclose="”"
|
||||
className={[
|
||||
"font-bricolage-grotesque font-normal",
|
||||
config.quote,
|
||||
"text-[var(--color-content-inverse-primary)]",
|
||||
// give space for the hanging open-quote so it's not clipped:
|
||||
"pl-[0.6em] -indent-[0.6em]",
|
||||
// inject quotes
|
||||
"relative before:content-[attr(data-qopen)] after:content-[attr(data-qclose)]",
|
||||
// lock quote glyphs to your display face
|
||||
"before:[font-family:var(--font-bricolage-grotesque)]",
|
||||
"after:[font-family:var(--font-bricolage-grotesque)]",
|
||||
].join(" ")}
|
||||
>
|
||||
{quote}
|
||||
</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
)}
|
||||
<footer className="flex flex-col gap-[var(--spacing-scale-008)] md:gap-[var(--spacing-scale-012)] xl:gap-[var(--spacing-scale-020)]">
|
||||
<cite
|
||||
id={authorId}
|
||||
className={`font-inter font-normal ${config.author} text-[var(--color-content-inverse-primary)] uppercase not-italic`}
|
||||
>
|
||||
{author}
|
||||
</cite>
|
||||
{source && (
|
||||
<p
|
||||
data-qopen="“"
|
||||
data-qclose="”"
|
||||
className={[
|
||||
"font-inter font-normal",
|
||||
config.source,
|
||||
"text-[var(--color-content-inverse-primary)] uppercase",
|
||||
"pl-[0.6em] -indent-[0.6em]",
|
||||
"relative before:content-[attr(data-qopen)] after:content-[attr(data-qclose)]",
|
||||
"before:[font-family:var(--font-inter)] after:[font-family:var(--font-inter)]",
|
||||
].join(" ")}
|
||||
>
|
||||
{source}
|
||||
</p>
|
||||
)}
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</blockquote>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user