fix(blog): optimize component share logic, typography, and modal layouts
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 5s
Build & Deploy / 🏗️ Build (push) Failing after 14s
Build & Deploy / 🧪 QA (push) Failing after 1m48s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🩺 Health Check (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s

This commit is contained in:
2026-02-22 11:41:28 +01:00
parent 75c61f1436
commit b15c8408ff
103 changed files with 4366 additions and 2293 deletions

View File

@@ -0,0 +1,95 @@
"use client";
import React, { useState } from "react";
import { Share2 } from "lucide-react";
import { ShareModal } from "./ShareModal";
import { useAnalytics } from "./analytics/useAnalytics";
import * as htmlToImage from "html-to-image";
interface ComponentShareButtonProps {
targetId: string;
title?: string;
className?: string;
}
export const ComponentShareButton: React.FC<ComponentShareButtonProps> = ({
targetId,
title = "Component",
className = ""
}) => {
const [isModalOpen, setIsModalOpen] = useState(false);
const [generatedImage, setGeneratedImage] = useState<string | undefined>();
const [isCapturing, setIsCapturing] = useState(false);
const { trackEvent } = useAnalytics();
const currentUrl =
typeof window !== "undefined"
? `${window.location.origin}${window.location.pathname}#${targetId}`
: "";
const handleOpenModal = async () => {
setIsCapturing(true);
try {
const element = document.getElementById(targetId);
if (element) {
// Find existing share buttons and temporarily hide them during capture to avoid infinite recursion loops in screenshots
const shareButtons = element.querySelectorAll('[data-share-button="true"]');
shareButtons.forEach(btn => (btn as HTMLElement).style.opacity = '0');
const dataUrl = await htmlToImage.toPng(element, {
quality: 1,
type: 'image/png',
pixelRatio: 2,
backgroundColor: '#ffffff',
skipFonts: true
});
// Restore buttons
shareButtons.forEach(btn => (btn as HTMLElement).style.opacity = '1');
setGeneratedImage(dataUrl);
}
} catch (err) {
console.error("Failed to capture component:", err);
} finally {
setIsCapturing(false);
setIsModalOpen(true);
trackEvent("component_share_opened", {
component_id: targetId,
component_title: title,
});
}
};
return (
<>
<button
onClick={handleOpenModal}
disabled={isCapturing}
data-share-button="true"
className={`inline-flex z-20 items-center justify-center gap-2 px-3 py-1.5 bg-white border border-slate-200 rounded-sm text-slate-500 hover:text-slate-900 hover:bg-slate-50 hover:border-slate-400 transition-all text-[10px] font-mono uppercase tracking-widest ${className}`}
aria-label="Component als Grafik teilen"
>
<Share2 strokeWidth={2.5} className={`w-3 h-3 ${isCapturing ? 'animate-spin' : ''}`} />
<span>{isCapturing ? "Erstelle Bild..." : "Teilen"}</span>
</button>
{/* ShareModal expects a direct image string in 'qrCodeData' or 'diagramImage' (except diagramImage specifically assumes SVGs).
Because ShareModal has logic that redraws diagramImage on a canvas assuming it's an SVG string,
we must bypass the SVG renderer. However, if we look at ShareModal, we need a way to pass a raw PNG.
Passing it as qrCodeData is a hack, or we can just send it via diagramImage and hope the canvas ignores it if it's already a Data URL.
Wait: ShareModal expects `diagramImage` (svg string) AND re-renders it.
Let's just pass our Data URL into a NEW prop or hijack the qrCodeData if necessary, but actually ShareModal only allows `diagramImage` as SVG logic right now.
Let's see if ShareModal needs an update to accept pure images, we'll check it. for now, let's pass it via diagramImage and see if we can adapt ShareModal. */}
{/* We will adapt ShareModal to handle both SVG strings & base64 PNG inputs via `diagramImage` */}
<ShareModal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
url={currentUrl}
title={title}
diagramImage={generatedImage}
/>
</>
);
};