INTELLIGENT STYLE APPLICATION DIRECTIVE

Comprehensive guidelines for AI-assisted development within the Editor platform

🎯 Intelligent Style Application Directive - The RIGHT Solution

Your Question: "a way to implement for example the font dynamics. or background dynamic styles"

What You Want: Smart, performant, reusable way to APPLY property groups to elements

Solution: Style Application Directive! ✅


❌ Current Problem (What You Pointed Out)

ButtonWidget lines 143-254: Manual style checking

// ❌ NOT SMART: Manually checking EVERY property in EVERY widget!
containerStyles = computed(() => {
  const props = this.widget()?.properties || {};
  return {
    'font-family': props['fontFamily'] || 'inherit',
    'font-size': props['fontSize'] ? `${props['fontSize']}px` : '14px',
    'font-weight': props['fontWeight'] || 'normal',
    // ... 20 more lines
  };
});

customButtonStyles = computed(() => {
  const props = this.widget()?.properties || {};
  if (props['fontFamily']) styles['font-family'] = props['fontFamily'];
  if (props['fontSize']) styles['font-size'] = `${props['fontSize']}px`;
  // ... 30 more lines
  return styles;
});

// Then in template:
<ui-button [ngStyle]="customButtonStyles()">...</ui-button>

Problems:

  1. ❌ 100+ lines of manual checking
  2. ❌ NOT reusable
  3. ❌ Duplicated in every widget
  4. ❌ Hard to maintain

✅ SMART Solution: Style Application Directive

Concept: Directive automatically applies property groups to element!

/**
 * libApplyPropertyGroups Directive
 * 
 * Intelligently applies property group styles to element
 * Uses PropertyApplicators under the hood!
 * 
 * Usage:
 * ```html
 * <div 
 *   libApplyPropertyGroups
 *   [propertyGroups]="['font', 'appearance']"
 *   [widget]="widget()"
 *   [breakpoint]="activeBreakpoint()"
 * >
 *   Content here
 * </div>
 * ```
 * 
 * Result: Font + Appearance properties automatically applied!
 */
@Directive({
  selector: '[libApplyPropertyGroups]',
  standalone: true
})
export class ApplyPropertyGroupsDirective implements OnInit {
  // Inputs
  propertyGroups = input.required<string[]>();  // ['font', 'appearance', 'layout']
  widget = input.required<Widget>();
  breakpoint = input<BreakpointKey>('base');
  
  // Services
  private readonly registry = inject(PropertyApplicatorRegistry);
  private readonly renderer = inject(Renderer2);
  private readonly elementRef = inject(ElementRef);
  private readonly logger = inject(LoggerService).child({ source: 'ApplyPropertyGroups' });
  
  // Computed styles (reactive!)
  private readonly computedStyles = computed(() => {
    const w = this.widget();
    const bp = this.breakpoint();
    const groups = this.propertyGroups();
    
    if (!w) return {};
    
    // ✅ SMART: Use applicators to convert properties → styles!
    return this.registry.applyGroups(w.properties || {}, bp, groups);
  });
  
  constructor() {
    // ⚡ PERFORMANCE: Use effect to reactively apply styles
    effect(() => {
      const styles = this.computedStyles();
      this.applyStylesToElement(styles);
    });
  }
  
  /**
   * Apply styles to host element (PERFORMANT!)
   */
  private applyStylesToElement(styles: Record<string, string>): void {
    const element = this.elementRef.nativeElement;
    
    // Clear previous styles (optional - can be optimized)
    // Only clear styles we manage
    const handledKeys = this.getHandledStyleKeys();
    for (const key of handledKeys) {
      this.renderer.removeStyle(element, key);
    }
    
    // Apply new styles
    for (const [key, value] of Object.entries(styles)) {
      this.renderer.setStyle(element, key, value);
    }
    
    this.logger.debug('Styles applied', {
      count: Object.keys(styles).length,
      groups: this.propertyGroups()
    });
  }
  
  /**
   * Get all style keys handled by current property groups
   */
  private getHandledStyleKeys(): string[] {
    const groups = this.propertyGroups();
    const keys: string[] = [];
    
    for (const groupId of groups) {
      const applicator = this.registry.get(groupId);
      if (applicator) {
        // Convert property keys → CSS property names
        // e.g., 'fontFamily' → 'font-family'
        keys.push(...applicator.getHandledKeys().map(k => this.toCssProperty(k)));
      }
    }
    
    return keys;
  }
  
  private toCssProperty(key: string): string {
    // Convert camelCase → kebab-case
    return key.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`);
  }
  
  ngOnInit() {
    // Initial application happens via effect in constructor
  }
}

🎨 Usage in Widgets (THE MAGIC!)

ButtonWidget (After Refactor):

@Component({
  template: `
    <!-- ✅ ONE LINE: Directive applies font + appearance automatically! -->
    <div 
      libApplyPropertyGroups
      [propertyGroups]="['font', 'appearance']"
      [widget]="widget()"
      [breakpoint]="activeBreakpoint()"
    >
      <ui-button [text]="config().text"></ui-button>
    </div>
  `
})
export class ButtonWidgetComponent extends BaseWidgetComponent {
  // ✅ NO MORE MANUAL STYLE CHECKING!
  // Directive handles it all!
  
  config = computed(() => ({
    text: this.widget().properties['text'] || 'Button'
  }));
  
  // That's it! 100+ lines removed!
}

Compare:


TextWidget (Example):

<!-- ✅ Font properties auto-applied! -->
<div 
  libApplyPropertyGroups
  [propertyGroups]="['font']"
  [widget]="widget()"
  [breakpoint]="activeBreakpoint()"
>
  {{ config().text }}
</div>

ImageWidget (Example):

<!-- ✅ Appearance properties auto-applied! -->
<img 
  libApplyPropertyGroups
  [propertyGroups]="['appearance']"
  [widget]="widget()"
  [src]="config().src"
/>

⚡ Performance (CRITICAL!)

How It's Fast:

  1. Computed Signal (reactive, efficient):
computedStyles = computed(() => {
  // Only re-runs when widget.properties or breakpoint changes
  return this.registry.applyGroups(props, breakpoint, groups);
});
  1. Effect (minimal re-renders):
effect(() => {
  const styles = this.computedStyles();
  // Only applies to DOM when styles actually change
  this.applyStylesToElement(styles);
});
  1. Renderer2 (Angular-optimized):
this.renderer.setStyle(element, key, value);
// Uses Angular's optimized DOM manipulation

Result: Only updates when properties change, minimal DOM operations!


🌐 Platform-Agnostic Strategy

For Web (Current):

<!-- ✅ Directive applies styles to DOM -->
<div libApplyPropertyGroups [propertyGroups]="['font']"></div>

For React Native (Future):

// ✅ Component wrapper uses same applicators!
<ApplyPropertyGroups propertyGroups={['font']} widget={widget}>
  <Text>Content</Text>
</ApplyPropertyGroups>

// Under the hood:
const styles = registry.applyGroups(widget.properties, breakpoint, ['font']);
// Convert to React Native style object
return <View style={styles}>{children}</View>;

For Flutter (Future):

// ✅ Widget uses same applicators (JSON contract)!
ApplyPropertyGroups(
  propertyGroups: ['font'],
  widget: widget,
  child: Text('Content')
)

// Under the hood:
var styles = registry.applyGroups(widget.properties, breakpoint, ['font']);
// Convert to Flutter TextStyle
return Text(child, style: TextStyle.fromJson(styles));

Key: PropertyApplicators return JSON-serializable style objects that work EVERYWHERE!


💎 The Complete Picture

Property Groups (Definitions)
    ↓
PropertyApplicators (Logic - Platform-agnostic!)
    ↓
    ├─→ Web: libApplyPropertyGroups Directive
    ├─→ React Native: ApplyPropertyGroups Component
    └─→ Flutter: ApplyPropertyGroups Widget
    ↓
Styles Applied to Elements

Reusability: PropertyApplicators work on ALL platforms!


🚀 Implementation Plan

Phase 1: Build Directive (20 mins)

Phase 2: Refactor ButtonWidget (10 mins)

Phase 3: Apply to All Widgets (30 mins)

Total: ~1 hour to complete!


🎯 My Understanding NOW

What You Want:

What You DON'T Want:

Solution: libApplyPropertyGroups Directive that uses the PropertyApplicators!


Is THIS what you meant? 🎯

Want me to build the directive now? I'll have it working in 20 minutes! 🚀