ismail c5f76b3855
Some checks are pending
Build and Push Docker Image / build (push) Waiting to run
updates
2026-05-11 14:45:30 +03:00

206 lines
5.1 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* DesignCanvas — 变体并排网格布局
*
* 用于展示2+个静态设计variations让用户对比选择。
* 每个variation有label可hover放大。
*
* 用法:
* <DesignCanvas
* title="Hero区设计探索"
* subtitle="3个方向对比"
* columns={3}
* >
* <Variation label="Minimal" description="极简克制版">
* <div>...你的设计1...</div>
* </Variation>
* <Variation label="Editorial" description="杂志编辑风">
* <div>...你的设计2...</div>
* </Variation>
* <Variation label="Brutalist" description="粗粝原始">
* <div>...你的设计3...</div>
* </Variation>
* </DesignCanvas>
*
* 配合React+Babel使用。放在合适的script里然后window.DesignCanvas/window.Variation可用。
*/
const canvasStyles = {
container: {
minHeight: '100vh',
background: '#F5F5F0',
padding: '40px 60px',
fontFamily: '-apple-system, "SF Pro Text", "PingFang SC", sans-serif',
},
header: {
marginBottom: 48,
maxWidth: 900,
},
title: {
fontSize: 36,
fontWeight: 600,
marginBottom: 12,
color: '#1A1A1A',
letterSpacing: '-0.02em',
},
subtitle: {
fontSize: 16,
color: '#666',
lineHeight: 1.5,
},
grid: {
display: 'grid',
gap: 32,
},
cell: {
display: 'flex',
flexDirection: 'column',
gap: 12,
},
cellHeader: {
display: 'flex',
alignItems: 'baseline',
gap: 12,
paddingBottom: 8,
borderBottom: '1px solid #E0E0DA',
},
label: {
fontSize: 14,
fontWeight: 600,
color: '#1A1A1A',
letterSpacing: '-0.01em',
},
description: {
fontSize: 13,
color: '#888',
},
frame: {
background: '#fff',
borderRadius: 4,
border: '1px solid #E0E0DA',
overflow: 'hidden',
position: 'relative',
transition: 'transform 0.2s ease, box-shadow 0.2s ease',
cursor: 'pointer',
},
frameInner: {
position: 'relative',
width: '100%',
},
badge: {
position: 'absolute',
top: 12,
left: 12,
background: 'rgba(0, 0, 0, 0.7)',
color: '#fff',
padding: '3px 8px',
borderRadius: 4,
fontSize: 11,
fontWeight: 500,
letterSpacing: '0.5px',
textTransform: 'uppercase',
zIndex: 10,
pointerEvents: 'none',
},
};
function DesignCanvas({ title, subtitle, columns = 3, children }) {
const [expanded, setExpanded] = React.useState(null);
const gridStyle = {
...canvasStyles.grid,
gridTemplateColumns: `repeat(${columns}, 1fr)`,
};
return (
<div style={canvasStyles.container}>
{(title || subtitle) && (
<div style={canvasStyles.header}>
{title && <h1 style={canvasStyles.title}>{title}</h1>}
{subtitle && <p style={canvasStyles.subtitle}>{subtitle}</p>}
</div>
)}
<div style={gridStyle}>
{React.Children.map(children, (child, idx) =>
React.isValidElement(child)
? React.cloneElement(child, {
_index: idx,
_expanded: expanded === idx,
_onToggle: () => setExpanded(expanded === idx ? null : idx),
})
: child
)}
</div>
{expanded !== null && (
<div
onClick={() => setExpanded(null)}
style={{
position: 'fixed',
inset: 0,
background: 'rgba(0, 0, 0, 0.75)',
zIndex: 1000,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: 40,
cursor: 'zoom-out',
}}
>
<div
onClick={e => e.stopPropagation()}
style={{
background: '#fff',
borderRadius: 8,
overflow: 'hidden',
maxWidth: '90vw',
maxHeight: '90vh',
position: 'relative',
}}
>
{React.Children.toArray(children)[expanded]}
</div>
</div>
)}
</div>
);
}
function Variation({ label, description, number, children, _index, _expanded, _onToggle, aspectRatio = '4 / 3' }) {
const displayNumber = number || String(_index + 1).padStart(2, '0');
return (
<div style={canvasStyles.cell}>
<div style={canvasStyles.cellHeader}>
<span style={{ ...canvasStyles.label, color: '#999', fontFamily: 'ui-monospace, monospace', fontSize: 12 }}>
{displayNumber}
</span>
<span style={canvasStyles.label}>{label}</span>
{description && <span style={canvasStyles.description}> {description}</span>}
</div>
<div
onClick={_onToggle}
style={{
...canvasStyles.frame,
aspectRatio,
}}
onMouseEnter={e => {
e.currentTarget.style.boxShadow = '0 8px 24px rgba(0,0,0,0.08)';
}}
onMouseLeave={e => {
e.currentTarget.style.boxShadow = 'none';
}}
>
<div style={canvasStyles.frameInner}>
{children}
</div>
</div>
</div>
);
}
if (typeof window !== 'undefined') {
Object.assign(window, { DesignCanvas, Variation });
}