import React, { CSSProperties } from 'react'; import { useNode, UserComponent } from '@craftjs/core'; import { cssPropsToString } from '../../utils/style-helpers'; interface ButtonLinkProps { text?: string; href?: string; target?: '_self' | '_blank'; style?: CSSProperties; } export const ButtonLink: UserComponent = ({ text = 'Click Me', href = '#', target = '_self', style = {}, }) => { const { connectors: { connect, drag }, selected, } = useNode((node) => ({ selected: node.events.selected, })); return ( { if (ref) connect(drag(ref)); }} href={href} target={target} onClick={(e) => { // Prevent navigation inside editor e.preventDefault(); }} style={{ display: 'inline-block', textDecoration: 'none', cursor: 'pointer', outline: selected ? '2px solid #3b82f6' : 'none', ...style, }} > {text} ); }; /* ---------- Settings panel ---------- */ const ButtonLinkSettings: React.FC = () => { const { actions: { setProp }, props } = useNode((node) => ({ props: node.data.props as ButtonLinkProps, })); const colorPresets = [ { bg: '#3b82f6', color: '#ffffff', label: 'Blue' }, { bg: '#10b981', color: '#ffffff', label: 'Green' }, { bg: '#ef4444', color: '#ffffff', label: 'Red' }, { bg: '#f59e0b', color: '#18181b', label: 'Amber' }, { bg: '#8b5cf6', color: '#ffffff', label: 'Purple' }, { bg: '#18181b', color: '#ffffff', label: 'Dark' }, { bg: '#ffffff', color: '#18181b', label: 'White' }, { bg: 'transparent', color: '#3b82f6', label: 'Ghost' }, ]; const radiusPresets = ['0px', '4px', '8px', '12px', '9999px']; const paddingPresets = ['8px 16px', '10px 20px', '12px 24px', '14px 32px', '16px 40px']; return (
setProp((p: ButtonLinkProps) => { p.text = e.target.value; })} style={{ width: '100%', padding: '4px 8px', background: '#27272a', color: '#e4e4e7', border: '1px solid #3f3f46', borderRadius: 4, fontSize: 12 }} />
setProp((p: ButtonLinkProps) => { p.href = e.target.value; })} placeholder="https://..." style={{ width: '100%', padding: '4px 8px', background: '#27272a', color: '#e4e4e7', border: '1px solid #3f3f46', borderRadius: 4, fontSize: 12 }} />
{(['_self', '_blank'] as const).map((t) => ( ))}
{colorPresets.map((preset) => (
{radiusPresets.map((r) => ( ))}
{paddingPresets.map((p) => ( ))}
setProp((p: ButtonLinkProps) => { p.style = { ...p.style, fontSize: e.target.value }; })} style={{ width: '100%', padding: '4px 8px', background: '#27272a', color: '#e4e4e7', border: '1px solid #3f3f46', borderRadius: 4, fontSize: 11 }} />
); }; /* ---------- Craft config ---------- */ ButtonLink.craft = { displayName: 'Button', props: { text: 'Click Me', href: '#', target: '_self', style: { backgroundColor: '#3b82f6', color: '#ffffff', padding: '12px 24px', borderRadius: '8px', fontWeight: '600', fontSize: '16px', border: 'none', }, }, rules: { canDrag: () => true, canMoveIn: () => false, canMoveOut: () => true, }, related: { settings: ButtonLinkSettings, }, }; /* ---------- HTML export ---------- */ (ButtonLink as any).toHtml = (props: ButtonLinkProps, _childrenHtml: string) => { const styleStr = cssPropsToString({ display: 'inline-block', textDecoration: 'none', ...props.style, }); const escapedText = (props.text || '').replace(//g, '>'); const targetAttr = props.target === '_blank' ? ' target="_blank" rel="noopener noreferrer"' : ''; return { html: `${escapedText}`, }; };