diff --git a/src/app/features/project-editor/components/html-editor/marketplace-html-editor.component.html b/src/app/features/project-editor/components/html-editor/marketplace-html-editor.component.html new file mode 100644 index 0000000..57e8f4d --- /dev/null +++ b/src/app/features/project-editor/components/html-editor/marketplace-html-editor.component.html @@ -0,0 +1,24 @@ +
+
+ @for (item of toolbar; track item.id) { + + } + +
+ + @if (showCode()) { + + } @else { +
+ } +
diff --git a/src/app/features/project-editor/components/html-editor/marketplace-html-editor.component.scss b/src/app/features/project-editor/components/html-editor/marketplace-html-editor.component.scss new file mode 100644 index 0000000..5696ff0 --- /dev/null +++ b/src/app/features/project-editor/components/html-editor/marketplace-html-editor.component.scss @@ -0,0 +1,27 @@ +.html-editor { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.html-editor-toolbar { + display: flex; + flex-wrap: wrap; + gap: 0.25rem; +} + +.html-editor-surface { + min-height: 160px; + border: 1px solid var(--border, #ccc); + border-radius: 4px; + padding: 0.5rem; + overflow-y: auto; +} + +.html-editor-code { + min-height: 160px; + font-family: monospace; + border: 1px solid var(--border, #ccc); + border-radius: 4px; + padding: 0.5rem; +} diff --git a/src/app/features/project-editor/components/html-editor/marketplace-html-editor.component.ts b/src/app/features/project-editor/components/html-editor/marketplace-html-editor.component.ts new file mode 100644 index 0000000..c0df2f2 --- /dev/null +++ b/src/app/features/project-editor/components/html-editor/marketplace-html-editor.component.ts @@ -0,0 +1,88 @@ +import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild, signal } from '@angular/core'; + +export interface HtmlEditorToolbarCommand { + id: string; + label: string; + command: string; + value?: string; +} + +export const HTML_EDITOR_TOOLBAR: HtmlEditorToolbarCommand[] = [ + { id: 'bold', label: 'B', command: 'bold' }, + { id: 'italic', label: 'I', command: 'italic' }, + { id: 'underline', label: 'U', command: 'underline' }, + { id: 'h2', label: 'H2', command: 'formatBlock', value: 'H2' }, + { id: 'h3', label: 'H3', command: 'formatBlock', value: 'H3' }, + { id: 'ul', label: 'List', command: 'insertUnorderedList' }, + { id: 'ol', label: '1,2,3', command: 'insertOrderedList' }, + { id: 'link', label: 'Link', command: 'createLink' }, + { id: 'image', label: 'Image', command: 'insertImage' }, + { id: 'table', label: 'Table', command: 'insertHTML', value: '
  
' }, +]; + +@Component({ + selector: 'app-marketplace-html-editor', + standalone: true, + imports: [], + templateUrl: './marketplace-html-editor.component.html', + styleUrls: ['./marketplace-html-editor.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class MarketplaceHtmlEditorComponent implements OnChanges { + @Input() html = ''; + @Output() htmlChange = new EventEmitter(); + @ViewChild('surface', { static: true }) surface!: ElementRef; + + readonly toolbar = HTML_EDITOR_TOOLBAR; + readonly showCode = signal(false); + readonly codeValue = signal(''); + + ngOnChanges(changes: SimpleChanges): void { + if (changes['html'] && this.surface && this.surface.nativeElement.innerHTML !== (this.html || '')) { + this.surface.nativeElement.innerHTML = this.html || ''; + } + } + + runCommand(item: HtmlEditorToolbarCommand): void { + this.surface.nativeElement.focus(); + if (item.command === 'createLink') { + const url = window.prompt('URL'); + if (!url) { + return; + } + document.execCommand('createLink', false, url); + } else if (item.command === 'insertImage') { + const url = window.prompt('Image URL'); + if (!url) { + return; + } + document.execCommand('insertImage', false, url); + } else { + document.execCommand(item.command, false, item.value); + } + this.emitChange(); + } + + onInput(): void { + this.emitChange(); + } + + toggleCode(): void { + if (!this.showCode()) { + this.codeValue.set(this.surface.nativeElement.innerHTML); + this.showCode.set(true); + return; + } + this.surface.nativeElement.innerHTML = this.codeValue(); + this.showCode.set(false); + this.emitChange(); + } + + updateCode(value: string): void { + this.codeValue.set(value); + } + + private emitChange(): void { + this.htmlChange.emit(this.surface.nativeElement.innerHTML); + } +}