SMART PROPERTY SYSTEM DESIGN

Comprehensive guidelines for AI-assisted development within the Editor platform

🧠 Smart Property System - Intelligent & Reusable Design

Problem: Current approach requires manual property checking in each widget (NOT REUSABLE!)
Solution: Self-applying property groups with automatic style generation


❌ Current Problem (What You Pointed Out)

Manual Property Checking (ButtonWidget lines 143-254):

// ❌ THIS IS NOT SMART!
containerStyles = computed(() => {
  const props = this.widget()?.properties || {};
  
  // Manual checking - NOT REUSABLE!
  'font-family': props['fontFamily'] || 'inherit',
  'font-size': props['fontSize'] ? `${props['fontSize']}px` : '14px',
  'font-weight': props['fontWeight'] || 'normal',
  'font-style': props['fontStyle'] || 'normal',
  'text-align': props['textAlign'] || 'left',
  // ... 20 more manual checks!
});

customButtonStyles = computed(() => {
  const props = this.widget()?.properties || {};
  const styles: Record<string, string> = {};
  
  // Manual checking AGAIN - DUPLICATE CODE!
  if (props['fontFamily']) styles['font-family'] = props['fontFamily'];
  if (props['fontSize']) styles['font-size'] = `${props['fontSize']}px`;
  // ... MORE manual checks!
  
  return styles;
});

Problems:

  1. ❌ Not reusable - every widget needs this code
  2. ❌ Violates DRY - same logic duplicated everywhere
  3. ❌ Not maintainable - change font property → update 20 widgets
  4. ❌ Not AI-trainable - too much boilerplate
  5. ❌ Not modular - can't easily add/remove property groups

✅ Smart Solution (What You Want)

Self-Applying Property Groups

// ✅ SMART: Property group KNOWS how to apply itself!

// 1. Define property group with applicator
export const FONT_PROPERTY_GROUP: PropertyGroupWithApplicator = {
  id: 'font',
  title: 'Font & Text',
  icon: 'text_format',
  properties: [
    { key: 'fontFamily', type: 'select', defaultValue: 'inherit' },
    { key: 'fontSize', type: 'number', defaultValue: 14 },
    { key: 'fontWeight', type: 'select', defaultValue: 'normal' },
    // ...
  ],
  
  // ✅ SMART: Applicator converts properties → styles automatically!
  applicator: new FontPropertyApplicator()
};

// 2. Use in widget (ONE LINE!)
@Component({...})
export class ButtonWidgetComponent extends BaseWidgetComponent {
  // ✅ SMART: Automatically applies font properties!
  styles = usePropertyGroups(this.widget, this.activeBreakpoint, [
    FONT_PROPERTY_GROUP,
    APPEARANCE_PROPERTY_GROUP,
    LAYOUT_PROPERTY_GROUP
  ]);
}

Benefits:

  1. ✅ Reusable - any widget can use FONT_PROPERTY_GROUP
  2. ✅ DRY - logic in ONE place
  3. ✅ Maintainable - change applicator → all widgets updated
  4. ✅ AI-trainable - clear, simple pattern
  5. ✅ Modular - add/remove groups easily

🎯 Proposed Architecture

1. Property Group Applicators (Smart Converters)

// Base interface
interface PropertyApplicator {
  // Convert properties → CSS styles
  apply(properties: Record<string, any>, breakpoint: BreakpointKey): Record<string, string>;
  
  // Get property keys this applicator handles
  getHandledKeys(): string[];
  
  // Validate property values
  validate(properties: Record<string, any>): ValidationResult;
}

// Font applicator
export class FontPropertyApplicator implements PropertyApplicator {
  apply(props: Record<string, any>, breakpoint: BreakpointKey): Record<string, string> {
    const styles: Record<string, string> = {};
    
    // Smart conversion with defaults
    if (props['fontFamily']) styles['font-family'] = props['fontFamily'];
    if (props['fontSize']) styles['font-size'] = `${props['fontSize']}px`;
    if (props['fontWeight']) styles['font-weight'] = props['fontWeight'];
    if (props['fontStyle']) styles['font-style'] = props['fontStyle'];
    if (props['textAlign']) styles['text-align'] = props['textAlign'];
    if (props['textDecoration']) styles['text-decoration'] = props['textDecoration'];
    if (props['textTransform']) styles['text-transform'] = props['textTransform'];
    if (props['lineHeight']) styles['line-height'] = this.formatLineHeight(props['lineHeight']);
    if (props['letterSpacing']) styles['letter-spacing'] = `${props['letterSpacing']}px`;
    
    return styles;
  }
  
  getHandledKeys(): string[] {
    return ['fontFamily', 'fontSize', 'fontWeight', 'fontStyle', 'textAlign', 
            'textDecoration', 'textTransform', 'lineHeight', 'letterSpacing'];
  }
  
  private formatLineHeight(value: any): string {
    return typeof value === 'number' ? `${value}` : value;
  }
}

// Appearance applicator
export class AppearancePropertyApplicator implements PropertyApplicator {
  apply(props: Record<string, any>, breakpoint: BreakpointKey): Record<string, string> {
    const styles: Record<string, string> = {};
    
    if (props['backgroundColor']) {
      styles['background'] = props['backgroundColor'];
      styles['background-image'] = 'none'; // Override gradients
    }
    if (props['color']) styles['color'] = props['color'];
    if (props['borderWidth']) styles['border-width'] = `${props['borderWidth']}px`;
    if (props['borderStyle']) styles['border-style'] = props['borderStyle'];
    if (props['borderColor']) styles['border-color'] = props['borderColor'];
    if (props['borderRadius']) styles['border-radius'] = `${props['borderRadius']}px`;
    if (props['opacity']) styles['opacity'] = `${props['opacity']}`;
    if (props['boxShadow']) styles['box-shadow'] = props['boxShadow'];
    
    return styles;
  }
  
  getHandledKeys(): string[] {
    return ['backgroundColor', 'color', 'borderWidth', 'borderStyle', 
            'borderColor', 'borderRadius', 'opacity', 'boxShadow'];
  }
}

// Layout applicator
export class LayoutPropertyApplicator implements PropertyApplicator {
  apply(props: Record<string, any>, breakpoint: BreakpointKey): Record<string, string> {
    const styles: Record<string, string> = {};
    
    if (props['padding']) styles['padding'] = `${props['padding']}px`;
    if (props['margin']) styles['margin'] = `${props['margin']}px`;
    if (props['display']) styles['display'] = props['display'];
    if (props['flexDirection']) styles['flex-direction'] = props['flexDirection'];
    if (props['justifyContent']) styles['justify-content'] = props['justifyContent'];
    if (props['alignItems']) styles['align-items'] = props['alignItems'];
    if (props['gap']) styles['gap'] = `${props['gap']}px`;
    
    return styles;
  }
  
  getHandledKeys(): string[] {
    return ['padding', 'margin', 'display', 'flexDirection', 
            'justifyContent', 'alignItems', 'gap'];
  }
}

2. Property Group Registry (Centralized)

@Injectable({ providedIn: 'root' })
export class PropertyApplicatorRegistry {
  private applicators = new Map<string, PropertyApplicator>();
  
  constructor() {
    // Register built-in applicators
    this.register('font', new FontPropertyApplicator());
    this.register('appearance', new AppearancePropertyApplicator());
    this.register('layout', new LayoutPropertyApplicator());
  }
  
  register(groupId: string, applicator: PropertyApplicator): void {
    this.applicators.set(groupId, applicator);
  }
  
  get(groupId: string): PropertyApplicator | undefined {
    return this.applicators.get(groupId);
  }
  
  // Apply all registered applicators
  applyAll(properties: Record<string, any>, breakpoint: BreakpointKey): Record<string, string> {
    const allStyles: Record<string, string> = {};
    
    for (const [groupId, applicator] of this.applicators) {
      const groupStyles = applicator.apply(properties, breakpoint);
      Object.assign(allStyles, groupStyles);
    }
    
    return allStyles;
  }
}

3. Smart Widget Hook (Reusable Everywhere!)

// Helper function for widgets
export function usePropertyStyles(
  widget: Signal<Widget | undefined>,
  breakpoint: Signal<BreakpointKey>,
  groupIds: string[]  // Which property groups to apply
): Signal<Record<string, string>> {
  const applicatorRegistry = inject(PropertyApplicatorRegistry);
  
  return computed(() => {
    const w = widget();
    if (!w) return {};
    
    const props = w.properties || {};
    const bp = breakpoint();
    const styles: Record<string, string> = {};
    
    // Apply each property group automatically
    for (const groupId of groupIds) {
      const applicator = applicatorRegistry.get(groupId);
      if (applicator) {
        const groupStyles = applicator.apply(props, bp);
        Object.assign(styles, groupStyles);
      }
    }
    
    return styles;
  });
}

4. Widget Usage (SIMPLE & CLEAN!)

// ✅ SMART: ButtonWidget using new system
@Component({
  selector: 'lib-button-widget',
  template: `
    <ui-button 
      [ngStyle]="smartStyles()"
      [text]="config().text"
    ></ui-button>
  `
})
export class ButtonWidgetComponent extends BaseWidgetComponent {
  // ✅ ONE LINE: Apply font, appearance, and layout properties!
  smartStyles = usePropertyStyles(
    this.widget,
    this.activeBreakpoint,
    ['font', 'appearance', 'layout']  // ← Groups to apply
  );
  
  // That's IT! No manual property checking!
}

Compare:


🔄 Property Flow (How It Would Work)

1. User changes "Font Size" in properties panel
      ↓
2. PropertyEditorComponent emits propertyChange event
      ↓
3. PropertiesPanelNewComponent updates widget.properties
      ↓
4. EditorService flash-persists widget
      ↓
5. Widget re-renders (signal reactivity)
      ↓
6. usePropertyStyles() hook triggers
      ↓
7. FontPropertyApplicator.apply() converts properties → styles
      ↓
8. Styles applied via [ngStyle]
      ↓
9. Button appearance updates instantly! ✅

🎯 Benefits

For Developers:

For Widgets:

// Want font properties? ONE LINE!
smartStyles = usePropertyStyles(this.widget, this.activeBreakpoint, ['font']);

// Want font + appearance? ONE LINE!
smartStyles = usePropertyStyles(this.widget, this.activeBreakpoint, ['font', 'appearance']);

// Want everything? ONE LINE!
smartStyles = usePropertyStyles(this.widget, this.activeBreakpoint, ['font', 'appearance', 'layout', 'animation']);

For AI:


📊 Impact

Code Reduction:

Maintainability:


🚀 Implementation Plan

Phase 1: Create Core System (30 mins)

  1. Create PropertyApplicator interface
  2. Create FontPropertyApplicator
  3. Create AppearancePropertyApplicator
  4. Create LayoutPropertyApplicator
  5. Create PropertyApplicatorRegistry
  6. Create usePropertyStyles() hook

Phase 2: Refactor ButtonWidget (15 mins)

  1. Remove manual property checking
  2. Use usePropertyStyles() hook
  3. Test and verify

Phase 3: Apply to Other Widgets (30 mins)

  1. TextWidget
  2. ImageWidget
  3. CardWidget
  4. All other widgets

💡 My Recommendation

DO THIS NOW! This is the RIGHT way to build a reusable system!

Your instinct is correct - the code needs to be SMARTER, not MORE code!

Want me to implement this? I can have the core system ready in 30 minutes! 🚀