160 lines
4.9 KiB
TypeScript
160 lines
4.9 KiB
TypeScript
|
|
import React, { useEffect } from 'react';
|
||
|
|
import { useSiteDesign } from '../../state/SiteDesignContext';
|
||
|
|
|
||
|
|
interface HeadCodeModalProps {
|
||
|
|
open: boolean;
|
||
|
|
onClose: () => void;
|
||
|
|
}
|
||
|
|
|
||
|
|
export const HeadCodeModal: React.FC<HeadCodeModalProps> = ({ open, onClose }) => {
|
||
|
|
const { design, updateDesign } = useSiteDesign();
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (!open) return;
|
||
|
|
const handler = (e: KeyboardEvent) => {
|
||
|
|
if (e.key === 'Escape') onClose();
|
||
|
|
};
|
||
|
|
window.addEventListener('keydown', handler);
|
||
|
|
return () => window.removeEventListener('keydown', handler);
|
||
|
|
}, [open, onClose]);
|
||
|
|
|
||
|
|
if (!open) return null;
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div style={backdropStyle} onClick={onClose}>
|
||
|
|
<div style={modalStyle} onClick={(e) => e.stopPropagation()}>
|
||
|
|
{/* Header */}
|
||
|
|
<div style={modalHeaderStyle}>
|
||
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||
|
|
<i className="fa fa-code" style={{ color: 'var(--color-accent)', fontSize: 16 }} />
|
||
|
|
<div>
|
||
|
|
<div style={{ fontSize: 15, fontWeight: 600, color: 'var(--color-text)' }}>
|
||
|
|
Custom Head Code
|
||
|
|
</div>
|
||
|
|
<div style={{ fontSize: 11, color: 'var(--color-text-muted)', marginTop: 2 }}>
|
||
|
|
Add tracking scripts, meta tags, or custom CSS to your site's <head> section.
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<button onClick={onClose} style={closeButtonStyle}>
|
||
|
|
<i className="fa fa-times" />
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Body */}
|
||
|
|
<div style={{ padding: 20, flex: 1, display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||
|
|
<div style={{
|
||
|
|
padding: '10px 14px',
|
||
|
|
background: 'rgba(59,130,246,0.08)',
|
||
|
|
border: '1px solid rgba(59,130,246,0.2)',
|
||
|
|
borderRadius: 6,
|
||
|
|
fontSize: 12,
|
||
|
|
color: 'var(--color-text-muted)',
|
||
|
|
lineHeight: 1.5,
|
||
|
|
}}>
|
||
|
|
<i className="fa fa-info-circle" style={{ color: 'var(--color-accent)', marginRight: 6 }} />
|
||
|
|
Code added here will be injected into the <code style={{ background: 'rgba(255,255,255,0.08)', padding: '1px 4px', borderRadius: 3, fontSize: 11 }}><head></code> of every page on your site. Use it for analytics, custom fonts, or global CSS.
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<textarea
|
||
|
|
value={design.headCode || ''}
|
||
|
|
onChange={(e) => updateDesign({ headCode: e.target.value })}
|
||
|
|
placeholder={"<!-- Google Analytics -->\n<script async src=\"https://...\"></script>\n\n<!-- Custom Fonts -->\n<link href=\"https://fonts.googleapis.com/...\" rel=\"stylesheet\">\n\n<style>\n /* Global CSS overrides */\n body { }\n</style>"}
|
||
|
|
style={{
|
||
|
|
flex: 1,
|
||
|
|
minHeight: 300,
|
||
|
|
padding: 14,
|
||
|
|
background: '#0d0d0f',
|
||
|
|
color: '#e4e4e7',
|
||
|
|
border: '1px solid #3f3f46',
|
||
|
|
borderRadius: 8,
|
||
|
|
fontFamily: 'Source Code Pro, Consolas, monospace',
|
||
|
|
fontSize: 13,
|
||
|
|
lineHeight: 1.6,
|
||
|
|
resize: 'vertical',
|
||
|
|
outline: 'none',
|
||
|
|
tabSize: 2,
|
||
|
|
}}
|
||
|
|
spellCheck={false}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Footer */}
|
||
|
|
<div style={{
|
||
|
|
padding: '12px 20px',
|
||
|
|
borderTop: '1px solid var(--color-border)',
|
||
|
|
display: 'flex',
|
||
|
|
justifyContent: 'flex-end',
|
||
|
|
gap: 8,
|
||
|
|
}}>
|
||
|
|
<button onClick={onClose} style={doneButtonStyle}>
|
||
|
|
Done
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
/* ---------- Styles ---------- */
|
||
|
|
|
||
|
|
const backdropStyle: React.CSSProperties = {
|
||
|
|
position: 'fixed',
|
||
|
|
top: 0,
|
||
|
|
left: 0,
|
||
|
|
right: 0,
|
||
|
|
bottom: 0,
|
||
|
|
backgroundColor: 'rgba(0, 0, 0, 0.65)',
|
||
|
|
display: 'flex',
|
||
|
|
alignItems: 'center',
|
||
|
|
justifyContent: 'center',
|
||
|
|
zIndex: 10000,
|
||
|
|
};
|
||
|
|
|
||
|
|
const modalStyle: React.CSSProperties = {
|
||
|
|
width: '90vw',
|
||
|
|
maxWidth: 700,
|
||
|
|
maxHeight: '80vh',
|
||
|
|
backgroundColor: 'var(--color-bg-surface)',
|
||
|
|
borderRadius: 12,
|
||
|
|
border: '1px solid var(--color-border)',
|
||
|
|
display: 'flex',
|
||
|
|
flexDirection: 'column',
|
||
|
|
overflow: 'hidden',
|
||
|
|
boxShadow: '0 20px 60px rgba(0,0,0,0.5)',
|
||
|
|
};
|
||
|
|
|
||
|
|
const modalHeaderStyle: React.CSSProperties = {
|
||
|
|
display: 'flex',
|
||
|
|
alignItems: 'center',
|
||
|
|
justifyContent: 'space-between',
|
||
|
|
padding: '16px 20px',
|
||
|
|
borderBottom: '1px solid var(--color-border)',
|
||
|
|
flexShrink: 0,
|
||
|
|
};
|
||
|
|
|
||
|
|
const closeButtonStyle: React.CSSProperties = {
|
||
|
|
width: 32,
|
||
|
|
height: 32,
|
||
|
|
display: 'inline-flex',
|
||
|
|
alignItems: 'center',
|
||
|
|
justifyContent: 'center',
|
||
|
|
background: 'none',
|
||
|
|
border: '1px solid var(--color-border)',
|
||
|
|
borderRadius: 6,
|
||
|
|
color: 'var(--color-text-muted)',
|
||
|
|
cursor: 'pointer',
|
||
|
|
fontSize: 14,
|
||
|
|
};
|
||
|
|
|
||
|
|
const doneButtonStyle: React.CSSProperties = {
|
||
|
|
padding: '8px 24px',
|
||
|
|
fontSize: 13,
|
||
|
|
fontWeight: 600,
|
||
|
|
background: 'var(--color-accent)',
|
||
|
|
color: '#fff',
|
||
|
|
border: 'none',
|
||
|
|
borderRadius: 6,
|
||
|
|
cursor: 'pointer',
|
||
|
|
};
|