diff --git a/src/app/core/layout/store-layout/store-footer/store-footer.component.html b/src/app/core/layout/store-layout/store-footer/store-footer.component.html index 642eb62..ef60575 100644 --- a/src/app/core/layout/store-layout/store-footer/store-footer.component.html +++ b/src/app/core/layout/store-layout/store-footer/store-footer.component.html @@ -5,12 +5,19 @@
- - SONDER + @if (logoUrl && logoLoaded) { + + } @else { + + }
diff --git a/src/app/core/layout/store-layout/store-footer/store-footer.component.scss b/src/app/core/layout/store-layout/store-footer/store-footer.component.scss index 9dbd2c8..6eea43d 100644 --- a/src/app/core/layout/store-layout/store-footer/store-footer.component.scss +++ b/src/app/core/layout/store-layout/store-footer/store-footer.component.scss @@ -6,6 +6,42 @@ min-width: 10rem; } +.store-layout__brand-logo-shell { + position: relative; + width: min(100%, 12rem); + height: 2.75rem; +} + +.store-layout__brand-logo { + display: block; + width: 100%; + height: 100%; + max-height: 2.75rem; + object-fit: contain; +} + +.store-layout__brand-logo--preload { + opacity: 0; + position: absolute; + inset: 0; + pointer-events: none; +} + +.store-layout__brand-logo-skeleton { + display: block; + width: 100%; + height: 100%; + border-radius: 0.75rem; + background: linear-gradient( + 90deg, + rgba(255, 255, 255, 0.12) 0%, + rgba(255, 255, 255, 0.24) 50%, + rgba(255, 255, 255, 0.12) 100% + ); + background-size: 200% 100%; + animation: store-footer-logo-skeleton 1.2s ease-in-out infinite; +} + .store-layout__brand-mark { display: grid; place-items: center; @@ -33,6 +69,16 @@ color: #f8f8f8; } +@keyframes store-footer-logo-skeleton { + from { + background-position: 200% 0; + } + + to { + background-position: -200% 0; + } +} + @media (max-width: 767.98px) { .store-layout__brand-slot { min-width: 0; diff --git a/src/app/core/layout/store-layout/store-footer/store-footer.component.ts b/src/app/core/layout/store-layout/store-footer/store-footer.component.ts index 6856258..4de54a7 100644 --- a/src/app/core/layout/store-layout/store-footer/store-footer.component.ts +++ b/src/app/core/layout/store-layout/store-footer/store-footer.component.ts @@ -17,7 +17,25 @@ export type StoreSocialLink = { styleUrl: './store-footer.component.scss' }) export class StoreFooterComponent { + private logoUrlValue: string | null = null; + @Input({ required: true }) currentYear = new Date().getFullYear(); @Input({ required: true }) footerSections: StoreFooterSection[] = []; @Input({ required: true }) socialLinks: StoreSocialLink[] = []; + + @Input() + set logoUrl(value: string | null) { + this.logoUrlValue = value; + this.logoLoaded = false; + } + + get logoUrl(): string | null { + return this.logoUrlValue; + } + + protected logoLoaded = false; + + protected onLogoLoad(): void { + this.logoLoaded = true; + } } diff --git a/src/app/core/layout/store-layout/store-header/store-header.component.html b/src/app/core/layout/store-layout/store-header/store-header.component.html index d317329..86abb35 100644 --- a/src/app/core/layout/store-layout/store-header/store-header.component.html +++ b/src/app/core/layout/store-layout/store-header/store-header.component.html @@ -4,10 +4,19 @@
- - SONDER + @if (logoUrl && logoLoaded) { + + } @else { + + }
diff --git a/src/app/core/layout/store-layout/store-header/store-header.component.scss b/src/app/core/layout/store-layout/store-header/store-header.component.scss index a44ea69..f36a635 100644 --- a/src/app/core/layout/store-layout/store-header/store-header.component.scss +++ b/src/app/core/layout/store-layout/store-header/store-header.component.scss @@ -2,23 +2,34 @@ min-width: 10rem; } -.store-layout__brand-mark { - display: grid; - place-items: center; - width: 2.75rem; +.store-layout__brand-logo-shell { + position: relative; + width: min(100%, 12rem); height: 2.75rem; - border: 1px dashed #bbbbbb; - border-radius: 999px; - font-size: 1.05rem; - font-weight: 700; - letter-spacing: 0.04em; - color: #373737; } -.store-layout__brand-text { - font-size: 1.8rem; - font-weight: 700; - letter-spacing: -0.08em; +.store-layout__brand-logo { + display: block; + width: 100%; + height: 100%; + object-fit: contain; +} + +.store-layout__brand-logo--preload { + opacity: 0; + position: absolute; + inset: 0; + pointer-events: none; +} + +.store-layout__brand-logo-skeleton { + display: block; + width: 100%; + height: 100%; + border-radius: 0.75rem; + background: linear-gradient(90deg, rgba(0, 0, 0, 0.08) 0%, rgba(0, 0, 0, 0.14) 50%, rgba(0, 0, 0, 0.08) 100%); + background-size: 200% 100%; + animation: store-header-logo-skeleton 1.2s ease-in-out infinite; } .store-layout__search { @@ -31,15 +42,21 @@ letter-spacing: 0.02em; } +@keyframes store-header-logo-skeleton { + from { + background-position: 200% 0; + } + + to { + background-position: -200% 0; + } +} + @media (max-width: 767.98px) { .store-layout__brand-slot { min-width: 0; } - .store-layout__brand-text { - font-size: 1.45rem; - } - .store-layout__search { min-width: 0; } diff --git a/src/app/core/layout/store-layout/store-header/store-header.component.ts b/src/app/core/layout/store-layout/store-header/store-header.component.ts index 44e18a1..0157ac1 100644 --- a/src/app/core/layout/store-layout/store-header/store-header.component.ts +++ b/src/app/core/layout/store-layout/store-header/store-header.component.ts @@ -12,5 +12,23 @@ export type StoreHeaderAction = { styleUrl: './store-header.component.scss' }) export class StoreHeaderComponent { + private logoUrlValue: string | null = null; + @Input({ required: true }) headerActions: StoreHeaderAction[] = []; + + @Input() + set logoUrl(value: string | null) { + this.logoUrlValue = value; + this.logoLoaded = false; + } + + get logoUrl(): string | null { + return this.logoUrlValue; + } + + protected logoLoaded = false; + + protected onLogoLoad(): void { + this.logoLoaded = true; + } } diff --git a/src/app/core/layout/store-layout/store-layout.component.html b/src/app/core/layout/store-layout/store-layout.component.html index f7e1639..79b0636 100644 --- a/src/app/core/layout/store-layout/store-layout.component.html +++ b/src/app/core/layout/store-layout/store-layout.component.html @@ -1,5 +1,5 @@
- +
@@ -9,5 +9,6 @@ [currentYear]="currentYear" [footerSections]="footerSections" [socialLinks]="socialLinks" + [logoUrl]="tenant()?.footer_logo ?? null" />
diff --git a/src/app/core/layout/store-layout/store-layout.component.ts b/src/app/core/layout/store-layout/store-layout.component.ts index 764d17f..932864a 100644 --- a/src/app/core/layout/store-layout/store-layout.component.ts +++ b/src/app/core/layout/store-layout/store-layout.component.ts @@ -1,5 +1,6 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { RouterOutlet } from '@angular/router'; +import { TenantService } from '../../services/tenant.service'; import { StoreFooterComponent, StoreFooterSection, StoreSocialLink } from './store-footer/store-footer.component'; import { StoreHeaderComponent, StoreHeaderAction } from './store-header/store-header.component'; @@ -10,7 +11,10 @@ import { StoreHeaderComponent, StoreHeaderAction } from './store-header/store-he styleUrl: './store-layout.component.scss' }) export class StoreLayoutComponent { + private readonly tenantService = inject(TenantService); + protected readonly currentYear = new Date().getFullYear(); + protected readonly tenant = this.tenantService.tenant; protected readonly headerActions: StoreHeaderAction[] = [ { diff --git a/src/app/core/services/tenant.service.spec.ts b/src/app/core/services/tenant.service.spec.ts index 5585a09..2a61dc2 100644 --- a/src/app/core/services/tenant.service.spec.ts +++ b/src/app/core/services/tenant.service.spec.ts @@ -44,6 +44,7 @@ describe('TenantService', () => { await expect(bootstrapPromise).resolves.toEqual(response.data); expect(service.tenant()).toEqual(response.data); + expect(service.getTenant()).toEqual(response.data); httpController.verify(); }); @@ -81,6 +82,7 @@ describe('TenantService', () => { await expect(service.bootstrap()).resolves.toBeNull(); expect(service.tenant()).toBeNull(); + expect(service.getTenant()).toBeNull(); httpController.expectNone(() => true); httpController.verify(); diff --git a/src/app/core/services/tenant.service.ts b/src/app/core/services/tenant.service.ts index 018b3b6..565a2e2 100644 --- a/src/app/core/services/tenant.service.ts +++ b/src/app/core/services/tenant.service.ts @@ -16,6 +16,10 @@ export class TenantService { readonly tenant = this.tenantState.asReadonly(); + getTenant(): Tenant | null { + return this.tenantState(); + } + async bootstrap(): Promise { if (!isPlatformBrowser(this.platformId)) { return null;