feat(project-editor): route-driven tabs under /edit/:section
This commit is contained in:
@@ -40,12 +40,22 @@ const coreRoutes: Routes = [
|
|||||||
loadComponent: () => import('./features/website/catalog/containers/catalog-container.component').then(m => m.CatalogContainerComponent)
|
loadComponent: () => import('./features/website/catalog/containers/catalog-container.component').then(m => m.CatalogContainerComponent)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'builder',
|
path: 'edit/:section',
|
||||||
loadComponent: () => import('./features/project-editor/pages/project-editor-page.component').then(m => m.ProjectEditorPageComponent)
|
loadComponent: () => import('./features/project-editor/pages/project-editor-page.component').then(m => m.ProjectEditorPageComponent)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'edit',
|
||||||
|
redirectTo: 'edit/general',
|
||||||
|
pathMatch: 'full'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'builder',
|
||||||
|
redirectTo: 'edit/general',
|
||||||
|
pathMatch: 'full'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'project-editor',
|
path: 'project-editor',
|
||||||
redirectTo: 'builder',
|
redirectTo: 'edit/general',
|
||||||
pathMatch: 'full'
|
pathMatch: 'full'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
<nav class="editor-nav" [attr.aria-label]="'builder.title' | translate">
|
<nav class="editor-nav" [attr.aria-label]="'builder.title' | translate">
|
||||||
@for (section of sections; track section.id) {
|
@for (section of sections; track section.id) {
|
||||||
<button type="button" [class.active]="active === section.id" (click)="activeChange.emit(section.id)">{{ section.label | translate }}</button>
|
<a
|
||||||
|
[routerLink]="['/edit', section.id] | langRoute"
|
||||||
|
routerLinkActive="active"
|
||||||
|
class="editor-nav-link"
|
||||||
|
>{{ section.label | translate }}</a>
|
||||||
}
|
}
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button,
|
||||||
|
.editor-nav-link {
|
||||||
min-height: 40px;
|
min-height: 40px;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
border: 1px solid var(--border-color, #d3dad9);
|
border: 1px solid var(--border-color, #d3dad9);
|
||||||
@@ -15,8 +16,15 @@ button {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.active {
|
button.active,
|
||||||
|
.editor-nav-link.active {
|
||||||
border-color: #497671;
|
border-color: #497671;
|
||||||
background: #497671;
|
background: #497671;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.editor-nav-link {
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 40px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
|
import { RouterLink, RouterLinkActive } from '@angular/router';
|
||||||
import { TranslatePipe } from '../../../i18n/translate.pipe';
|
import { TranslatePipe } from '../../../i18n/translate.pipe';
|
||||||
|
import { LangRoutePipe } from '../../../pipes/lang-route.pipe';
|
||||||
import { ProjectEditorSectionId } from '../models/project-editor.model';
|
import { ProjectEditorSectionId } from '../models/project-editor.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-project-editor-nav',
|
selector: 'app-project-editor-nav',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [TranslatePipe],
|
imports: [TranslatePipe, RouterLink, RouterLinkActive, LangRoutePipe],
|
||||||
templateUrl: './project-editor-nav.component.html',
|
templateUrl: './project-editor-nav.component.html',
|
||||||
styleUrls: ['./project-editor-nav.component.scss'],
|
styleUrls: ['./project-editor-nav.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
})
|
})
|
||||||
export class ProjectEditorNavComponent {
|
export class ProjectEditorNavComponent {
|
||||||
@Input() active: ProjectEditorSectionId = 'general';
|
|
||||||
@Output() activeChange = new EventEmitter<ProjectEditorSectionId>();
|
|
||||||
|
|
||||||
readonly sections: Array<{ id: ProjectEditorSectionId; label: string }> = [
|
readonly sections: Array<{ id: ProjectEditorSectionId; label: string }> = [
|
||||||
{ id: 'general', label: 'builder.general' },
|
{ id: 'general', label: 'builder.general' },
|
||||||
{ id: 'branding', label: 'builder.branding' },
|
{ id: 'branding', label: 'builder.branding' },
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<h1>{{ 'builder.title' | translate }}</h1>
|
<h1>{{ 'builder.title' | translate }}</h1>
|
||||||
<p>{{ 'builder.subtitle' | translate }}</p>
|
<p>{{ 'builder.subtitle' | translate }}</p>
|
||||||
</div>
|
</div>
|
||||||
<app-project-editor-nav [active]="activeSection()" (activeChange)="facade.setActiveSection($event)" />
|
<app-project-editor-nav />
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@if (!bootstrap()) {
|
@if (!bootstrap()) {
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, effect, inject } from '@angular/core';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { toSignal } from '@angular/core/rxjs-interop';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
import { ProjectEditorFacade } from '../facade/project-editor.facade';
|
import { ProjectEditorFacade } from '../facade/project-editor.facade';
|
||||||
import { ProjectEditorNavComponent } from '../components/project-editor-nav.component';
|
import { ProjectEditorNavComponent } from '../components/project-editor-nav.component';
|
||||||
import { ProjectEditorGeneralSectionComponent } from '../sections/general-section.component';
|
import { ProjectEditorGeneralSectionComponent } from '../sections/general-section.component';
|
||||||
@@ -12,6 +15,11 @@ import { ProjectEditorFeaturesSectionComponent } from '../sections/features-sect
|
|||||||
import { ProjectEditorPreviewSectionComponent } from '../sections/preview-section.component';
|
import { ProjectEditorPreviewSectionComponent } from '../sections/preview-section.component';
|
||||||
import { TranslatePipe } from '../../../i18n/translate.pipe';
|
import { TranslatePipe } from '../../../i18n/translate.pipe';
|
||||||
import { StaticPagesEditorComponent } from '../../content-management/components/static-pages-editor.component';
|
import { StaticPagesEditorComponent } from '../../content-management/components/static-pages-editor.component';
|
||||||
|
import { ProjectEditorSectionId } from '../models/project-editor.model';
|
||||||
|
|
||||||
|
const KNOWN_SECTIONS: ProjectEditorSectionId[] = [
|
||||||
|
'general', 'branding', 'theme', 'header', 'footer', 'homepage', 'widgets', 'static-pages', 'features', 'preview'
|
||||||
|
];
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-project-editor-page',
|
selector: 'app-project-editor-page',
|
||||||
@@ -36,10 +44,22 @@ import { StaticPagesEditorComponent } from '../../content-management/components/
|
|||||||
})
|
})
|
||||||
export class ProjectEditorPageComponent {
|
export class ProjectEditorPageComponent {
|
||||||
readonly facade = inject(ProjectEditorFacade);
|
readonly facade = inject(ProjectEditorFacade);
|
||||||
|
private readonly route = inject(ActivatedRoute);
|
||||||
readonly bootstrap = this.facade.bootstrap;
|
readonly bootstrap = this.facade.bootstrap;
|
||||||
readonly activeSection = this.facade.activeSection;
|
readonly activeSection = this.facade.activeSection;
|
||||||
|
|
||||||
|
private readonly routeSection = toSignal(
|
||||||
|
this.route.paramMap.pipe(map(params => params.get('section') as ProjectEditorSectionId | null)),
|
||||||
|
{ initialValue: null }
|
||||||
|
);
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.facade.loadBootstrap();
|
this.facade.loadBootstrap();
|
||||||
|
effect(() => {
|
||||||
|
const section = this.routeSection();
|
||||||
|
if (section && KNOWN_SECTIONS.includes(section)) {
|
||||||
|
this.facade.setActiveSection(section);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user