feat: initial rich-text-elements-ui library implementation
TipTap-powered rich text editing library with WYSIWYG editor, markdown editor, template editor, collaboration support (Yjs), mentions, track changes, comments, code blocks, and table insertion. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
96
src/components/rt-toolbar/rt-toolbar.component.ts
Normal file
96
src/components/rt-toolbar/rt-toolbar.component.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
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<RtToolbarGroup[]>();
|
||||
readonly activeMarks = input.required<RtActiveMarks>();
|
||||
readonly activeNodes = input.required<RtActiveNodes>();
|
||||
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) });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user