import React, { CSSProperties } from 'react'; import { useNode, UserComponent } from '@craftjs/core'; import { cssPropsToString } from '../../utils/style-helpers'; import { escapeAttr } from '../../utils/escape'; interface ContainerProps { style?: CSSProperties; tag?: 'div' | 'section' | 'article' | 'header' | 'footer' | 'main'; children?: React.ReactNode; cssId?: string; cssClass?: string; anchorId?: string; hideOnDesktop?: boolean; hideOnTablet?: boolean; hideOnMobile?: boolean; animation?: string; animationDelay?: string; fullWidth?: boolean; contentWidth?: 'boxed' | 'full'; } // Map textAlign to a flex alignItems value so block-level children (images, // columns, sections) align horizontally — textAlign alone only affects inline // content. Returns undefined when no alignment is set so we leave layout as // normal block flow. const flexAlignFromTextAlign = (textAlign: CSSProperties['textAlign']): CSSProperties => { if (textAlign === 'center') return { display: 'flex', flexDirection: 'column', alignItems: 'center' }; if (textAlign === 'right') return { display: 'flex', flexDirection: 'column', alignItems: 'flex-end' }; if (textAlign === 'left') return { display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }; return {}; }; export const Container: UserComponent = ({ style = {}, tag = 'div', children, fullWidth = false, contentWidth = 'full', anchorId, }) => { const { connectors: { connect, drag } } = useNode(); const needsBoxedWrapper = contentWidth === 'boxed'; const flexStyles = flexAlignFromTextAlign(style.textAlign); const outerStyle: CSSProperties = { minHeight: '40px', ...style, ...(fullWidth ? { width: '100vw', marginLeft: 'calc(-50vw + 50%)' } : {}), ...(needsBoxedWrapper ? {} : flexStyles), }; const el = React.createElement( tag, { ref: (ref: HTMLElement | null): void => { if (ref) connect(drag(ref)); }, style: outerStyle, 'data-craft-container': 'true', id: anchorId || undefined, }, needsBoxedWrapper ? React.createElement('div', { style: { maxWidth: '1200px', margin: '0 auto', ...flexStyles } }, children) : children, ); return el; }; /* ---------- Craft config ---------- */ Container.craft = { displayName: 'Container', props: { style: { padding: '20px', minHeight: '100px' }, tag: 'div', fullWidth: false, contentWidth: 'full', anchorId: '', }, rules: { canDrag: () => true, canMoveIn: () => true, canMoveOut: () => true, }, }; /* ---------- HTML export ---------- */ (Container as any).toHtml = (props: ContainerProps, childrenHtml: string) => { const tag = props.tag || 'div'; const isBoxed = props.contentWidth === 'boxed'; const flexStyles = flexAlignFromTextAlign(props.style?.textAlign); const outerCss: CSSProperties = { ...props.style, ...(isBoxed ? {} : flexStyles), }; if (props.fullWidth) { outerCss.width = '100vw'; outerCss.marginLeft = 'calc(-50vw + 50%)'; } const styleStr = cssPropsToString(outerCss); const idAttr = props.anchorId ? ` id="${escapeAttr(props.anchorId)}"` : ''; if (isBoxed) { const innerStyle = cssPropsToString({ maxWidth: '1200px', margin: '0 auto', ...flexStyles }); return { html: `<${tag}${idAttr}${styleStr ? ` style="${styleStr}"` : ''}>${childrenHtml}` }; } return { html: `<${tag}${idAttr}${styleStr ? ` style="${styleStr}"` : ''}>${childrenHtml}` }; };