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
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:
95
apps/web/src/components/ComponentShareButton.tsx
Normal file
95
apps/web/src/components/ComponentShareButton.tsx
Normal 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}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user