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:
- ❌ 100+ lines of manual checking
- ❌ NOT reusable
- ❌ Duplicated in every widget
- ❌ 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:
- ❌ Old: 100+ lines of manual
containerStyles(),customButtonStyles() - ✅ New: 1 directive in template!
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:
- Computed Signal (reactive, efficient):
computedStyles = computed(() => {
// Only re-runs when widget.properties or breakpoint changes
return this.registry.applyGroups(props, breakpoint, groups);
});
- Effect (minimal re-renders):
effect(() => {
const styles = this.computedStyles();
// Only applies to DOM when styles actually change
this.applyStylesToElement(styles);
});
- 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)
- Create
apply-property-groups.directive.ts - Use PropertyApplicatorRegistry
- Reactive with signals + effects
- Test with ButtonWidget
Phase 2: Refactor ButtonWidget (10 mins)
- Remove 100+ lines of manual code
- Add directive to template
- Test and verify
Phase 3: Apply to All Widgets (30 mins)
- TextWidget, ImageWidget, CardWidget
- All other widgets
- Massive code reduction
Total: ~1 hour to complete!
🎯 My Understanding NOW
What You Want:
- ✅ Smart way to apply font properties to elements (directive!)
- ✅ Smart way to apply appearance properties to elements (directive!)
- ✅ Reusable across ALL widgets
- ✅ Performant (signals + effects)
- ✅ Platform-agnostic (applicators are JSON-based)
What You DON'T Want:
- ❌ Manual style checking in every widget
- ❌ 100+ lines of repeated code
- ❌ Hard to maintain
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! 🚀