182 lines
6.6 KiB
TypeScript
182 lines
6.6 KiB
TypeScript
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<HTMLInputElement>) => {
|
|
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,
|
|
});
|
|
},
|
|
};
|