import { Component, ChangeDetectionStrategy, input, output, inject, } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { ButtonComponent, IconComponent, IconRegistry, SelectComponent, type SelectOption, TooltipDirective } from '@sda/base-ui'; import type { RtToolbarGroup, RtToolbarAction } from '../../types/toolbar.types'; import type { RtActiveMarks, RtActiveNodes } from '../../types/editor.types'; import { registerRtIcons } from '../../utils/icons.utils'; @Component({ selector: 'rt-toolbar', standalone: true, imports: [CommonModule, FormsModule, ButtonComponent, IconComponent, SelectComponent, TooltipDirective], templateUrl: './rt-toolbar.component.html', styleUrl: './rt-toolbar.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) export class RtToolbarComponent { readonly groups = input.required(); readonly activeMarks = input.required(); readonly activeNodes = input.required(); readonly canUndo = input(false); readonly canRedo = input(false); readonly action = output<{ action: RtToolbarAction; value?: string }>(); readonly headingOptions: SelectOption[] = [ { value: '0', label: 'Normal text' }, { value: '1', label: 'Heading 1' }, { value: '2', label: 'Heading 2' }, { value: '3', label: 'Heading 3' }, { value: '4', label: 'Heading 4' }, ]; constructor() { registerRtIcons(inject(IconRegistry)); } /** Check if a toolbar action is currently active */ isActive(actionName: RtToolbarAction): boolean { const marks = this.activeMarks(); const nodes = this.activeNodes(); switch (actionName) { case 'bold': return marks.bold; case 'italic': return marks.italic; case 'underline': return marks.underline; case 'strikethrough': return marks.strike; case 'code': return marks.code; case 'subscript': return marks.subscript; case 'superscript': return marks.superscript; case 'link': return marks.link; case 'highlight-color': return marks.highlight; case 'bullet-list': return nodes.bulletList; case 'ordered-list': return nodes.orderedList; case 'task-list': return nodes.taskList; case 'blockquote': return nodes.blockquote; case 'code-block': return nodes.codeBlock; case 'align-left': return nodes.textAlign === 'left'; case 'align-center': return nodes.textAlign === 'center'; case 'align-right': return nodes.textAlign === 'right'; case 'align-justify': return nodes.textAlign === 'justify'; default: return false; } } /** Check if a toolbar action is disabled */ isDisabled(actionName: RtToolbarAction): boolean { switch (actionName) { case 'undo': return !this.canUndo(); case 'redo': return !this.canRedo(); default: return false; } } /** Get the current heading level as a string value */ getCurrentHeadingValue(): string { const heading = this.activeNodes().heading; return heading ? String(heading) : '0'; } /** Emit action event */ onAction(actionName: RtToolbarAction, value?: string): void { this.action.emit({ action: actionName, value }); } /** Handle heading select change */ onHeadingChange(value: string | number): void { this.action.emit({ action: 'heading', value: String(value) }); } }