feat: Page Editor UX Phase 2 - unsaved changes panel, property search, reset property, empty states
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
- ProjectEditorFacade.resetField(key): field-level revert-to-original, reusing getByPath + a new immutable setByPath (write-side counterpart, scalar/object dot-paths only). - app-form-field gains showReset/resetLabel/(resetClicked) - a reusable per-field reset affordance, wired on branding logo/title and theme primary/background color. - Save bar: unsaved-changes count is now clickable, expanding a field-level diff list (reuses facade.changeSummary(), already built for the Preview tab) with jump-to-section links. - BuilderPropertySearchComponent: filters EditorSchemaService.all() by translated label/hint, jumps to the owning section - no new registry, reuses the existing schema. - navigation-section: app-empty-state (existing component) added for empty header/footer link lists. - Reset Section and Draft/Published badge were already implemented; not touched. - New copy added as translation keys (en/ru/hy).
This commit is contained in:
@@ -1,11 +1,19 @@
|
||||
<div class="app-form-field">
|
||||
@if (label()) {
|
||||
<label class="app-form-field__label" [for]="fieldId">
|
||||
{{ label() }}
|
||||
@if (required()) {
|
||||
<span class="app-form-field__required" aria-hidden="true">*</span>
|
||||
<div class="app-form-field__label-row">
|
||||
<label class="app-form-field__label" [for]="fieldId">
|
||||
{{ label() }}
|
||||
@if (required()) {
|
||||
<span class="app-form-field__required" aria-hidden="true">*</span>
|
||||
}
|
||||
</label>
|
||||
@if (showReset()) {
|
||||
<button type="button" class="app-form-field__reset" [attr.aria-label]="resetLabel()" (click)="resetClicked.emit()">
|
||||
<app-icon name="refresh" [size]="12" />
|
||||
{{ resetLabel() }}
|
||||
</button>
|
||||
}
|
||||
</label>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="app-form-field__control">
|
||||
|
||||
@@ -4,12 +4,40 @@
|
||||
gap: var(--space-xs, 0.25rem);
|
||||
}
|
||||
|
||||
.app-form-field__label-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-sm, 0.5rem);
|
||||
}
|
||||
|
||||
.app-form-field__label {
|
||||
font-size: var(--font-size-base, 0.875rem);
|
||||
font-weight: var(--font-weight-semibold, 600);
|
||||
color: var(--text-primary, #1f322d);
|
||||
}
|
||||
|
||||
.app-form-field__reset {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.2rem;
|
||||
border: none;
|
||||
background: none;
|
||||
padding: 0;
|
||||
color: var(--text-secondary, #6b7280);
|
||||
font-size: var(--font-size-xs, 0.75rem);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: var(--primary-color, #2f6e5d);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid var(--primary-color, #2f6e5d);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.app-form-field__required {
|
||||
color: var(--error-color, #c0392b);
|
||||
margin-left: 0.125rem;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core';
|
||||
import { IconComponent } from '../icon/icon.component';
|
||||
|
||||
let nextFormFieldId = 0;
|
||||
|
||||
@@ -14,6 +15,7 @@ export abstract class FormFieldContext {
|
||||
@Component({
|
||||
selector: 'app-form-field',
|
||||
standalone: true,
|
||||
imports: [IconComponent],
|
||||
templateUrl: './form-field.component.html',
|
||||
styleUrl: './form-field.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
@@ -27,6 +29,10 @@ export class FormFieldComponent implements FormFieldContext {
|
||||
/** Optional "Where is this used?" list, e.g. ['Buttons', 'Links', 'Badges']. Already-translated strings. */
|
||||
readonly usedBy = input<readonly string[] | null>(null);
|
||||
readonly usedByLabel = input<string | null>(null);
|
||||
/** Shows a small "reset to original" button next to the label when the field has been changed. */
|
||||
readonly showReset = input(false);
|
||||
readonly resetLabel = input<string | null>(null);
|
||||
readonly resetClicked = output<void>();
|
||||
|
||||
readonly fieldId = `app-form-field-${++nextFormFieldId}`;
|
||||
protected readonly hintId = `${this.fieldId}-hint`;
|
||||
|
||||
Reference in New Issue
Block a user