import React, { CSSProperties } from 'react'; import { useNode, UserComponent } from '@craftjs/core'; import { cssPropsToString } from '../../utils/style-helpers'; interface IconProps { icon?: string; size?: string; color?: string; bgColor?: string; bgShape?: 'none' | 'circle' | 'square' | 'rounded'; bgSize?: string; link?: string; style?: CSSProperties; } const COMMON_ICONS = [ 'fa-star', 'fa-heart', 'fa-check', 'fa-phone', 'fa-envelope', 'fa-map-marker', 'fa-globe', 'fa-facebook', 'fa-twitter', 'fa-instagram', 'fa-linkedin', 'fa-youtube', 'fa-github', 'fa-arrow-right', 'fa-arrow-down', 'fa-play', 'fa-search', 'fa-user', 'fa-lock', 'fa-cog', 'fa-home', 'fa-comment', 'fa-camera', 'fa-music', 'fa-shopping-cart', 'fa-calendar', 'fa-clock-o', 'fa-thumbs-up', 'fa-lightbulb-o', 'fa-rocket', ]; function getBgBorderRadius(shape: string): string { if (shape === 'circle') return '50%'; if (shape === 'rounded') return '8px'; if (shape === 'square') return '0px'; return '0px'; } export const Icon: UserComponent = ({ icon = 'fa-star', size = '32px', color = '#3b82f6', bgColor = 'transparent', bgShape = 'none', bgSize = '56px', link = '', style = {}, }) => { const { connectors: { connect, drag }, selected, } = useNode((node) => ({ selected: node.events.selected, })); const iconEl = ( ); const hasBg = bgShape !== 'none' && bgColor !== 'transparent'; const wrapperEl = hasBg ? (
{iconEl}
) : iconEl; const content = link ? ( e.preventDefault()} style={{ textDecoration: 'none', color: 'inherit' }}> {wrapperEl} ) : wrapperEl; return (
{ if (ref) connect(drag(ref)); }} style={{ display: 'inline-block', outline: selected ? '2px solid #3b82f6' : 'none', ...style, }} > {content}
); }; /* ---------- Settings panel ---------- */ const IconSettings: React.FC = () => { const { actions: { setProp }, props } = useNode((node) => ({ props: node.data.props as IconProps, })); const labelStyle: CSSProperties = { fontSize: 11, color: '#a1a1aa', display: 'block', marginBottom: 6 }; const inputStyle: CSSProperties = { width: '100%', padding: '4px 8px', background: '#27272a', color: '#e4e4e7', border: '1px solid #3f3f46', borderRadius: 4, fontSize: 12, }; const sizePresets = ['24px', '32px', '48px', '64px']; const colorPresets = ['#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6', '#ec4899', '#18181b', '#ffffff']; const shapePresets: Array<{ label: string; value: IconProps['bgShape'] }> = [ { label: 'None', value: 'none' }, { label: 'Circle', value: 'circle' }, { label: 'Square', value: 'square' }, { label: 'Rounded', value: 'rounded' }, ]; return (
{/* Icon picker */}
{COMMON_ICONS.map((ic) => ( ))}
{/* Custom icon class */}
setProp((p: IconProps) => { p.icon = e.target.value; })} placeholder="fa-star" style={inputStyle} />
{/* Size */}
{sizePresets.map((s) => ( ))}
{/* Color */}
{colorPresets.map((c) => (
{/* Background shape */}
{shapePresets.map((s) => ( ))}
{/* Background color */} {props.bgShape !== 'none' && (
{['#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6', '#18181b', '#f1f5f9', '#ffffff'].map((c) => (
)} {/* Background size */} {props.bgShape !== 'none' && (
setProp((p: IconProps) => { p.bgSize = e.target.value; })} placeholder="56px" style={inputStyle} />
)} {/* Link */}
setProp((p: IconProps) => { p.link = e.target.value; })} placeholder="https://..." style={inputStyle} />
); }; /* ---------- Craft config ---------- */ Icon.craft = { displayName: 'Icon', props: { icon: 'fa-star', size: '32px', color: '#3b82f6', bgColor: 'transparent', bgShape: 'none', bgSize: '56px', link: '', style: {}, }, rules: { canDrag: () => true, canMoveIn: () => false, canMoveOut: () => true, }, related: { settings: IconSettings, }, }; /* ---------- HTML export ---------- */ (Icon as any).toHtml = (props: IconProps, _childrenHtml: string) => { const { icon = 'fa-star', size = '32px', color = '#3b82f6', bgColor = 'transparent', bgShape = 'none', bgSize = '56px', link = '', style = {}, } = props; const iconStyle = cssPropsToString({ fontSize: size, color, lineHeight: '1' }); let iconHtml = ``; const hasBg = bgShape !== 'none' && bgColor !== 'transparent'; if (hasBg) { const bgStyle = cssPropsToString({ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: bgSize, height: bgSize, backgroundColor: bgColor, borderRadius: getBgBorderRadius(bgShape || 'none'), }); iconHtml = `${iconHtml}`; } if (link) { iconHtml = `${iconHtml}`; } const wrapperStyle = cssPropsToString({ display: 'inline-block', ...style }); return { html: `${iconHtml}` }; };