/** * Style Panel Configuration * * Defines which style controls appear for each element category. * Each category has groups of related properties. */ import { StyleGroup, StylePanelConfig } from '../types/editor'; // Common style groups shared across multiple element types const SPACING_GROUP: StyleGroup = { title: 'Spacing', properties: [ { name: 'Margin Top', cssProperty: 'marginTop', type: 'unit', unit: 'px' }, { name: 'Margin Right', cssProperty: 'marginRight', type: 'unit', unit: 'px' }, { name: 'Margin Bottom', cssProperty: 'marginBottom', type: 'unit', unit: 'px' }, { name: 'Margin Left', cssProperty: 'marginLeft', type: 'unit', unit: 'px' }, { name: 'Padding Top', cssProperty: 'paddingTop', type: 'unit', unit: 'px' }, { name: 'Padding Right', cssProperty: 'paddingRight', type: 'unit', unit: 'px' }, { name: 'Padding Bottom', cssProperty: 'paddingBottom', type: 'unit', unit: 'px' }, { name: 'Padding Left', cssProperty: 'paddingLeft', type: 'unit', unit: 'px' } ] }; const BORDER_GROUP: StyleGroup = { title: 'Border', properties: [ { name: 'Border Width', cssProperty: 'borderWidth', type: 'unit', unit: 'px' }, { name: 'Border Style', cssProperty: 'borderStyle', type: 'select', options: ['none', 'solid', 'dashed', 'dotted', 'double'] }, { name: 'Border Color', cssProperty: 'borderColor', type: 'color' }, { name: 'Border Radius', cssProperty: 'borderRadius', type: 'unit', unit: 'px' } ] }; const BACKGROUND_GROUP: StyleGroup = { title: 'Background', properties: [ { name: 'Background Color', cssProperty: 'backgroundColor', type: 'color' } ] }; const SIZE_GROUP: StyleGroup = { title: 'Size', properties: [ { name: 'Width', cssProperty: 'width', type: 'unit', unit: 'px' }, { name: 'Height', cssProperty: 'height', type: 'unit', unit: 'px' }, { name: 'Min Width', cssProperty: 'minWidth', type: 'unit', unit: 'px' }, { name: 'Max Width', cssProperty: 'maxWidth', type: 'unit', unit: 'px' }, { name: 'Min Height', cssProperty: 'minHeight', type: 'unit', unit: 'px' }, { name: 'Max Height', cssProperty: 'maxHeight', type: 'unit', unit: 'px' } ] }; // Text-specific groups const TYPOGRAPHY_GROUP: StyleGroup = { title: 'Typography', properties: [ { name: 'Font Family', cssProperty: 'fontFamily', type: 'select', options: [ 'inherit', 'system-ui, -apple-system, sans-serif', 'Georgia, serif', 'Menlo, monospace', 'Arial, sans-serif', 'Times New Roman, serif', 'Courier New, monospace', 'Verdana, sans-serif', 'Trebuchet MS, sans-serif' ] }, { name: 'Font Size', cssProperty: 'fontSize', type: 'unit', unit: 'px' }, { name: 'Font Weight', cssProperty: 'fontWeight', type: 'select', options: ['100', '200', '300', '400', '500', '600', '700', '800', '900'] }, { name: 'Color', cssProperty: 'color', type: 'color' }, { name: 'Text Align', cssProperty: 'textAlign', type: 'select', options: ['left', 'center', 'right', 'justify'] }, { name: 'Line Height', cssProperty: 'lineHeight', type: 'unit', unit: '' }, { name: 'Letter Spacing', cssProperty: 'letterSpacing', type: 'unit', unit: 'px' }, { name: 'Text Transform', cssProperty: 'textTransform', type: 'select', options: ['none', 'uppercase', 'lowercase', 'capitalize'] }, { name: 'Text Decoration', cssProperty: 'textDecoration', type: 'select', options: ['none', 'underline', 'line-through', 'overline'] } ] }; // Image-specific groups const IMAGE_GROUP: StyleGroup = { title: 'Image', properties: [ { name: 'Width', cssProperty: 'width', type: 'unit', unit: 'px' }, { name: 'Height', cssProperty: 'height', type: 'unit', unit: 'px' }, { name: 'Object Fit', cssProperty: 'objectFit', type: 'select', options: ['fill', 'contain', 'cover', 'none', 'scale-down'] }, { name: 'Object Position', cssProperty: 'objectPosition', type: 'select', options: ['center', 'top', 'bottom', 'left', 'right', 'top left', 'top right', 'bottom left', 'bottom right'] } ] }; // Container-specific groups const LAYOUT_GROUP: StyleGroup = { title: 'Layout', properties: [ { name: 'Display', cssProperty: 'display', type: 'select', options: ['block', 'flex', 'grid', 'inline-block', 'inline', 'none'] }, { name: 'Flex Direction', cssProperty: 'flexDirection', type: 'select', options: ['row', 'row-reverse', 'column', 'column-reverse'] }, { name: 'Justify Content', cssProperty: 'justifyContent', type: 'select', options: ['flex-start', 'flex-end', 'center', 'space-between', 'space-around', 'space-evenly'] }, { name: 'Align Items', cssProperty: 'alignItems', type: 'select', options: ['stretch', 'flex-start', 'flex-end', 'center', 'baseline'] }, { name: 'Gap', cssProperty: 'gap', type: 'unit', unit: 'px' }, { name: 'Flex Wrap', cssProperty: 'flexWrap', type: 'select', options: ['nowrap', 'wrap', 'wrap-reverse'] } ] }; const POSITION_GROUP: StyleGroup = { title: 'Position', properties: [ { name: 'Position', cssProperty: 'position', type: 'select', options: ['static', 'relative', 'absolute', 'fixed', 'sticky'] }, { name: 'Top', cssProperty: 'top', type: 'unit', unit: 'px' }, { name: 'Right', cssProperty: 'right', type: 'unit', unit: 'px' }, { name: 'Bottom', cssProperty: 'bottom', type: 'unit', unit: 'px' }, { name: 'Left', cssProperty: 'left', type: 'unit', unit: 'px' }, { name: 'Z-Index', cssProperty: 'zIndex', type: 'number' } ] }; // Button-specific groups const BUTTON_GROUP: StyleGroup = { title: 'Button Style', properties: [ { name: 'Background', cssProperty: 'backgroundColor', type: 'color' }, { name: 'Text Color', cssProperty: 'color', type: 'color' }, { name: 'Font Size', cssProperty: 'fontSize', type: 'unit', unit: 'px' }, { name: 'Font Weight', cssProperty: 'fontWeight', type: 'select', options: ['400', '500', '600', '700'] }, { name: 'Border Radius', cssProperty: 'borderRadius', type: 'unit', unit: 'px' }, { name: 'Padding X', cssProperty: 'paddingLeft', type: 'unit', unit: 'px' }, { name: 'Padding Y', cssProperty: 'paddingTop', type: 'unit', unit: 'px' }, { name: 'Cursor', cssProperty: 'cursor', type: 'select', options: ['pointer', 'default', 'not-allowed'] } ] }; // Link-specific groups const LINK_GROUP: StyleGroup = { title: 'Link Style', properties: [ { name: 'Color', cssProperty: 'color', type: 'color' }, { name: 'Font Size', cssProperty: 'fontSize', type: 'unit', unit: 'px' }, { name: 'Font Weight', cssProperty: 'fontWeight', type: 'select', options: ['400', '500', '600', '700'] }, { name: 'Text Decoration', cssProperty: 'textDecoration', type: 'select', options: ['none', 'underline'] } ] }; // List-specific groups const LIST_GROUP: StyleGroup = { title: 'List Style', properties: [ { name: 'List Style Type', cssProperty: 'listStyleType', type: 'select', options: ['disc', 'circle', 'square', 'decimal', 'decimal-leading-zero', 'lower-roman', 'upper-roman', 'lower-alpha', 'upper-alpha', 'none'] }, { name: 'List Style Position', cssProperty: 'listStylePosition', type: 'select', options: ['inside', 'outside'] } ] }; // Complete configuration mapping element categories to their style groups export const STYLE_PANEL_CONFIG: StylePanelConfig = { text: [ TYPOGRAPHY_GROUP, SPACING_GROUP, BACKGROUND_GROUP, BORDER_GROUP ], image: [ IMAGE_GROUP, BORDER_GROUP, SPACING_GROUP ], container: [ LAYOUT_GROUP, SIZE_GROUP, SPACING_GROUP, BACKGROUND_GROUP, BORDER_GROUP, POSITION_GROUP ], button: [ BUTTON_GROUP, BORDER_GROUP, SPACING_GROUP ], link: [ LINK_GROUP, SPACING_GROUP ], list: [ LIST_GROUP, TYPOGRAPHY_GROUP, SPACING_GROUP ], unknown: [ SIZE_GROUP, SPACING_GROUP, BACKGROUND_GROUP, BORDER_GROUP, POSITION_GROUP ] }; /** * Gets the style groups for a given element category */ export function getStyleGroupsForCategory(category: string): StyleGroup[] { return STYLE_PANEL_CONFIG[category] || STYLE_PANEL_CONFIG.unknown; } /** * Parses a CSS value with unit (e.g., "16px" -> { value: 16, unit: "px" }) */ export function parseStyleValue(value: string): { value: number; unit: string } { if (!value || value === 'auto' || value === 'inherit' || value === 'initial') { return { value: 0, unit: '' }; } const match = value.match(/^(-?\d*\.?\d+)(.*)?$/); if (match) { return { value: parseFloat(match[1]) || 0, unit: match[2]?.trim() || '' }; } return { value: 0, unit: '' }; } /** * Formats a value with unit for setting as style */ export function formatStyleValue(value: number, unit: string): string { if (unit === '') { return String(value); } return `${value}${unit}`; } /** * Converts RGB color to hex */ export function rgbToHex(rgb: string): string { // Handle already hex values if (rgb.startsWith('#')) { return rgb; } // Handle rgba/rgb const match = rgb.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/); if (match) { const r = parseInt(match[1]).toString(16).padStart(2, '0'); const g = parseInt(match[2]).toString(16).padStart(2, '0'); const b = parseInt(match[3]).toString(16).padStart(2, '0'); return `#${r}${g}${b}`; } return '#000000'; } /** * Gets display name for font family */ export function getFontDisplayName(fontFamily: string): string { // Extract first font from the stack const firstFont = fontFamily.split(',')[0].trim().replace(/['"]/g, ''); return firstFont || 'Default'; }