feat(saju-ui-v2): OrnamentBloom + TopRibbon + OrnateFrame + TitleBlock
This commit is contained in:
13
src/pages/saju/_shell/OrnamentBloom.jsx
Normal file
13
src/pages/saju/_shell/OrnamentBloom.jsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default function OrnamentBloom({ size = 18, color = '#D4AF37' }) {
|
||||||
|
return (
|
||||||
|
<svg width={size} height={size} viewBox="0 0 18 18" fill="none">
|
||||||
|
<circle cx="9" cy="9" r="2.4" fill={color} />
|
||||||
|
{[0, 60, 120, 180, 240, 300].map((angle) => (
|
||||||
|
<ellipse key={angle} cx="9" cy="4" rx="1.6" ry="3" fill={color} opacity="0.7"
|
||||||
|
transform={`rotate(${angle} 9 9)`} />
|
||||||
|
))}
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
36
src/pages/saju/_shell/OrnateFrame.jsx
Normal file
36
src/pages/saju/_shell/OrnateFrame.jsx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import hexA from './helpers/hexA';
|
||||||
|
|
||||||
|
export default function OrnateFrame({
|
||||||
|
children, color = '#D4AF37', bg = 'transparent', radius = 14, padding = '20px',
|
||||||
|
style = {}, double = false,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
position: 'relative', borderRadius: radius,
|
||||||
|
background: bg, padding,
|
||||||
|
border: `1px solid ${hexA(color, 0.45)}`,
|
||||||
|
boxShadow: 'var(--shadow-card)',
|
||||||
|
...style,
|
||||||
|
}}>
|
||||||
|
{double && (
|
||||||
|
<div style={{
|
||||||
|
position: 'absolute', inset: 4, borderRadius: radius - 4,
|
||||||
|
border: `1px solid ${hexA(color, 0.3)}`, pointerEvents: 'none',
|
||||||
|
}} />
|
||||||
|
)}
|
||||||
|
{[[0,0,0],[0,1,90],[1,1,180],[1,0,270]].map(([x,y,r], i) => (
|
||||||
|
<svg key={i} width="12" height="12" viewBox="0 0 12 12" style={{
|
||||||
|
position: 'absolute',
|
||||||
|
[x ? 'right' : 'left']: 6,
|
||||||
|
[y ? 'bottom' : 'top']: 6,
|
||||||
|
transform: `rotate(${r}deg)`,
|
||||||
|
pointerEvents: 'none',
|
||||||
|
}}>
|
||||||
|
<path d="M0 4 L0 0 L4 0" stroke={color} strokeWidth="1.2" fill="none" strokeLinecap="round" />
|
||||||
|
</svg>
|
||||||
|
))}
|
||||||
|
<div style={{ position: 'relative', zIndex: 1 }}>{children}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
37
src/pages/saju/_shell/TitleBlock.jsx
Normal file
37
src/pages/saju/_shell/TitleBlock.jsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import OrnamentBloom from './OrnamentBloom';
|
||||||
|
|
||||||
|
export default function TitleBlock({
|
||||||
|
title, subtitle, color = '#1F2A44', subColor = '#6B6B6B',
|
||||||
|
center = true, withBloom = true, gold = '#D4AF37',
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div style={{ textAlign: center ? 'center' : 'left' }}>
|
||||||
|
{withBloom && center && (
|
||||||
|
<div style={{
|
||||||
|
display: 'flex', justifyContent: 'center', gap: 12,
|
||||||
|
alignItems: 'center', marginBottom: 10, color: gold,
|
||||||
|
}}>
|
||||||
|
<svg width="40" height="6" viewBox="0 0 40 6">
|
||||||
|
<path d="M0 3 L36 3" stroke={gold} strokeWidth="1" />
|
||||||
|
<circle cx="38" cy="3" r="1.5" fill={gold} />
|
||||||
|
</svg>
|
||||||
|
<OrnamentBloom size={18} color={gold} />
|
||||||
|
<svg width="40" height="6" viewBox="0 0 40 6">
|
||||||
|
<circle cx="2" cy="3" r="1.5" fill={gold} />
|
||||||
|
<path d="M4 3 L40 3" stroke={gold} strokeWidth="1" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<h1 className="font-title" style={{
|
||||||
|
margin: 0, fontSize: 30, color, letterSpacing: '-0.02em',
|
||||||
|
}}>{title}</h1>
|
||||||
|
{subtitle && (
|
||||||
|
<div style={{
|
||||||
|
marginTop: 6, fontSize: 13, color: subColor, lineHeight: 1.55,
|
||||||
|
letterSpacing: '-0.01em',
|
||||||
|
}}>{subtitle}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
21
src/pages/saju/_shell/TopRibbon.jsx
Normal file
21
src/pages/saju/_shell/TopRibbon.jsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function CloudOrnament({ width = 90, color = '#D4AF37', opacity = 0.85 }) {
|
||||||
|
return (
|
||||||
|
<svg width={width} height={width / 3.5} viewBox="0 0 90 26" fill="none" opacity={opacity}>
|
||||||
|
<path d="M5 18 Q12 6 24 12 Q36 4 48 14 Q60 6 72 14 Q82 8 88 18"
|
||||||
|
stroke={color} strokeWidth="1" fill="none" />
|
||||||
|
<circle cx="24" cy="12" r="1.4" fill={color} />
|
||||||
|
<circle cx="48" cy="14" r="1.4" fill={color} />
|
||||||
|
<circle cx="72" cy="14" r="1.4" fill={color} />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function TopRibbon({ color = '#D4AF37', opacity = 0.5 }) {
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'center', padding: '4px 0 0', opacity }}>
|
||||||
|
<CloudOrnament width={90} color={color} opacity={0.85} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user