import React from 'react'; import { InputIcon } from '../../icons/ComponentIcons'; import type { ComponentDefinition } from '../types'; import { parseScopedPath } from '../../state/scopedPath'; export const inputDefinition: ComponentDefinition = { type: 'input', label: 'Input', icon: InputIcon, category: 'leaf', canHaveChildren: false, defaultStyles: { padding: '8px', border: '1px solid #ccc', borderRadius: '4px', width: 'auto', }, defaultProps: { placeholder: 'Enter text...', type: 'text', value: '', onChangeStatePath: '', }, propertySchema: [ { key: 'value', label: 'Value', type: 'text-binding', category: 'content' }, { key: 'onChangeStatePath', label: 'On Change State Path', type: 'text-binding', category: 'content' }, { key: 'placeholder', label: 'Placeholder', type: 'text', category: 'content' }, { key: 'type', label: 'Input Type', type: 'select', category: 'content', options: [ { value: 'text', label: 'Text' }, { value: 'email', label: 'Email' }, { value: 'password', label: 'Password' }, { value: 'number', label: 'Number' }, { value: 'tel', label: 'Tel' }, { value: 'url', label: 'URL' }, ], }, ], styleSchema: [ // Layout { key: 'width', label: 'Width', type: 'dimension', category: 'layout' }, { key: 'height', label: 'Height', type: 'dimension', category: 'layout' }, { key: 'minWidth', label: 'Min Width', type: 'dimension', category: 'layout' }, { key: 'maxWidth', label: 'Max Width', type: 'dimension', category: 'layout' }, { key: 'alignSelf', label: 'Self Align', type: 'select', category: 'layout', options: [ { value: 'auto', label: 'Auto' }, { value: 'flex-start', label: 'Start' }, { value: 'center', label: 'Center' }, { value: 'flex-end', label: 'End' }, { value: 'stretch', label: 'Stretch' }, { value: 'baseline', label: 'Baseline' }, ], }, // Positioning { key: 'position', label: 'Position', type: 'select', category: 'layout', options: [ { value: 'static', label: 'Static' }, { value: 'relative', label: 'Relative' }, { value: 'absolute', label: 'Absolute' }, { value: 'fixed', label: 'Fixed' }, { value: 'sticky', label: 'Sticky' }, ], }, { key: 'top', label: 'Top', type: 'dimension', category: 'layout' }, { key: 'right', label: 'Right', type: 'dimension', category: 'layout' }, { key: 'bottom', label: 'Bottom', type: 'dimension', category: 'layout' }, { key: 'left', label: 'Left', type: 'dimension', category: 'layout' }, { key: 'zIndex', label: 'Z-Index', type: 'number', category: 'layout' }, // Spacing { key: 'padding', label: 'Padding', type: 'dimension', category: 'spacing' }, { key: 'margin', label: 'Margin', type: 'dimension', category: 'spacing' }, // Appearance { key: 'color', label: 'Text Color', type: 'color', category: 'appearance' }, { key: 'backgroundColor', label: 'Background', type: 'color', category: 'appearance' }, { key: 'opacity', label: 'Opacity', type: 'range', category: 'appearance' }, // Typography { key: 'fontSize', label: 'Font Size', type: 'dimension', category: 'appearance' }, { key: 'fontFamily', label: 'Font Family', type: 'select', category: 'appearance', options: [ { value: 'inherit', label: 'Inherit' }, { value: 'Arial, sans-serif', label: 'Arial' }, { value: 'Helvetica, sans-serif', label: 'Helvetica' }, { value: 'Georgia, serif', label: 'Georgia' }, { value: '"Times New Roman", serif', label: 'Times New Roman' }, { value: '"Courier New", monospace', label: 'Courier New' }, { value: 'Verdana, sans-serif', label: 'Verdana' }, { value: 'system-ui, sans-serif', label: 'System UI' }, ], }, // Border { key: 'borderWidth', label: 'Border Width', type: 'dimension', category: 'appearance' }, { key: 'borderStyle', label: 'Border Style', type: 'select', category: 'appearance', options: [ { value: 'none', label: 'None' }, { value: 'solid', label: 'Solid' }, { value: 'dashed', label: 'Dashed' }, { value: 'dotted', label: 'Dotted' }, { value: 'double', label: 'Double' }, ], }, { key: 'borderColor', label: 'Border Color', type: 'color', category: 'appearance' }, { key: 'borderRadius', label: 'Border Radius', type: 'dimension', category: 'appearance' }, { key: 'boxShadow', label: 'Box Shadow', type: 'boxShadow', category: 'appearance' }, // Grid Item (when inside grid container) { key: 'gridColumn', label: 'Grid Column', type: 'text', category: 'grid-item' }, { key: 'gridRow', label: 'Grid Row', type: 'text', category: 'grid-item' }, { key: 'gridColumnStart', label: 'Column Start', type: 'text', category: 'grid-item' }, { key: 'gridColumnEnd', label: 'Column End', type: 'text', category: 'grid-item' }, { key: 'gridRowStart', label: 'Row Start', type: 'text', category: 'grid-item' }, { key: 'gridRowEnd', label: 'Row End', type: 'text', category: 'grid-item' }, { key: 'justifySelf', label: 'Justify Self', type: 'select', category: 'grid-item', options: [ { value: 'start', label: 'Start' }, { value: 'end', label: 'End' }, { value: 'center', label: 'Center' }, { value: 'stretch', label: 'Stretch' }, ], }, ], render: ({ component, isSelected, onSelect, previewMode, runtime }) => { // Resolve value binding const valueBinding = component.props?.value || ''; const displayValue = runtime.resolveText(valueBinding); // Handle onChange const handleChange = (e: React.ChangeEvent) => { if (previewMode && component.props?.onChangeStatePath) { const parsedPath = parseScopedPath(component.props.onChangeStatePath); runtime.setStateValue(parsedPath.fullPath, e.target.value); } }; return React.createElement('input', { type: component.props?.type || 'text', placeholder: component.props?.placeholder || 'Enter text...', value: displayValue || '', onChange: handleChange, style: { ...component.styles, outline: isSelected && !previewMode ? '2px solid rgb(255, 89, 47)' : 'none', }, onClick: previewMode ? undefined : (e: React.MouseEvent) => { e.stopPropagation(); onSelect(component.id); }, readOnly: !previewMode, }); }, };