diff --git a/.github/workflows/announce.yml b/.github/workflows/announce.yml
index accc280bd..a023c5206 100644
--- a/.github/workflows/announce.yml
+++ b/.github/workflows/announce.yml
@@ -18,6 +18,7 @@ jobs:
echo "name=$(node -p "require('./projects/editor/package.json').name")" >> $GITHUB_OUTPUT
- name: Announce to Telegram
+ if: ${{ !contains(steps.info.outputs.version, 'rc') }}
uses: taiga-family/ci/actions/messenger/telegram/announce@v1.66.3
with:
chatId: ${{ secrets.TAIGA_TELEGRAM_CHAT_ID }}
diff --git a/projects/demo-playwright/utils/goto.ts b/projects/demo-playwright/utils/goto.ts
index b1f345beb..b971dbefc 100644
--- a/projects/demo-playwright/utils/goto.ts
+++ b/projects/demo-playwright/utils/goto.ts
@@ -43,11 +43,15 @@ export async function tuiGoto(
await expect(page.locator('app')).toHaveClass(/_loaded/, {timeout: 15_000});
if (hideHeader) {
- await page.locator('[tuidocheader]').evaluate((el) => el.remove());
+ (await page.locator('[tuidocheader]').all()).forEach((e) =>
+ e.evaluate((el) => el.remove()),
+ );
}
if (hideNavigation) {
- await page.locator('tui-doc-navigation').evaluate((el) => el.remove());
+ (await page.locator('tui-doc-navigation').all()).forEach((e) =>
+ e.evaluate((el) => el.remove()),
+ );
}
await page.waitForTimeout(1000);
diff --git a/projects/demo/src/app/app.pages.ts b/projects/demo/src/app/app.pages.ts
index fa8bcd83b..101a51e01 100644
--- a/projects/demo/src/app/app.pages.ts
+++ b/projects/demo/src/app/app.pages.ts
@@ -223,6 +223,13 @@ export const DEMO_PAGES: TuiDocRoutePages = [
'editor, toolbar, floating, wysiwyg, редактор, текст, подсветка, html, rich, text',
route: `/${TuiDemoPath.ToolbarFloating}`,
},
+ {
+ section: 'Documentation',
+ title: 'Shared',
+ keywords:
+ 'editor, toolbar, shared, multiple, один на всех, html, rich, text',
+ route: `/${TuiDemoPath.ToolbarShared}`,
+ },
],
},
];
diff --git a/projects/demo/src/app/app.routes.ts b/projects/demo/src/app/app.routes.ts
index 6b7bd1bb1..46c7bfafe 100644
--- a/projects/demo/src/app/app.routes.ts
+++ b/projects/demo/src/app/app.routes.ts
@@ -144,6 +144,11 @@ export const routes: Routes = [
loadComponent: async () => import('./pages/toolbar/floating'),
title: 'Editor — Toolbar',
}),
+ route({
+ path: TuiDemoPath.ToolbarShared,
+ loadComponent: async () => import('./pages/toolbar/shared'),
+ title: 'Editor — Toolbar',
+ }),
{
path: '**',
redirectTo: TuiDemoPath.StarterKit,
diff --git a/projects/demo/src/app/pages/toolbar/shared/examples/1/index.html b/projects/demo/src/app/pages/toolbar/shared/examples/1/index.html
new file mode 100644
index 000000000..8ab59bfd5
--- /dev/null
+++ b/projects/demo/src/app/pages/toolbar/shared/examples/1/index.html
@@ -0,0 +1,47 @@
+
+
+
+
+ Output
+
+
+ {{group.value|json}}
+
+
+
diff --git a/projects/demo/src/app/pages/toolbar/shared/examples/1/index.less b/projects/demo/src/app/pages/toolbar/shared/examples/1/index.less
new file mode 100644
index 000000000..5f63bb747
--- /dev/null
+++ b/projects/demo/src/app/pages/toolbar/shared/examples/1/index.less
@@ -0,0 +1,17 @@
+:host {
+ display: flex;
+ flex-direction: column;
+ gap: 0;
+}
+
+tui-toolbar.toolbar {
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ background: var(--tui-base-01);
+}
+
+tui-editor.editor {
+ min-height: auto;
+ border-radius: 0;
+}
diff --git a/projects/demo/src/app/pages/toolbar/shared/examples/1/index.ts b/projects/demo/src/app/pages/toolbar/shared/examples/1/index.ts
new file mode 100644
index 000000000..fc09ab82b
--- /dev/null
+++ b/projects/demo/src/app/pages/toolbar/shared/examples/1/index.ts
@@ -0,0 +1,49 @@
+import {JsonPipe} from '@angular/common';
+import {
+ ChangeDetectionStrategy,
+ Component,
+ Injector,
+ ViewEncapsulation,
+} from '@angular/core';
+import {FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms';
+import type {AbstractTuiEditor} from '@taiga-ui/editor';
+import {
+ TUI_EDITOR_DEFAULT_EXTENSIONS,
+ TUI_EDITOR_DEFAULT_TOOLS,
+ TUI_EDITOR_EXTENSIONS,
+ TuiEditor,
+ TuiToolbar,
+} from '@taiga-ui/editor';
+import {TuiAccordion} from '@taiga-ui/kit';
+
+@Component({
+ standalone: true,
+ imports: [JsonPipe, ReactiveFormsModule, TuiAccordion, TuiEditor, TuiToolbar],
+ templateUrl: './index.html',
+ styleUrls: ['./index.less'],
+ encapsulation: ViewEncapsulation.None,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ providers: [
+ {
+ provide: TUI_EDITOR_EXTENSIONS,
+ deps: [Injector],
+ useFactory: (injector: Injector) => [
+ ...TUI_EDITOR_DEFAULT_EXTENSIONS,
+ import('@taiga-ui/editor').then(({tuiCreateImageEditorExtension}) =>
+ tuiCreateImageEditorExtension({injector}),
+ ),
+ ],
+ },
+ ],
+})
+export default class Example {
+ public editorRef: AbstractTuiEditor | null = null;
+
+ public readonly builtInTools = TUI_EDITOR_DEFAULT_TOOLS;
+
+ public readonly group = new FormGroup({
+ header: new FormControl(''),
+ main: new FormControl(''),
+ footer: new FormControl(''),
+ });
+}
diff --git a/projects/demo/src/app/pages/toolbar/shared/index.html b/projects/demo/src/app/pages/toolbar/shared/index.html
new file mode 100644
index 000000000..ad334348d
--- /dev/null
+++ b/projects/demo/src/app/pages/toolbar/shared/index.html
@@ -0,0 +1,12 @@
+
+
+
diff --git a/projects/demo/src/app/pages/toolbar/shared/index.ts b/projects/demo/src/app/pages/toolbar/shared/index.ts
new file mode 100644
index 000000000..936175542
--- /dev/null
+++ b/projects/demo/src/app/pages/toolbar/shared/index.ts
@@ -0,0 +1,24 @@
+import {ChangeDetectionStrategy, Component} from '@angular/core';
+import {TuiAddonDoc} from '@taiga-ui/addon-doc';
+import {TUI_EDITOR_DEFAULT_EXTENSIONS, TUI_EDITOR_EXTENSIONS} from '@taiga-ui/editor';
+
+@Component({
+ standalone: true,
+ imports: [TuiAddonDoc],
+ templateUrl: './index.html',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ providers: [
+ {
+ provide: TUI_EDITOR_EXTENSIONS,
+ useValue: TUI_EDITOR_DEFAULT_EXTENSIONS,
+ },
+ ],
+})
+export default class Example {
+ protected readonly component1 = import('./examples/1');
+ protected readonly example1 = {
+ TypeScript: import('./examples/1/index.ts?raw'),
+ HTML: import('./examples/1/index.html?raw'),
+ LESS: import('./examples/1/index.less?raw'),
+ };
+}
diff --git a/projects/demo/src/app/shared/routes.ts b/projects/demo/src/app/shared/routes.ts
index a742840bf..9f3a37774 100644
--- a/projects/demo/src/app/shared/routes.ts
+++ b/projects/demo/src/app/shared/routes.ts
@@ -26,4 +26,5 @@ export const TuiDemoPath = {
UploadFiles: 'upload-files',
ToolbarBottom: 'toolbar/bottom',
ToolbarFloating: 'toolbar/floating',
+ ToolbarShared: 'toolbar/shared',
} as const;
diff --git a/projects/editor/src/components/edit-link/edit-link.component.ts b/projects/editor/src/components/edit-link/edit-link.component.ts
index a72343b6b..c356ad463 100644
--- a/projects/editor/src/components/edit-link/edit-link.component.ts
+++ b/projects/editor/src/components/edit-link/edit-link.component.ts
@@ -14,6 +14,7 @@ import {TuiAutoFocus, tuiIsElement} from '@taiga-ui/cdk';
import {TuiButton, TuiLink, TuiScrollbar} from '@taiga-ui/core';
import {TuiInputInline} from '@taiga-ui/kit';
+import type {AbstractTuiEditor} from '../../abstract/editor-adapter.abstract';
import type {
TuiEditorLinkPrefix,
TuiEditorLinkProtocol,
@@ -52,7 +53,7 @@ import {tuiEditLinkParseUrl} from './utils/edit-link-parse-url';
export class TuiEditLink {
private readonly doc: Document | null = inject(WA_WINDOW)?.document ?? null;
private isOnlyAnchorMode: boolean = this.detectAnchorMode();
- private readonly editor = inject(TuiTiptapEditorService);
+ private readonly injectionEditor = inject(TuiTiptapEditorService, {optional: true});
protected readonly options = inject(TUI_EDITOR_OPTIONS);
protected url: string = this.getHrefOrAnchorId();
@@ -61,6 +62,9 @@ export class TuiEditLink {
protected anchorIds = this.getAllAnchorsIds();
protected readonly texts$ = inject(TUI_EDITOR_LINK_TEXTS);
+ @Input('editor')
+ public inputEditor: AbstractTuiEditor | null = null;
+
@Output()
public readonly addLink = new EventEmitter();
@@ -77,6 +81,10 @@ export class TuiEditLink {
return this.isOnlyAnchorMode;
}
+ protected get editor(): AbstractTuiEditor | null {
+ return this.injectionEditor ?? this.inputEditor;
+ }
+
protected get defaultProtocol(): TuiEditorLinkProtocol {
return this.options.linkOptions?.protocol ?? TUI_EDITOR_LINK_HTTPS_PREFIX;
}
@@ -232,7 +240,7 @@ export class TuiEditLink {
private getAllAnchorsIds(): string[] {
const nodes: Element[] = Array.from(
this.editor
- .getOriginTiptapEditor()
+ ?.getOriginTiptapEditor()
?.view.dom.querySelectorAll('[data-type="jump-anchor"]') ?? [],
);
diff --git a/projects/editor/src/components/editor/editor.component.less b/projects/editor/src/components/editor/editor.component.less
index 3d2909673..012440119 100644
--- a/projects/editor/src/components/editor/editor.component.less
+++ b/projects/editor/src/components/editor/editor.component.less
@@ -6,6 +6,7 @@
isolation: isolate;
font: var(--tui-font-text-m);
border-radius: var(--tui-radius-m);
+ border: 0.0625rem solid var(--tui-border-normal);
max-height: inherit;
min-height: 10rem;
box-sizing: border-box;
@@ -20,10 +21,6 @@
overflow: clip;
flex-direction: column;
border-radius: inherit;
-
- &:not([data-focus='true']) {
- outline: 0.0625rem solid var(--tui-border-normal);
- }
}
.t-editor-placeholder {
diff --git a/projects/editor/src/components/toolbar-tools/align-content/align-content.component.ts b/projects/editor/src/components/toolbar-tools/align-content/align-content.component.ts
index 4b29f5545..fa908a9f4 100644
--- a/projects/editor/src/components/toolbar-tools/align-content/align-content.component.ts
+++ b/projects/editor/src/components/toolbar-tools/align-content/align-content.component.ts
@@ -1,9 +1,12 @@
import {AsyncPipe, NgIf} from '@angular/common';
-import {ChangeDetectionStrategy, Component, inject} from '@angular/core';
+import type {OnInit} from '@angular/core';
+import {ChangeDetectionStrategy, Component, inject, Input} from '@angular/core';
import {TuiLet} from '@taiga-ui/cdk';
import {TuiButton, TuiDropdown, TuiHint} from '@taiga-ui/core';
-import {combineLatest, map} from 'rxjs';
+import type {Observable} from 'rxjs';
+import {combineLatest, map, of} from 'rxjs';
+import type {AbstractTuiEditor} from '../../../abstract/editor-adapter.abstract';
import {TuiTiptapEditorService} from '../../../directives/tiptap-editor/tiptap-editor.service';
import {TUI_EDITOR_OPTIONS} from '../../../tokens/editor-options';
import {TUI_EDITOR_TOOLBAR_TEXTS} from '../../../tokens/i18n';
@@ -16,22 +19,45 @@ import {TUI_EDITOR_TOOLBAR_TEXTS} from '../../../tokens/i18n';
styleUrls: ['../../../../styles/tools-common.less'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class TuiAlignContent {
+export class TuiAlignContent implements OnInit {
+ private localEditor: AbstractTuiEditor | null = null;
protected readonly options = inject(TUI_EDITOR_OPTIONS);
- protected readonly editor = inject(TuiTiptapEditorService);
+ protected readonly injectionEditor = inject(TuiTiptapEditorService, {optional: true});
protected readonly texts$ = inject(TUI_EDITOR_TOOLBAR_TEXTS);
+ protected alignState$: Observable<{
+ left: boolean;
+ right: boolean;
+ center: boolean;
+ justify: boolean;
+ }> | null = null;
- protected readonly alignState$ = combineLatest([
- this.editor.isActive$({textAlign: 'left'}),
- this.editor.isActive$({textAlign: 'right'}),
- this.editor.isActive$({textAlign: 'center'}),
- this.editor.isActive$({textAlign: 'justify'}),
- ]).pipe(
- map(([left, right, center, justify]) => ({
- left,
- right,
- center,
- justify,
- })),
- );
+ @Input('editor')
+ public set inputEditor(value: AbstractTuiEditor | null) {
+ this.localEditor = value;
+ this.initStream();
+ }
+
+ public ngOnInit(): void {
+ this.initStream();
+ }
+
+ protected get editor(): AbstractTuiEditor | null {
+ return this.injectionEditor ?? this.localEditor;
+ }
+
+ private initStream(): void {
+ this.alignState$ = combineLatest([
+ this.editor?.isActive$({textAlign: 'left'}) ?? of(false),
+ this.editor?.isActive$({textAlign: 'right'}) ?? of(false),
+ this.editor?.isActive$({textAlign: 'center'}) ?? of(false),
+ this.editor?.isActive$({textAlign: 'justify'}) ?? of(false),
+ ]).pipe(
+ map(([left, right, center, justify]) => ({
+ left,
+ right,
+ center,
+ justify,
+ })),
+ );
+ }
}
diff --git a/projects/editor/src/components/toolbar-tools/align-content/align-content.template.html b/projects/editor/src/components/toolbar-tools/align-content/align-content.template.html
index 63f7bb780..809fb730e 100644
--- a/projects/editor/src/components/toolbar-tools/align-content/align-content.template.html
+++ b/projects/editor/src/components/toolbar-tools/align-content/align-content.template.html
@@ -26,7 +26,7 @@
class="t-option t-option_margin"
[iconStart]="options.icons.textAlignLeft"
[tuiHint]="texts.justifyLeft"
- (click)="editor.onAlign('left')"
+ (click)="editor?.onAlign('left')"
>
diff --git a/projects/editor/src/components/toolbar-tools/code/code.component.ts b/projects/editor/src/components/toolbar-tools/code/code.component.ts
index dcaa70628..452b1812a 100644
--- a/projects/editor/src/components/toolbar-tools/code/code.component.ts
+++ b/projects/editor/src/components/toolbar-tools/code/code.component.ts
@@ -1,8 +1,11 @@
import {AsyncPipe, NgForOf} from '@angular/common';
-import {ChangeDetectionStrategy, Component, inject} from '@angular/core';
+import type {OnInit} from '@angular/core';
+import {ChangeDetectionStrategy, Component, inject, Input} from '@angular/core';
import {TuiButton, TuiDataList, TuiDropdown, TuiHint} from '@taiga-ui/core';
+import type {Observable} from 'rxjs';
import {distinctUntilChanged, map} from 'rxjs';
+import type {AbstractTuiEditor} from '../../../abstract/editor-adapter.abstract';
import {TuiTiptapEditorService} from '../../../directives/tiptap-editor/tiptap-editor.service';
import {TUI_EDITOR_OPTIONS} from '../../../tokens/editor-options';
import {TUI_EDITOR_CODE_OPTIONS, TUI_EDITOR_TOOLBAR_TEXTS} from '../../../tokens/i18n';
@@ -14,23 +17,47 @@ import {TUI_EDITOR_CODE_OPTIONS, TUI_EDITOR_TOOLBAR_TEXTS} from '../../../tokens
templateUrl: './code.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class TuiCode {
+export class TuiCode implements OnInit {
+ private localEditor: AbstractTuiEditor | null = null;
protected readonly options = inject(TUI_EDITOR_OPTIONS);
- protected readonly editor = inject(TuiTiptapEditorService);
+ protected readonly injectionEditor = inject(TuiTiptapEditorService, {optional: true});
protected readonly texts$ = inject(TUI_EDITOR_TOOLBAR_TEXTS);
protected readonly codeOptionsTexts$ = inject(TUI_EDITOR_CODE_OPTIONS);
protected readonly hintText$ = this.texts$.pipe(map((texts) => texts.code));
+ protected insideCode$: Observable | null = null;
- protected readonly insideCode$ = this.editor.stateChange$.pipe(
- map(() => this.editor.isActive('code') || this.editor.isActive('codeBlock')),
- distinctUntilChanged(),
- );
+ @Input('editor')
+ public set inputEditor(value: AbstractTuiEditor | null) {
+ this.localEditor = value;
+ this.initStream();
+ }
+
+ public ngOnInit(): void {
+ this.initStream();
+ }
- protected onCode(isCodeBlock: boolean): void {
+ public onCode(isCodeBlock: boolean): void {
if (isCodeBlock) {
- this.editor.toggleCodeBlock();
+ this.editor?.toggleCodeBlock();
} else {
- this.editor.toggleCode();
+ this.editor?.toggleCode();
}
}
+
+ protected get editor(): AbstractTuiEditor | null {
+ return this.injectionEditor ?? this.localEditor;
+ }
+
+ private initStream(): void {
+ this.insideCode$ =
+ this.editor?.stateChange$.pipe(
+ map(
+ () =>
+ (this.editor?.isActive('code') ||
+ this.editor?.isActive('codeBlock')) ??
+ false,
+ ),
+ distinctUntilChanged(),
+ ) ?? null;
+ }
}
diff --git a/projects/editor/src/components/toolbar-tools/details/details-remove/details-remove.component.ts b/projects/editor/src/components/toolbar-tools/details/details-remove/details-remove.component.ts
index 6c99d9ffd..166531dbb 100644
--- a/projects/editor/src/components/toolbar-tools/details/details-remove/details-remove.component.ts
+++ b/projects/editor/src/components/toolbar-tools/details/details-remove/details-remove.component.ts
@@ -1,8 +1,11 @@
import {AsyncPipe, NgIf} from '@angular/common';
-import {ChangeDetectionStrategy, Component, inject} from '@angular/core';
+import type {OnInit} from '@angular/core';
+import {ChangeDetectionStrategy, Component, inject, Input} from '@angular/core';
import {TuiButton, TuiHint} from '@taiga-ui/core';
+import type {Observable} from 'rxjs';
import {distinctUntilChanged, map} from 'rxjs';
+import type {AbstractTuiEditor} from '../../../../abstract/editor-adapter.abstract';
import {TuiTiptapEditorService} from '../../../../directives/tiptap-editor/tiptap-editor.service';
import {TUI_EDITOR_OPTIONS} from '../../../../tokens/editor-options';
import {TUI_EDITOR_TOOLBAR_TEXTS} from '../../../../tokens/i18n';
@@ -14,17 +17,36 @@ import {TUI_EDITOR_TOOLBAR_TEXTS} from '../../../../tokens/i18n';
templateUrl: './details-remove.template.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class TuiDetailsRemove {
- protected readonly editor = inject(TuiTiptapEditorService);
+export class TuiDetailsRemove implements OnInit {
+ private localEditor: AbstractTuiEditor | null = null;
+ protected readonly injectionEditor = inject(TuiTiptapEditorService, {optional: true});
protected readonly texts$ = inject(TUI_EDITOR_TOOLBAR_TEXTS);
protected readonly options = inject(TUI_EDITOR_OPTIONS);
+ protected disabled$: Observable | null = null;
- protected readonly disabled$ = this.editor.stateChange$.pipe(
- map(() => !this.editor.isActive('details')),
- distinctUntilChanged(),
- );
+ @Input('editor')
+ public set inputEditor(value: AbstractTuiEditor | null) {
+ this.localEditor = value;
+ this.initStream();
+ }
+
+ public ngOnInit(): void {
+ this.initStream();
+ }
+
+ public removeDetails(): void {
+ this.editor?.removeDetails();
+ }
+
+ protected get editor(): AbstractTuiEditor | null {
+ return this.injectionEditor ?? this.localEditor;
+ }
- protected removeDetails(): void {
- this.editor.removeDetails();
+ private initStream(): void {
+ this.disabled$ =
+ this.editor?.stateChange$.pipe(
+ map(() => !this.editor?.isActive('details') ?? false),
+ distinctUntilChanged(),
+ ) ?? null;
}
}
diff --git a/projects/editor/src/components/toolbar-tools/details/details.component.ts b/projects/editor/src/components/toolbar-tools/details/details.component.ts
index ea0eea6e7..fa4af25de 100644
--- a/projects/editor/src/components/toolbar-tools/details/details.component.ts
+++ b/projects/editor/src/components/toolbar-tools/details/details.component.ts
@@ -1,7 +1,8 @@
import {AsyncPipe, NgIf} from '@angular/common';
-import {ChangeDetectionStrategy, Component, inject} from '@angular/core';
+import {ChangeDetectionStrategy, Component, inject, Input} from '@angular/core';
import {TuiButton, TuiHint} from '@taiga-ui/core';
+import type {AbstractTuiEditor} from '../../../abstract/editor-adapter.abstract';
import {TuiTiptapEditorService} from '../../../directives/tiptap-editor/tiptap-editor.service';
import {TUI_EDITOR_OPTIONS} from '../../../tokens/editor-options';
import {TUI_EDITOR_TOOLBAR_TEXTS} from '../../../tokens/i18n';
@@ -14,11 +15,18 @@ import {TUI_EDITOR_TOOLBAR_TEXTS} from '../../../tokens/i18n';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TuiDetails {
- protected readonly editor = inject(TuiTiptapEditorService);
+ protected readonly injectionEditor = inject(TuiTiptapEditorService, {optional: true});
protected readonly texts$ = inject(TUI_EDITOR_TOOLBAR_TEXTS);
protected readonly options = inject(TUI_EDITOR_OPTIONS);
+ @Input('editor')
+ public inputEditor: AbstractTuiEditor | null = null;
+
+ protected get editor(): AbstractTuiEditor | null {
+ return this.injectionEditor ?? this.inputEditor;
+ }
+
protected setDetails(): void {
- this.editor.setDetails();
+ this.editor?.setDetails();
}
}
diff --git a/projects/editor/src/components/toolbar-tools/font-size/font-size.component.ts b/projects/editor/src/components/toolbar-tools/font-size/font-size.component.ts
index 2e98d6cc9..0013b59f8 100644
--- a/projects/editor/src/components/toolbar-tools/font-size/font-size.component.ts
+++ b/projects/editor/src/components/toolbar-tools/font-size/font-size.component.ts
@@ -1,10 +1,11 @@
import {AsyncPipe, LowerCasePipe, NgClass, NgForOf, NgStyle} from '@angular/common';
-import {ChangeDetectionStrategy, Component, inject} from '@angular/core';
+import {ChangeDetectionStrategy, Component, inject, Input} from '@angular/core';
import {tuiPx} from '@taiga-ui/cdk';
import {TuiButton, TuiDataList, TuiDropdown, TuiHint} from '@taiga-ui/core';
import type {Observable} from 'rxjs';
import {map} from 'rxjs';
+import type {AbstractTuiEditor} from '../../../abstract/editor-adapter.abstract';
import {EDITOR_BLANK_COLOR} from '../../../constants/default-editor-colors';
import {TuiTiptapEditorService} from '../../../directives/tiptap-editor/tiptap-editor.service';
import type {TuiEditorFontOption} from '../../../interfaces/editor-font-option';
@@ -31,7 +32,7 @@ import {TUI_EDITOR_FONT_OPTIONS, TUI_EDITOR_TOOLBAR_TEXTS} from '../../../tokens
export class TuiFontSize {
private readonly fontOptionsTexts$ = inject(TUI_EDITOR_FONT_OPTIONS);
protected readonly options = inject(TUI_EDITOR_OPTIONS);
- protected readonly editor = inject(TuiTiptapEditorService);
+ protected readonly injectionEditor = inject(TuiTiptapEditorService, {optional: true});
protected readonly texts$ = inject(TUI_EDITOR_TOOLBAR_TEXTS);
protected readonly fontsOptions$: Observable<
ReadonlyArray>
@@ -39,24 +40,31 @@ export class TuiFontSize {
protected readonly fontText$ = this.texts$.pipe(map((texts) => texts.font));
+ @Input('editor')
+ public inputEditor: AbstractTuiEditor | null = null;
+
+ protected get editor(): AbstractTuiEditor | null {
+ return this.injectionEditor ?? this.inputEditor;
+ }
+
protected setFontOption({headingLevel, px}: Partial): void {
- const color = this.editor.getFontColor();
+ const color = this.editor?.getFontColor() || EDITOR_BLANK_COLOR;
this.clearPreviousTextStyles();
if (headingLevel) {
- this.editor.setHeading(headingLevel);
+ this.editor?.setHeading(headingLevel);
} else {
- this.editor.setParagraph({fontSize: tuiPx(px || 0)});
+ this.editor?.setParagraph({fontSize: tuiPx(px || 0)});
}
if (color !== EDITOR_BLANK_COLOR) {
- this.editor.setFontColor(color);
+ this.editor?.setFontColor(color);
}
}
private clearPreviousTextStyles(): void {
- this.editor.removeEmptyTextStyle();
- this.editor.toggleMark('textStyle');
+ this.editor?.removeEmptyTextStyle();
+ this.editor?.toggleMark('textStyle');
}
}
diff --git a/projects/editor/src/components/toolbar-tools/font-style/font-style.component.ts b/projects/editor/src/components/toolbar-tools/font-style/font-style.component.ts
index b2d82f6bd..63aac953b 100644
--- a/projects/editor/src/components/toolbar-tools/font-style/font-style.component.ts
+++ b/projects/editor/src/components/toolbar-tools/font-style/font-style.component.ts
@@ -1,8 +1,11 @@
import {AsyncPipe, NgIf} from '@angular/common';
+import type {OnInit} from '@angular/core';
import {ChangeDetectionStrategy, Component, inject, Input} from '@angular/core';
import {TuiButton, TuiDropdown, TuiHint} from '@taiga-ui/core';
-import {combineLatest, map} from 'rxjs';
+import type {Observable} from 'rxjs';
+import {combineLatest, map, of} from 'rxjs';
+import type {AbstractTuiEditor} from '../../../abstract/editor-adapter.abstract';
import {TUI_EDITOR_DEFAULT_TOOLS} from '../../../constants/default-editor-tools';
import {TuiTiptapEditorService} from '../../../directives/tiptap-editor/tiptap-editor.service';
import {TUI_EDITOR_OPTIONS} from '../../../tokens/editor-options';
@@ -18,27 +21,25 @@ import {TuiEditorTool} from '../../../types/editor-tool';
styleUrls: ['../../../../styles/tools-common.less'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class TuiFontStyle {
+export class TuiFontStyle implements OnInit {
private toolsSet = new Set(TUI_EDITOR_DEFAULT_TOOLS);
-
+ private localEditor: AbstractTuiEditor | null = null;
protected readonly editorTool: typeof TuiEditorTool = TuiEditorTool;
protected readonly options = inject(TUI_EDITOR_OPTIONS);
- protected readonly editor = inject(TuiTiptapEditorService);
+ protected readonly injectionEditor = inject(TuiTiptapEditorService, {optional: true});
protected readonly texts$ = inject(TUI_EDITOR_TOOLBAR_TEXTS);
+ protected fontStyleState$: Observable<{
+ bold: boolean;
+ italic: boolean;
+ underline: boolean;
+ strike: boolean;
+ }> | null = null;
- protected readonly fontStyleState$ = combineLatest([
- this.editor.isActive$('bold'),
- this.editor.isActive$('italic'),
- this.editor.isActive$('underline'),
- this.editor.isActive$('strike'),
- ]).pipe(
- map(([bold, italic, underline, strike]) => ({
- bold,
- italic,
- underline,
- strike,
- })),
- );
+ @Input('editor')
+ public set inputEditor(value: AbstractTuiEditor | null) {
+ this.localEditor = value;
+ this.initStream();
+ }
@Input()
public set enabledTools(
@@ -47,7 +48,31 @@ export class TuiFontStyle {
this.toolsSet = new Set(value);
}
- protected isEnabled(tool: TuiEditorToolType): boolean {
+ public get editor(): AbstractTuiEditor | null {
+ return this.injectionEditor ?? this.localEditor;
+ }
+
+ public ngOnInit(): void {
+ this.initStream();
+ }
+
+ public isEnabled(tool: TuiEditorToolType): boolean {
return this.toolsSet.has(tool);
}
+
+ private initStream(): void {
+ this.fontStyleState$ = combineLatest([
+ this.editor?.isActive$('bold') ?? of(false),
+ this.editor?.isActive$('italic') ?? of(false),
+ this.editor?.isActive$('underline') ?? of(false),
+ this.editor?.isActive$('strike') ?? of(false),
+ ]).pipe(
+ map(([bold, italic, underline, strike]) => ({
+ bold,
+ italic,
+ underline,
+ strike,
+ })),
+ );
+ }
}
diff --git a/projects/editor/src/components/toolbar-tools/font-style/font-style.template.html b/projects/editor/src/components/toolbar-tools/font-style/font-style.template.html
index e45e21aff..d4215b0f3 100644
--- a/projects/editor/src/components/toolbar-tools/font-style/font-style.template.html
+++ b/projects/editor/src/components/toolbar-tools/font-style/font-style.template.html
@@ -27,7 +27,7 @@
class="t-option t-option_margin"
[iconStart]="options.icons.fontStyleBold"
[tuiHint]="texts.bold"
- (click)="editor.toggleBold()"
+ (click)="editor?.toggleBold()"
>
diff --git a/projects/editor/src/components/toolbar-tools/group/group.component.ts b/projects/editor/src/components/toolbar-tools/group/group.component.ts
index 996d8ba8d..d44215422 100644
--- a/projects/editor/src/components/toolbar-tools/group/group.component.ts
+++ b/projects/editor/src/components/toolbar-tools/group/group.component.ts
@@ -1,8 +1,11 @@
import {AsyncPipe} from '@angular/common';
-import {ChangeDetectionStrategy, Component, inject} from '@angular/core';
+import type {OnInit} from '@angular/core';
+import {ChangeDetectionStrategy, Component, inject, Input} from '@angular/core';
import {TuiButton, TuiHint} from '@taiga-ui/core';
+import type {Observable} from 'rxjs';
import {distinctUntilChanged, map} from 'rxjs';
+import type {AbstractTuiEditor} from '../../../abstract/editor-adapter.abstract';
import {TuiTiptapEditorService} from '../../../directives/tiptap-editor/tiptap-editor.service';
import {TUI_EDITOR_OPTIONS} from '../../../tokens/editor-options';
import {TUI_EDITOR_TOOLBAR_TEXTS} from '../../../tokens/i18n';
@@ -15,8 +18,9 @@ import {TUI_EDITOR_TOOLBAR_TEXTS} from '../../../tokens/i18n';
styleUrls: ['../../../../styles/tools-common.less'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class TuiEditorGroupTool {
- protected readonly editor = inject(TuiTiptapEditorService);
+export class TuiEditorGroupTool implements OnInit {
+ private localEditor: AbstractTuiEditor | null = null;
+ protected readonly injectionEditor = inject(TuiTiptapEditorService, {optional: true});
protected readonly texts$ = inject(TUI_EDITOR_TOOLBAR_TEXTS);
protected readonly options = inject(TUI_EDITOR_OPTIONS);
@@ -28,16 +32,40 @@ export class TuiEditorGroupTool {
map((texts) => texts.removeGroup),
);
- protected readonly disabled$ = this.editor.stateChange$.pipe(
- map(() => !this.editor.isActive('group')),
- distinctUntilChanged(),
- );
+ protected disabled$: Observable | null = null;
+
+ @Input('editor')
+ public set inputEditor(value: AbstractTuiEditor | null) {
+ this.localEditor = value;
+ this.initStream();
+ }
+
+ public ngOnInit(): void {
+ this.initStream();
+ }
- protected addGroup(): void {
- this.editor.setGroup();
+ public addGroup(): void {
+ this.editor?.setGroup();
}
- protected removeGroup(): void {
- this.editor.removeGroup();
+ public removeGroup(): void {
+ this.editor?.removeGroup();
+ }
+
+ protected get editor(): AbstractTuiEditor | null {
+ return this.injectionEditor ?? this.localEditor;
+ }
+
+ private initStream(): void {
+ this.disabled$ =
+ this.editor?.stateChange$.pipe(
+ map(() => !this.editor?.isActive('group')),
+ distinctUntilChanged(),
+ ) ?? null;
+
+ this.editor?.stateChange$.pipe(
+ map(() => !this.editor?.isActive('group')),
+ distinctUntilChanged(),
+ );
}
}
diff --git a/projects/editor/src/components/toolbar-tools/highlight-color/highlight-color.component.ts b/projects/editor/src/components/toolbar-tools/highlight-color/highlight-color.component.ts
index 8061a39b0..121256004 100644
--- a/projects/editor/src/components/toolbar-tools/highlight-color/highlight-color.component.ts
+++ b/projects/editor/src/components/toolbar-tools/highlight-color/highlight-color.component.ts
@@ -1,10 +1,13 @@
import {AsyncPipe, NgIf} from '@angular/common';
+import type {OnInit} from '@angular/core';
import {ChangeDetectionStrategy, Component, inject, Input} from '@angular/core';
import {TuiActiveZone, TuiLet} from '@taiga-ui/cdk';
import {TuiButton, TuiDropdown, TuiHint} from '@taiga-ui/core';
import {TuiPaletteModule} from '@taiga-ui/legacy';
+import type {Observable} from 'rxjs';
import {distinctUntilChanged, map} from 'rxjs';
+import type {AbstractTuiEditor} from '../../../abstract/editor-adapter.abstract';
import {TuiTiptapEditorService} from '../../../directives/tiptap-editor/tiptap-editor.service';
import type {TuiEditorOptions} from '../../../tokens/editor-options';
import {TUI_EDITOR_OPTIONS} from '../../../tokens/editor-options';
@@ -27,26 +30,48 @@ import {TUI_EDITOR_TOOLBAR_TEXTS} from '../../../tokens/i18n';
styleUrls: ['../../../../styles/tools-common.less'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class TuiHighlightColor {
+export class TuiHighlightColor implements OnInit {
+ private localEditor: AbstractTuiEditor | null = null;
private readonly options = inject(TUI_EDITOR_OPTIONS);
-
- protected readonly editor = inject(TuiTiptapEditorService);
+ protected readonly injectionEditor = inject(TuiTiptapEditorService, {optional: true});
protected readonly texts$ = inject(TUI_EDITOR_TOOLBAR_TEXTS);
- protected readonly backgroundColor$ = this.editor.stateChange$.pipe(
- map(() => this.editor.getBackgroundColor() || this.options.blankColor),
- distinctUntilChanged(),
- );
-
+ protected backgroundColor$: Observable | null = null;
protected readonly backColorText$ = this.texts$.pipe(map((texts) => texts.backColor));
@Input()
public colors: ReadonlyMap = this.options.colors;
+ @Input('editor')
+ public set inputEditor(value: AbstractTuiEditor | null) {
+ this.localEditor = value;
+ this.initStream();
+ }
+
+ public ngOnInit(): void {
+ this.initStream();
+ }
+
+ public isBlankColor(color: string): boolean {
+ return color === this.options.blankColor;
+ }
+
+ protected get editor(): AbstractTuiEditor | null {
+ return this.injectionEditor ?? this.localEditor;
+ }
+
protected get icons(): TuiEditorOptions['icons'] {
return this.options.icons;
}
- protected isBlankColor(color: string): boolean {
- return color === this.options.blankColor;
+ private initStream(): void {
+ this.backgroundColor$ =
+ this.editor?.stateChange$.pipe(
+ map(
+ () =>
+ (this.editor?.getBackgroundColor() || this.options.blankColor) ??
+ false,
+ ),
+ distinctUntilChanged(),
+ ) ?? null;
}
}
diff --git a/projects/editor/src/components/toolbar-tools/highlight-color/highlight-color.template.html b/projects/editor/src/components/toolbar-tools/highlight-color/highlight-color.template.html
index 60ba0fb66..b7cab9d95 100644
--- a/projects/editor/src/components/toolbar-tools/highlight-color/highlight-color.template.html
+++ b/projects/editor/src/components/toolbar-tools/highlight-color/highlight-color.template.html
@@ -25,7 +25,7 @@
diff --git a/projects/editor/src/components/toolbar-tools/list-configs/list-configs.component.ts b/projects/editor/src/components/toolbar-tools/list-configs/list-configs.component.ts
index c58011ff8..8134779ad 100644
--- a/projects/editor/src/components/toolbar-tools/list-configs/list-configs.component.ts
+++ b/projects/editor/src/components/toolbar-tools/list-configs/list-configs.component.ts
@@ -1,8 +1,10 @@
import {AsyncPipe, NgIf} from '@angular/common';
-import {ChangeDetectionStrategy, Component, inject} from '@angular/core';
+import {ChangeDetectionStrategy, Component, inject, Input} from '@angular/core';
import {TuiLet} from '@taiga-ui/cdk';
import {TuiButton, TuiDropdown, TuiHint} from '@taiga-ui/core';
-import {combineLatest, map} from 'rxjs';
+import type {AbstractTuiEditor} from 'projects/editor/src/abstract/editor-adapter.abstract';
+import type {Observable} from 'rxjs';
+import {combineLatest, map, of} from 'rxjs';
import {TuiTiptapEditorService} from '../../../directives/tiptap-editor/tiptap-editor.service';
import {TUI_EDITOR_OPTIONS} from '../../../tokens/editor-options';
@@ -17,27 +19,46 @@ import {TUI_EDITOR_TOOLBAR_TEXTS} from '../../../tokens/i18n';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TuiListConfigs {
+ private localEditor: AbstractTuiEditor | null = null;
protected readonly options = inject(TUI_EDITOR_OPTIONS);
- protected readonly editor = inject(TuiTiptapEditorService);
+ protected readonly injectionEditor = inject(TuiTiptapEditorService, {optional: true});
protected readonly texts$ = inject(TUI_EDITOR_TOOLBAR_TEXTS);
- protected readonly listState$ = combineLatest([
- this.editor.isActive$('orderedList'),
- this.editor.isActive$('bulletList'),
- this.editor.isActive$('taskList'),
- ]).pipe(
- map(([ordered, unordered, tasked]) => ({
- ordered,
- unordered,
- tasked,
- })),
- );
+ protected listState$: Observable<{
+ ordered: boolean;
+ unordered: boolean;
+ tasked: boolean;
+ }> | null = null;
+
+ @Input('editor')
+ public set inputEditor(value: AbstractTuiEditor | null) {
+ this.localEditor = value;
+ this.initStream();
+ }
+
+ protected get editor(): AbstractTuiEditor | null {
+ return this.injectionEditor ?? this.localEditor;
+ }
protected sinkListItem(): void {
- this.editor.sinkListItem();
+ this.editor?.sinkListItem();
}
protected liftListItem(): void {
- this.editor.liftListItem();
+ this.editor?.liftListItem();
+ }
+
+ private initStream(): void {
+ this.listState$ = combineLatest([
+ this.editor?.isActive$('orderedList') ?? of(false),
+ this.editor?.isActive$('bulletList') ?? of(false),
+ this.editor?.isActive$('taskList') ?? of(false),
+ ]).pipe(
+ map(([ordered, unordered, tasked]) => ({
+ ordered,
+ unordered,
+ tasked,
+ })),
+ );
}
}
diff --git a/projects/editor/src/components/toolbar-tools/list-configs/list-configs.template.html b/projects/editor/src/components/toolbar-tools/list-configs/list-configs.template.html
index 0557c88b0..cd2dbdf34 100644
--- a/projects/editor/src/components/toolbar-tools/list-configs/list-configs.template.html
+++ b/projects/editor/src/components/toolbar-tools/list-configs/list-configs.template.html
@@ -27,7 +27,7 @@
class="t-option t-option_margin"
[iconStart]="options.icons.listUnOrdered"
[tuiHint]="texts.unorderedList"
- (click)="editor.toggleUnorderedList()"
+ (click)="editor?.toggleUnorderedList()"
>
-
+
@@ -78,12 +80,14 @@
*ngIf="enabled(editorTool.Align)"
tuiItem
class="t-tool t-wrapper"
+ [editor]="editor"
/>