chore: overhaul infrastructure and integrate @mintel packages
Some checks failed
🧪 CI (QA) / 🧪 Quality Assurance (push) Failing after 1m3s
Some checks failed
🧪 CI (QA) / 🧪 Quality Assurance (push) Failing after 1m3s
- Restructure to pnpm monorepo (site moved to apps/web) - Integrate @mintel/tsconfig, @mintel/eslint-config, @mintel/husky-config - Implement Docker service architecture (Varnish, Directus, Gatekeeper) - Setup environment-aware Gitea Actions deployment
This commit is contained in:
98
apps/web/src/components/GenericEmbed.tsx
Normal file
98
apps/web/src/components/GenericEmbed.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
import React from 'react';
|
||||
|
||||
interface GenericEmbedProps {
|
||||
url: string;
|
||||
className?: string;
|
||||
maxWidth?: string;
|
||||
type?: 'video' | 'article' | 'rich';
|
||||
}
|
||||
|
||||
export const GenericEmbed: React.FC<GenericEmbedProps> = ({
|
||||
url,
|
||||
className = "",
|
||||
maxWidth = "100%",
|
||||
type = 'rich'
|
||||
}) => {
|
||||
let embedUrl: string | null = null;
|
||||
let provider = 'unknown';
|
||||
|
||||
try {
|
||||
const urlObj = new URL(url);
|
||||
const hostname = urlObj.hostname.replace('www.', '');
|
||||
|
||||
if (hostname.includes('youtube.com') || hostname.includes('youtu.be')) {
|
||||
const videoId = urlObj.searchParams.get('v') || urlObj.pathname.split('/').pop();
|
||||
if (videoId) {
|
||||
embedUrl = `https://www.youtube.com/embed/${videoId}`;
|
||||
provider = 'youtube.com';
|
||||
}
|
||||
}
|
||||
else if (hostname.includes('vimeo.com')) {
|
||||
const videoId = urlObj.pathname.split('/').filter(Boolean)[0];
|
||||
if (videoId) {
|
||||
embedUrl = `https://player.vimeo.com/video/${videoId}`;
|
||||
provider = 'vimeo.com';
|
||||
}
|
||||
}
|
||||
else if (hostname.includes('codepen.io')) {
|
||||
const penPath = urlObj.pathname.replace('/pen/', '/');
|
||||
embedUrl = `https://codepen.io${penPath}?default-tab=html,result`;
|
||||
provider = 'codepen.io';
|
||||
}
|
||||
else if (hostname.includes('gist.github.com')) {
|
||||
const gistPath = urlObj.pathname;
|
||||
embedUrl = `https://gist.github.com${gistPath}.js`;
|
||||
provider = 'gist.github.com';
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('GenericEmbed: Failed to parse URL', e);
|
||||
}
|
||||
|
||||
const hasEmbed = embedUrl !== null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`generic-embed not-prose ${className}`}
|
||||
data-provider={provider}
|
||||
data-type={type}
|
||||
style={{ '--max-width': maxWidth } as React.CSSProperties}
|
||||
>
|
||||
{hasEmbed ? (
|
||||
<div className="embed-wrapper">
|
||||
{type === 'video' ? (
|
||||
<iframe
|
||||
src={embedUrl!}
|
||||
width="100%"
|
||||
height="100%"
|
||||
style={{ border: 'none', width: '100%', height: '100%' }}
|
||||
allowFullScreen
|
||||
loading="lazy"
|
||||
title={`${provider} embed`}
|
||||
/>
|
||||
) : (
|
||||
<iframe
|
||||
src={embedUrl!}
|
||||
width="100%"
|
||||
height="400"
|
||||
style={{ border: 'none' }}
|
||||
loading="lazy"
|
||||
title={`${provider} embed`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="embed-fallback">
|
||||
<div className="fallback-content">
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<span>Unable to embed this URL</span>
|
||||
<a href={url} target="_blank" rel="noopener noreferrer" className="fallback-link">
|
||||
Open link →
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user