mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-10 07:27:23 +02:00
feat(RichText): render gallery as 3-column grid for compact display
- Add new 'grid' preset to MediaEmbed with square aspect ratio and object-cover - Update Gallery component to use CSS grid (3 cols) for images/videos - Separate audio items into stacked layout below the grid - Grid adapts to container width for varied event card sizes
This commit is contained in:
@@ -13,7 +13,7 @@ interface MediaEmbedProps {
|
||||
url: string;
|
||||
type?: "image" | "video" | "audio" | "auto";
|
||||
alt?: string;
|
||||
preset?: "inline" | "thumbnail" | "preview" | "banner";
|
||||
preset?: "inline" | "thumbnail" | "grid" | "preview" | "banner";
|
||||
className?: string;
|
||||
|
||||
// Image-specific
|
||||
@@ -42,6 +42,12 @@ const PRESETS = {
|
||||
maxWidthClass: "max-w-[120px]",
|
||||
roundedClass: "rounded-md",
|
||||
},
|
||||
grid: {
|
||||
maxHeightClass: "",
|
||||
maxWidthClass: "w-full",
|
||||
roundedClass: "rounded-md",
|
||||
objectFit: "cover" as const,
|
||||
},
|
||||
preview: {
|
||||
maxHeightClass: "max-h-[500px]",
|
||||
maxWidthClass: "max-w-full",
|
||||
@@ -61,7 +67,7 @@ const getDefaultAspectRatio = (
|
||||
mediaType: string,
|
||||
preset: string,
|
||||
): string | undefined => {
|
||||
if (preset === "thumbnail") return "1/1";
|
||||
if (preset === "thumbnail" || preset === "grid") return "1/1";
|
||||
if (mediaType === "video") return "16/9";
|
||||
return undefined; // auto for images
|
||||
};
|
||||
@@ -256,7 +262,8 @@ export function MediaEmbed({
|
||||
alt={alt || "Image"}
|
||||
loading="lazy"
|
||||
className={cn(
|
||||
"w-full h-full object-contain",
|
||||
"w-full h-full",
|
||||
preset === "grid" ? "object-cover" : "object-contain",
|
||||
presetStyles.roundedClass,
|
||||
enableZoom && "cursor-zoom-in",
|
||||
fadeIn && "transition-opacity duration-300",
|
||||
|
||||
@@ -36,13 +36,13 @@ export function Gallery({ node }: GalleryNodeProps) {
|
||||
|
||||
if (isImageURL(url)) {
|
||||
if (shouldShowMedia && options.showImages) {
|
||||
return <MediaEmbed url={url} type="image" preset="inline" enableZoom />;
|
||||
return <MediaEmbed url={url} type="image" preset="grid" enableZoom />;
|
||||
}
|
||||
return <MediaPlaceholder type="image" />;
|
||||
}
|
||||
if (isVideoURL(url)) {
|
||||
if (shouldShowMedia && options.showVideos) {
|
||||
return <MediaEmbed url={url} type="video" preset="inline" />;
|
||||
return <MediaEmbed url={url} type="video" preset="grid" />;
|
||||
}
|
||||
return <MediaPlaceholder type="video" />;
|
||||
}
|
||||
@@ -65,13 +65,28 @@ export function Gallery({ node }: GalleryNodeProps) {
|
||||
// Only show dialog for audio files
|
||||
const audioLinks = links.filter((url) => isAudioURL(url));
|
||||
|
||||
// Separate media types for layout
|
||||
const imageLinks = links.filter((url) => isImageURL(url) || isVideoURL(url));
|
||||
const audioOnlyLinks = links.filter((url) => isAudioURL(url));
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="my-2 flex flex-wrap gap-2">
|
||||
{links.map((url: string, i: number) => (
|
||||
<div key={i}>{renderLink(url, i)}</div>
|
||||
))}
|
||||
</div>
|
||||
{/* Grid layout for images/videos */}
|
||||
{imageLinks.length > 0 && (
|
||||
<div className="my-2 grid grid-cols-3 gap-1.5">
|
||||
{imageLinks.map((url: string, i: number) => (
|
||||
<div key={`${url}-${i}`}>{renderLink(url, links.indexOf(url))}</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{/* Stack layout for audio */}
|
||||
{audioOnlyLinks.length > 0 && (
|
||||
<div className="my-2 flex flex-col gap-2">
|
||||
{audioOnlyLinks.map((url: string, i: number) => (
|
||||
<div key={`${url}-${i}`}>{renderLink(url, links.indexOf(url))}</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{audioLinks.length > 0 && (
|
||||
<MediaDialog
|
||||
open={dialogOpen}
|
||||
|
||||
Reference in New Issue
Block a user