diff --git a/src/app/widgets/registry/widget-registry.bootstrap.service.ts b/src/app/widgets/registry/widget-registry.bootstrap.service.ts new file mode 100644 index 0000000..2ebff97 --- /dev/null +++ b/src/app/widgets/registry/widget-registry.bootstrap.service.ts @@ -0,0 +1,16 @@ +import { Injectable } from '@angular/core'; +import { HeroWidgetComponent, FooterNavigationWidgetComponent, ProductCarouselWidgetComponent } from '../ui'; +import { WidgetRegistryService } from './widget-registry.service'; + +@Injectable({ providedIn: 'root' }) +export class WidgetRegistryBootstrapService { + constructor(private readonly registry: WidgetRegistryService) {} + + registerDefaults(): void { + this.registry.registerMany([ + { type: 'hero', version: '1.0.0', component: HeroWidgetComponent }, + { type: 'footer-navigation', version: '1.0.0', component: FooterNavigationWidgetComponent }, + { type: 'product-carousel', version: '1.0.0', component: ProductCarouselWidgetComponent } + ]); + } +} diff --git a/src/app/widgets/ui/footer-navigation-widget.component.ts b/src/app/widgets/ui/footer-navigation-widget.component.ts new file mode 100644 index 0000000..fe646c4 --- /dev/null +++ b/src/app/widgets/ui/footer-navigation-widget.component.ts @@ -0,0 +1,44 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; + +export interface FooterNavigationLink { + id: string; + label: string; + href: string; +} + +@Component({ + selector: 'app-footer-navigation-widget', + standalone: true, + imports: [CommonModule], + template: ` + + `, + styles: [ + ` + .footer-nav-widget { padding: 1rem; border-top: 1px solid var(--border-color, #d3dad9); } + ul { display: flex; gap: 1rem; list-style: none; margin: 0; padding: 0; flex-wrap: wrap; } + button { background: transparent; border: none; color: var(--text-secondary, #667a77); cursor: pointer; } + ` + ], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class FooterNavigationWidgetComponent { + @Input() links: FooterNavigationLink[] = []; + + @Output() linkSelected = new EventEmitter(); + + onLinkClick(link: FooterNavigationLink): void { + this.linkSelected.emit(link); + } +} diff --git a/src/app/widgets/ui/hero-widget.component.ts b/src/app/widgets/ui/hero-widget.component.ts new file mode 100644 index 0000000..66586d4 --- /dev/null +++ b/src/app/widgets/ui/hero-widget.component.ts @@ -0,0 +1,39 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; + +@Component({ + selector: 'app-hero-widget', + standalone: true, + imports: [CommonModule], + template: ` +
+

{{ title }}

+ @if (subtitle) { +

{{ subtitle }}

+ } + @if (ctaLabel) { + + } +
+ `, + styles: [ + ` + .hero-widget { padding: 2rem; border-radius: var(--radius-lg, 16px); background: var(--bg-secondary, #f5f5f5); } + .hero-widget__title { margin: 0 0 0.5rem; font-size: 2rem; color: var(--text-primary, #1e3c38); } + .hero-widget__subtitle { margin: 0 0 1rem; color: var(--text-secondary, #667a77); } + .hero-widget__cta { border: none; border-radius: var(--radius-md, 12px); padding: 0.75rem 1rem; cursor: pointer; background: var(--primary-color, #497671); color: #fff; } + ` + ], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class HeroWidgetComponent { + @Input() title: string = ''; + @Input() subtitle: string = ''; + @Input() ctaLabel: string = ''; + + @Output() ctaClicked = new EventEmitter(); + + onCtaClick(): void { + this.ctaClicked.emit(); + } +} diff --git a/src/app/widgets/ui/index.ts b/src/app/widgets/ui/index.ts new file mode 100644 index 0000000..9543cce --- /dev/null +++ b/src/app/widgets/ui/index.ts @@ -0,0 +1,3 @@ +export * from './footer-navigation-widget.component'; +export * from './hero-widget.component'; +export * from './product-carousel-widget.component'; diff --git a/src/app/widgets/ui/product-carousel-widget.component.ts b/src/app/widgets/ui/product-carousel-widget.component.ts new file mode 100644 index 0000000..61cca6f --- /dev/null +++ b/src/app/widgets/ui/product-carousel-widget.component.ts @@ -0,0 +1,53 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { ProductCardConfig } from '../../shared/models/ui'; + +@Component({ + selector: 'app-product-carousel-widget', + standalone: true, + imports: [CommonModule], + template: ` + + `, + styles: [ + ` + .product-carousel-widget { padding: 1rem; } + .product-carousel-widget__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 1rem; } + .product-card { padding: 0.75rem; border: 1px solid var(--border-color, #d3dad9); border-radius: var(--radius-md, 12px); } + img { width: 100%; height: 140px; object-fit: cover; border-radius: 8px; margin-bottom: 0.5rem; } + h3 { margin: 0 0 0.25rem; font-size: 1rem; } + p { margin: 0 0 0.5rem; color: var(--text-secondary, #667a77); } + button { margin-top: 0.5rem; border: none; border-radius: 8px; padding: 0.5rem 0.75rem; cursor: pointer; background: var(--primary-color, #497671); color: #fff; } + ` + ], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class ProductCarouselWidgetComponent { + @Input() title: string = ''; + @Input() actionLabel: string = 'Select'; + @Input() items: ProductCardConfig[] = []; + + @Output() productSelected = new EventEmitter(); + + onProductClick(item: ProductCardConfig): void { + this.productSelected.emit(item); + } +}