diff --git a/src/app/features/project-editor/components/live-preview/builder-live-preview.component.html b/src/app/features/project-editor/components/live-preview/builder-live-preview.component.html new file mode 100644 index 0000000..acc83e7 --- /dev/null +++ b/src/app/features/project-editor/components/live-preview/builder-live-preview.component.html @@ -0,0 +1,56 @@ +@if (bootstrap(); as bootstrap) { +
{{ 'builder.livePreviewEmpty' | translate }}
+ } +{{ 'builder.livePreviewEmpty' | translate }}
+} diff --git a/src/app/features/project-editor/components/live-preview/builder-live-preview.component.scss b/src/app/features/project-editor/components/live-preview/builder-live-preview.component.scss new file mode 100644 index 0000000..0b4b70d --- /dev/null +++ b/src/app/features/project-editor/components/live-preview/builder-live-preview.component.scss @@ -0,0 +1,160 @@ +.builder-live-preview { + display: flex; + flex-direction: column; + gap: var(--space-xs, 0.25rem); + border: 1px solid var(--border-color, #e4e4e7); + border-radius: var(--radius-lg, 10px); + overflow: hidden; + background: var(--bg-primary, #fff); +} + +.builder-live-preview__header { + display: flex; + align-items: center; + gap: var(--space-sm, 0.5rem); + padding: var(--space-sm, 0.5rem) var(--space-md, 1rem); + border-bottom: 1px solid var(--border-color, #e4e4e7); + transition: box-shadow var(--transition-fast, 120ms ease); +} + +.builder-live-preview__header--active { + box-shadow: inset 0 0 0 2px var(--primary-color, #2f6e5d); +} + +.builder-live-preview__logo { + width: 24px; + height: 24px; + border-radius: var(--radius-sm, 4px); + object-fit: contain; + transition: outline-color var(--transition-fast, 120ms ease); + outline: 2px solid transparent; + outline-offset: 2px; +} + +.builder-live-preview__logo--placeholder { + background: var(--bg-secondary, #f4f4f5); +} + +.builder-live-preview__logo--active { + outline-color: var(--primary-color, #2f6e5d); +} + +.builder-live-preview__title { + font-size: var(--font-size-sm, 0.8125rem); + font-weight: var(--font-weight-semibold, 600); + color: var(--text-primary, #1f322d); + border-radius: var(--radius-sm, 4px); + transition: box-shadow var(--transition-fast, 120ms ease); +} + +.builder-live-preview__title--active { + box-shadow: inset 0 0 0 2px var(--primary-color, #2f6e5d); + padding: 0 var(--space-xs, 0.25rem); +} + +.builder-live-preview__hero { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: var(--space-sm, 0.5rem); + padding: var(--space-lg, 1.5rem) var(--space-md, 1rem); + transition: box-shadow var(--transition-fast, 120ms ease); +} + +.builder-live-preview__hero--active { + box-shadow: inset 0 0 0 2px var(--primary-color, #2f6e5d); +} + +.builder-live-preview__hero-image { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 32px; + border-radius: var(--radius-md, 6px); + background: var(--bg-primary, #fff); + color: var(--text-secondary, #6b7280); + outline: 2px solid transparent; + outline-offset: 2px; +} + +.builder-live-preview__hero-image--active { + outline-color: var(--primary-color, #2f6e5d); +} + +.builder-live-preview__button { + padding: var(--space-xs, 0.25rem) var(--space-md, 1rem); + border-radius: var(--radius-md, 6px); + color: #fff; + font-size: var(--font-size-xs, 0.75rem); + font-weight: var(--font-weight-semibold, 600); + outline: 2px solid transparent; + outline-offset: 2px; + transition: outline-color var(--transition-fast, 120ms ease); +} + +.builder-live-preview__button--active { + outline-color: var(--primary-color, #2f6e5d); +} + +.builder-live-preview__blocks { + display: flex; + flex-wrap: wrap; + gap: var(--space-xs, 0.25rem); + padding: var(--space-sm, 0.5rem) var(--space-md, 1rem); + border-radius: var(--radius-md, 6px); + transition: box-shadow var(--transition-fast, 120ms ease); +} + +.builder-live-preview__blocks--active { + box-shadow: inset 0 0 0 2px var(--primary-color, #2f6e5d); +} + +.builder-live-preview__block { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 28px; + border-radius: var(--radius-sm, 4px); + background: var(--bg-secondary, #f4f4f5); + color: var(--text-secondary, #6b7280); + outline: 2px solid transparent; + outline-offset: 2px; + transition: outline-color var(--transition-fast, 120ms ease), opacity var(--transition-fast, 120ms ease); +} + +.builder-live-preview__block--hidden { + opacity: 0.35; +} + +.builder-live-preview__block--active { + outline-color: var(--primary-color, #2f6e5d); +} + +.builder-live-preview__footer { + height: 16px; + background: var(--bg-secondary, #f4f4f5); +} + +.builder-live-preview__empty { + margin: 0; + padding: var(--space-md, 1rem); + font-size: var(--font-size-sm, 0.8125rem); + color: var(--text-secondary, #6b7280); + text-align: center; +} + +@media (prefers-reduced-motion: reduce) { + .builder-live-preview__header, + .builder-live-preview__logo, + .builder-live-preview__title, + .builder-live-preview__hero, + .builder-live-preview__hero-image, + .builder-live-preview__button, + .builder-live-preview__blocks, + .builder-live-preview__block { + transition: none; + } +} diff --git a/src/app/features/project-editor/components/live-preview/builder-live-preview.component.ts b/src/app/features/project-editor/components/live-preview/builder-live-preview.component.ts new file mode 100644 index 0000000..2ce8891 --- /dev/null +++ b/src/app/features/project-editor/components/live-preview/builder-live-preview.component.ts @@ -0,0 +1,57 @@ +import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core'; +import { ProjectEditorFacade } from '../../facade/project-editor.facade'; +import { PreviewHighlightService } from '../../../../shared/services/preview-highlight.service'; +import { TranslatePipe } from '../../../../i18n/translate.pipe'; +import { IconComponent } from '../../../../shared/ui/icon/icon.component'; +import { AppIconName } from '../../../../shared/ui/icon/icon-registry'; + +const BLOCK_ICON_BY_TYPE: Record{{ 'common.loading' | translate }}
} @else { -{{ blockDescription(section.type) }}
{{ hint() }}
} + + @if (usedBy(); as targets) { + @if (targets.length) { ++ @if (usedByLabel()) { + {{ usedByLabel() }} + } + {{ targets.join(', ') }} +
+ } + }