import { Component, OnDestroy } from '@angular/core'; import { NgIf, NgFor, NgSwitch, NgSwitchCase, KeyValuePipe } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { MatExpansionModule } from '@angular/material/expansion'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { Subscription } from 'rxjs'; import { BuilderStateService } from '../services/builder-state.service'; import { ComponentItem, getComponentLabel } from '../../core/models/component'; @Component({ selector: 'app-property-panel', standalone: true, imports: [ NgIf, NgFor, NgSwitch, NgSwitchCase, KeyValuePipe, FormsModule, MatExpansionModule, MatFormFieldModule, MatInputModule, MatSelectModule, MatButtonModule, MatIconModule, MatSlideToggleModule, ], template: `

Properties

tune

Select a component to edit

{{ componentLabel }}

{{ component.type }}

Content
Text Level H1 H2 H3 H4 H5 H6
Content
Image URL Alt Text Link To (optional)
Button Text Link
Style Solid Dashed Dotted
Video URL
Autoplay Controls
Submit Button Text Email To
Address Zoom Level {{ z }}
{{ img.src }}
Image URL

No images. Add image URLs above.

HTML
Styles
{{ formatKey(entry) }}
`, }) export class PropertyPanelComponent implements OnDestroy { private sub: Subscription; component: ComponentItem | null = null; newImageUrl = ''; constructor(private state: BuilderStateService) { this.sub = this.state.selectedComponentId$.subscribe(() => { this.component = this.state.selectedComponent ? { ...this.state.selectedComponent } : null; this.newImageUrl = ''; }); } get componentLabel(): string { return this.component ? getComponentLabel(this.component.type) : ''; } get styleKeys(): string[] { return this.component ? Object.keys(this.component.styles) : []; } formatKey(key: string): string { return key.replace(/[A-Z]/g, c => ' ' + c.toLowerCase()).replace(/^./, s => s.toUpperCase()); } getImages(): { src: string; alt?: string }[] { return (this.component?.config['images'] as any[]) || []; } addImage(): void { const url = this.newImageUrl?.trim(); if (!url || !this.component) return; const images = [...this.getImages(), { src: url, alt: '' }]; this.component.config['images'] = images; this.onConfigChange(); this.newImageUrl = ''; } removeImage(index: number): void { if (!this.component) return; const images = this.getImages().filter((_, i) => i !== index); this.component.config['images'] = images; this.onConfigChange(); } onConfigChange(): void { if (this.component) { this.state.updateComponent(this.component.id, { config: { ...this.component.config } }); } } onStyleChange(key: string, value: string): void { if (this.component) { const updatedStyles = { ...this.component.styles, [key]: value }; this.component = { ...this.component, styles: updatedStyles }; this.state.updateComponent(this.component.id, { styles: updatedStyles }); } } removeComponent(): void { if (this.component) { this.state.removeComponent(this.component.id); } } ngOnDestroy(): void { this.sub?.unsubscribe(); } }