import React, { useState, useRef, useEffect } from 'react' import { createPortal } from 'react-dom' import { X, Link as LinkIcon, Unlink as UnlinkIcon, RotateCcw } from 'lucide-react' export default function InlineSpacingEditor({ field, isOpen, onClose, onUpdate, anchorRef, }) { const [position, setPosition] = useState({ top: 0, left: 0 }) const [positionCalculated, setPositionCalculated] = useState(false) const toolbarRef = useRef(null) const parseVal = (v) => { if (!v) return 0 return parseInt(v, 10) || 0 } const getCurrentMargin = () => { const margin = field?.margin || { top: 0, right: 0, bottom: 8, left: 0 } return { top: parseVal(margin.top), right: parseVal(margin.right), bottom: margin.bottom !== undefined ? parseVal(margin.bottom) : 8, left: parseVal(margin.left), } } const getCurrentPadding = () => { const padding = field?.padding || { top: 0, right: 0, bottom: 0, left: 0 } if (typeof padding === 'string' && !padding.includes(' ')) { const v = parseVal(padding) return { top: v, right: v, bottom: v, left: v } } if (typeof padding === 'object') { return { top: parseVal(padding.top), right: parseVal(padding.right), bottom: parseVal(padding.bottom), left: parseVal(padding.left), } } return { top: 0, right: 0, bottom: 0, left: 0 } } const [margin, setMargin] = useState(getCurrentMargin()) const [padding, setPadding] = useState(getCurrentPadding()) const [marginLinked, setMarginLinked] = useState(false) const [paddingLinked, setPaddingLinked] = useState(false) useEffect(() => { if (isOpen) { setMargin(getCurrentMargin()) setPadding(getCurrentPadding()) } }, [isOpen, field?.margin, field?.padding]) useEffect(() => { setPositionCalculated(false) }, [isOpen]) // Position calculation via anchorRef useEffect(() => { if (isOpen && anchorRef?.current) { const updatePosition = () => { if (!anchorRef?.current) return const anchorRect = anchorRef.current.getBoundingClientRect() const toolbarWidth = 240 const toolbarHeight = 220 let top = anchorRect.bottom + 8 let left = anchorRect.left - toolbarWidth / 2 + anchorRect.width / 2 const viewportWidth = window.innerWidth const viewportHeight = window.innerHeight // Vertical adjustment if (top + toolbarHeight > viewportHeight) { top = anchorRect.top - toolbarHeight - 8 } if (top < 8) top = 8 // Horizontal adjustment if (left < 8) left = 8 if (left + toolbarWidth > viewportWidth - 8) { left = viewportWidth - toolbarWidth - 8 } setPosition({ top, left }) setPositionCalculated(true) } const timeoutId = setTimeout(updatePosition, 0) window.addEventListener('scroll', updatePosition, true) window.addEventListener('resize', updatePosition) return () => { clearTimeout(timeoutId) window.removeEventListener('scroll', updatePosition, true) window.removeEventListener('resize', updatePosition) } } }, [isOpen, anchorRef]) // Click outside listener useEffect(() => { if (isOpen) { const handleClickOutside = (e) => { if (anchorRef?.current && anchorRef.current.contains(e.target)) { return } if (toolbarRef.current && toolbarRef.current.contains(e.target)) { return } onClose() } document.addEventListener('mousedown', handleClickOutside) return () => document.removeEventListener('mousedown', handleClickOutside) } }, [isOpen, onClose, anchorRef]) // Keyboard shortcut: Escape to close useEffect(() => { if (isOpen) { const handleKeyDown = (e) => { if (e.key === 'Escape') { onClose() } } document.addEventListener('keydown', handleKeyDown) return () => document.removeEventListener('keydown', handleKeyDown) } }, [isOpen, onClose]) // Update margin handler (real-time preview) const handleMarginChange = (side, value) => { const numValue = parseInt(value, 10) || 0 let newMargin if (marginLinked) { newMargin = { top: numValue, right: numValue, bottom: numValue, left: numValue } } else { newMargin = { ...margin, [side]: numValue } } setMargin(newMargin) if (onUpdate) { onUpdate({ margin: newMargin, padding }) } } // Update padding handler (real-time preview) const handlePaddingChange = (side, value) => { const numValue = parseInt(value, 10) || 0 let newPadding if (paddingLinked) { newPadding = { top: numValue, right: numValue, bottom: numValue, left: numValue } } else { newPadding = { ...padding, [side]: numValue } } setPadding(newPadding) if (onUpdate) { onUpdate({ margin, padding: newPadding }) } } // Reset margin to default const resetMargin = () => { const defaultMargin = { top: 0, right: 0, bottom: 8, left: 0 } setMargin(defaultMargin) if (onUpdate) { onUpdate({ margin: defaultMargin, padding }) } } // Reset padding to default const resetPadding = () => { const defaultPadding = { top: 0, right: 0, bottom: 0, left: 0 } setPadding(defaultPadding) if (onUpdate) { onUpdate({ margin, padding: defaultPadding }) } } if (!isOpen || !positionCalculated) return null return createPortal(