@flogeez/angular-tiptap-editor 3.0.3 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,49 +1,29 @@
1
1
  # Angular Tiptap Editor
2
2
 
3
3
  > [!IMPORTANT]
4
- > **New Version Available**: v3.0.0 uses Tiptap v3. If you need to stay on Tiptap v2, please use version `^2.4.0`.
4
+ > **^v3.0.0 uses Tiptap v3**. If you need to stay on Tiptap v2, please use version `^2.4.0`.
5
5
 
6
- A modern, customizable rich-text editor for Angular, built with Tiptap.
6
+ A modern, customizable Angular rich-text editor, built with **Tiptap**.
7
7
 
8
8
  [![NPM Version](https://img.shields.io/npm/v/@flogeez/angular-tiptap-editor?style=for-the-badge&logo=npm)](https://www.npmjs.com/package/@flogeez/angular-tiptap-editor) [![Demo](https://img.shields.io/badge/Demo-Live-brightgreen?style=for-the-badge&logo=google-chrome)](https://flogeez.github.io/angular-tiptap-editor/) [![Try it on StackBlitz](https://img.shields.io/badge/Try%20it-StackBlitz-blue?style=for-the-badge&logo=stackblitz)](https://stackblitz.com/edit/angular-tiptap-editor)
9
9
 
10
- Angular Tiptap Editor is a high-performance WYSIWYG editor engineered for the modern Angular ecosystem. Built on top of Tiptap and powered by a native **Signals** architecture, it features a polished, professional design that feels, I think, clean and modern out of the box.
10
+ High-performance Angular WYSIWYG editor. Built on top of **Tiptap** and powered by a native **Signals** architecture, it features a polished, professional design that feels, I think, clean and modern out of the box.
11
11
  Yet, I've worked to keep it fully customizable: you can easily configure the editor, tweak the UI, or even embed your own Angular components as interactive nodes.
12
12
 
13
- ## 🚀 Features
14
-
15
- - **Modern Angular**: Built with Angular 18+ using Signals and modern patterns for peak performance.
16
- - **Full Rich Text Power**: Powered by Tiptap with extensive formatting and block capabilities.
17
- - **Modern UX (Notion-like)**: Intuitive slash commands and bubble menus for a keyboard-first experience.
18
- - **Highly Customizable**: Easily configure toolbars, bubble menus, and slash command items.
19
- - **Signal-Based Reactivity**: Pure Signal architecture natively compatible with `ChangeDetectionStrategy.OnPush`.
20
- - **Advanced Table Support**: Full table management with cell selection and context-aware bubble menus.
21
- - **Professional Media**: Advanced image handling with resizing, auto-compression, and custom uploaders.
22
- - **Built-in i18n**: English & French support with a reactive, extensible locale system.
23
- - **Word/Character Count**: Real-time statistics with proper pluralization support.
24
- - **Office-Ready**: Cleaned-up pasting from Microsoft Word and Excel to maintain layout integrity.
25
- - **Seamless Angular Integration**: Use a single `provideAteEditor()` to initialize the library and share a root injector across all nodes.
26
- - **Universal Component Embedding**: Embed _any_ Angular component (library or custom) directly into the editor as a TipTap node.
27
- - **Global Configuration**: Set application-wide defaults for themes, toolbars, and features with hierarchical inheritance.
28
- - **Service Driven**: Deep programmatic control via `AteEditorCommandsService` and isolated instances.
29
- - **A11y First**: Built with accessibility best practices and full keyboard navigation.
30
-
31
- ## 💎 Why this editor?
32
-
33
- Most Angular wrappers for Tiptap provide a basic component but leave the heavy lifting to you. **Angular Tiptap Editor** is built to solve common production hurdles:
34
-
35
- - **True Scalability**: Thanks to **isolated services** provided at the component level, you can host multiple independent editors with different configurations and languages on the same page without a single state leak.
36
- - **OnPush by Default**: The entire UI (toolbar, menus) is powered by **Angular Signals**. The `editorState` snapshot logic ensures that your components only re-render when necessary, even in complex `OnPush` applications.
37
- - **Deep i18n & Extensibility**: Not just English/French — you can inject **custom translations** and **custom Tiptap extensions**. Our `DiscoveryCalculator` automatically tracks any new mark or node you add, making them reactive without extra code.
38
- - **Clean Office UX**: Professional-grade pasting from **Word and Excel** plus smart image handling (auto-compression, resizing handles) ensures a polished experience for end-users.
13
+ ---
39
14
 
40
- ## 🛠️ Extensions included
15
+ ## Features
41
16
 
42
- The library comes with a pre-configured set of standard and custom extensions:
17
+ - **Signal-Based**: Native performance with `ChangeDetectionStrategy.OnPush`.
18
+ - 🧩 **Angular Nodes**: Embed any Angular component as an interactive editor node.
19
+ - ⌨️ **UX First**: Slash commands (`/`) and context-aware bubble menus (Notion-like).
20
+ - 📊 **Table Power**: Advanced management with cell selection and merging.
21
+ - 🖼️ **Pro Media**: Image resizing, auto-compression, and custom uploaders.
22
+ - 🌍 **Built-in i18n**: Support for English and French out of the box.
23
+ - 📎 **Office-Ready**: Clean pasting from Word and Excel.
24
+ - 🎨 **Highly Customizable**: Easily configure toolbars, bubble menus, and slash command items.
43
25
 
44
- - **Nodes**: `StarterKit`, `Heading`, `Table`, `Image`, `HorizontalRule`, `CodeBlock`.
45
- - **Marks**: `Bold`, `Italic`, `Underline`, `Strike`, `Code`, `Link`, `Highlight`, `TextStyle`, `Color`, `Superscript`, `Subscript`.
46
- - **Utilities**: `Placeholder`, `CharacterCount`, `Typography`, `Focus`, `BubbleMenu`, `Gapcursor`, `Dropcursor`, `ResizableImage` (Custom).
26
+ - 🛠️ **Extensible**: Easily add custom Tiptap extensions and reactive state calculators.
47
27
 
48
28
  ## 📦 Installation
49
29
 
@@ -51,9 +31,7 @@ The library comes with a pre-configured set of standard and custom extensions:
51
31
  npm install @flogeez/angular-tiptap-editor
52
32
  ```
53
33
 
54
- ### CSS Styles
55
-
56
- Add the required CSS to your `angular.json` file in the `styles` array:
34
+ Add the styles to your `angular.json`:
57
35
 
58
36
  ```json
59
37
  {
@@ -66,738 +44,119 @@ Add the required CSS to your `angular.json` file in the `styles` array:
66
44
  }
67
45
  ```
68
46
 
69
- ## 🎯 Quick Start
47
+ ## 🚀 Quick Start
70
48
 
71
- ### 1. Basic Usage
49
+ ### 1. Global Setup
72
50
 
73
- ```typescript
74
- import { Component } from "@angular/core";
75
- import { AngularTiptapEditorComponent } from "@flogeez/angular-tiptap-editor";
51
+ Initialize the library in `app.config.ts`:
76
52
 
77
- @Component({
78
- selector: "app-example",
79
- standalone: true,
80
- imports: [AngularTiptapEditorComponent],
81
- template: `
82
- <angular-tiptap-editor [content]="content" (contentChange)="onContentChange($event)" />
83
- `,
84
- })
85
- export class ExampleComponent {
86
- content = "<p>Hello <strong>World</strong>!</p>";
87
-
88
- onContentChange(newContent: string) {
89
- this.content = newContent;
90
- console.log("Content updated:", newContent);
91
- }
92
- }
53
+ ```typescript
54
+ export const appConfig: ApplicationConfig = {
55
+ providers: [provideAteEditor()],
56
+ };
93
57
  ```
94
58
 
95
- ### 2. With Custom Configuration
96
-
97
- The editor can be fully configured using a single `[config]` object, which provides a clean and type-safe way to manage all settings.
59
+ ### 2. Basic Usage
98
60
 
99
61
  ```typescript
100
- import { Component } from "@angular/core";
101
- import {
102
- AngularTiptapEditorComponent,
103
- AteEditorConfig,
104
- ATE_DEFAULT_TOOLBAR_CONFIG,
105
- } from "@flogeez/angular-tiptap-editor";
62
+ import { AngularTiptapEditorComponent } from "@flogeez/angular-tiptap-editor";
106
63
 
107
64
  @Component({
108
- selector: "app-advanced",
65
+ selector: "app-example",
109
66
  standalone: true,
110
67
  imports: [AngularTiptapEditorComponent],
111
- template: `
112
- <angular-tiptap-editor
113
- [content]="content"
114
- [config]="editorConfig"
115
- (contentChange)="onContentChange($event)" />
116
- `,
68
+ template: `<angular-tiptap-editor
69
+ [content]="content"
70
+ (contentChange)="onContentChange($event)" />`,
117
71
  })
118
- export class AdvancedComponent {
119
- content = "<h1>Welcome!</h1><p>Start editing...</p>";
120
-
121
- editorConfig: AteEditorConfig = {
122
- locale: "fr", // Force French (default is auto-detect)
123
- height: "400px", // Set a fixed height
124
- placeholder: "Commencez à rédiger...",
125
- showWordCount: false, // Hide the word counter (default is true)
126
- showEditToggle: true, // Show the button to toggle read-only mode (default is false)
127
-
128
- // Customize the toolbar by enabling specific features
129
- toolbar: {
130
- ...ATE_DEFAULT_TOOLBAR_CONFIG,
131
- clear: true, // Enable the 'Clear' button
132
- highlight: false, // Disable the highlight button
133
- },
134
-
135
- // Only enable specific slash commands
136
- slashCommands: {
137
- heading1: true,
138
- heading2: true,
139
- image: true,
140
- table: true,
141
- },
142
- };
143
-
144
- onContentChange(newContent: string) {
145
- this.content = newContent;
72
+ export class ExampleComponent {
73
+ content = "<p>Hello World!</p>";
74
+ onContentChange(html: string) {
75
+ console.log(html);
146
76
  }
147
77
  }
148
78
  ```
149
79
 
150
- ### 3. With Form Integration
80
+ ### 3. Reactive Forms Integration
151
81
 
152
82
  ```typescript
153
- import { Component } from "@angular/core";
154
83
  import { FormControl, ReactiveFormsModule } from "@angular/forms";
155
- import { AngularTiptapEditorComponent } from "@flogeez/angular-tiptap-editor";
156
84
 
157
85
  @Component({
158
- selector: "app-form",
159
86
  standalone: true,
160
87
  imports: [AngularTiptapEditorComponent, ReactiveFormsModule],
161
- template: `
162
- <form>
163
- <angular-tiptap-editor
164
- [formControl]="contentControl"
165
- placeholder="Enter your content here..." />
166
- <button type="submit">Submit</button>
167
- </form>
168
- `,
88
+ template: ` <angular-tiptap-editor [formControl]="contentControl" /> `,
169
89
  })
170
90
  export class FormComponent {
171
91
  contentControl = new FormControl("<p>Initial content</p>");
172
92
  }
173
93
  ```
174
94
 
175
- ## ⚙️ Advanced Setup & Extensions
176
-
177
- ### 1. Global Setup (Recommended)
178
-
179
- Initialize the library globally in your `app.config.ts` or `main.ts` to capture the root injector and set application-wide defaults.
180
-
181
- ```typescript
182
- import { ApplicationConfig } from "@angular/core";
183
- import { provideAteEditor } from "@flogeez/angular-tiptap-editor";
184
-
185
- export const appConfig: ApplicationConfig = {
186
- providers: [
187
- provideAteEditor({
188
- theme: "auto",
189
- mode: "seamless",
190
- tiptapExtensions: [
191
- /* Global TipTap Extensions */
192
- ],
193
- stateCalculators: [
194
- /* Global State Calculators */
195
- ],
196
- }),
197
- ],
198
- };
199
- ```
200
-
201
- ### 2. Embedding Angular Components (Angular Nodes)
202
-
203
- Turn any Angular component into a TipTap node without writing extension code. This project makes it easy to map your existing Angular components directly to the editor's document structure.
204
-
205
- ```typescript
206
- import { Component } from "@angular/core";
207
- import {
208
- AngularTiptapEditorComponent,
209
- AteEditorConfig,
210
- AteAngularNode,
211
- } from "@flogeez/angular-tiptap-editor";
212
- import { MyCounterComponent } from "./my-counter.component";
213
-
214
- @Component({
215
- selector: "app-custom-nodes",
216
- standalone: true,
217
- imports: [AngularTiptapEditorComponent],
218
- template: ` <angular-tiptap-editor [config]="editorConfig" /> `,
219
- })
220
- export class CustomNodesComponent {
221
- // Use AteAngularNode for explicit typing if needed
222
- myNodes: AteAngularNode[] = [
223
- {
224
- component: MyCounterComponent,
225
- name: "counter",
226
- attributes: { count: { default: 0 } },
227
- group: "block",
228
- draggable: true,
229
- },
230
- ];
231
-
232
- editorConfig: AteEditorConfig = {
233
- angularNodes: this.myNodes,
234
- };
235
- }
236
- ```
237
-
238
- > **Note**: Your component can inherit from `AteAngularNodeView` to access the full TipTap API (`editor`, `node`, `updateAttributes`) via Signals!
239
-
240
- ### 3. Using EditorCommandsService
241
-
242
- Deep programmatic control over any editor instance.
243
-
244
- ```typescript
245
- import { Component, inject } from "@angular/core";
246
- import { AteEditorCommandsService } from "@flogeez/angular-tiptap-editor";
247
- import { Editor } from "@tiptap/core";
248
-
249
- @Component({
250
- selector: "app-commands",
251
- standalone: true,
252
- template: `
253
- <div class="controls">
254
- <button (click)="clearContent()">Clear</button>
255
- <button (click)="focusEditor()">Focus</button>
256
- <button (click)="setContent()">Set Content</button>
257
- </div>
258
- <angular-tiptap-editor (editorCreated)="onEditorCreated($event)" />
259
- `,
260
- })
261
- export class CommandsComponent {
262
- private editorCommandsService = inject(AteEditorCommandsService);
263
- private editor: Editor | null = null;
264
-
265
- onEditorCreated(editor: Editor) {
266
- this.editor = editor;
267
- }
268
-
269
- clearContent() {
270
- if (this.editor) this.editorCommandsService.clearContent(this.editor);
271
- }
272
-
273
- focusEditor() {
274
- if (this.editor) this.editorCommandsService.focus(this.editor);
275
- }
276
-
277
- setContent() {
278
- if (this.editor) this.editorCommandsService.setContent(this.editor, "<h1>New!</h1>");
279
- }
280
- }
281
- ```
282
-
283
- ### 4. Custom Tiptap Extensions (Low Level)
284
-
285
- Standard TipTap extensions can be passed via the `tiptapExtensions` property in your config.
286
-
287
- ```typescript
288
- import { AteEditorConfig } from "@flogeez/angular-tiptap-editor";
289
-
290
- @Component({
291
- template: ` <angular-tiptap-editor [config]="editorConfig" /> `,
292
- })
293
- export class CustomExtensionsComponent {
294
- editorConfig: AteEditorConfig = {
295
- tiptapExtensions: [
296
- /* Standard TipTap extensions (Highlight, Link, etc.) */
297
- ],
298
- };
299
- }
300
- ```
301
-
302
- ### 5. Extending Reactive Editor State (Calculators)
303
-
304
- The editor features a dual-layer state architecture for maximum reactivity.
305
-
306
- #### A. Automatic Extension Tracking (Zero Config)
307
-
308
- Any TipTap **Mark** or **Node** you add to `tiptapExtensions` is automatically tracked. You don't need extra code to make them reactive.
309
-
310
- - **For Marks**: `state().marks.yourExtensionName` (boolean).
311
- - **For Nodes**: `state().nodes.yourExtensionName` (boolean).
312
-
313
- #### B. Custom State Calculators (Advanced)
314
-
315
- Extract complex data (attributes, depth, custom logic) via specialized Calculators.
316
-
317
- **1. Define a Calculator**:
318
-
319
- ```typescript
320
- import { AteStateCalculator } from "@flogeez/angular-tiptap-editor";
321
-
322
- // Called on every editor update
323
- export const MyCustomCalculator: AteStateCalculator = editor => ({
324
- custom: { selectionDepth: editor.state.selection.$from.depth },
325
- });
326
- ```
95
+ ## ⚙️ Configuration
327
96
 
328
- **2. Register in the Config**:
97
+ The editor is fully configurable via the `[config]` input:
329
98
 
330
99
  ```typescript
331
- import { AteEditorConfig } from "@flogeez/angular-tiptap-editor";
332
-
333
100
  editorConfig: AteEditorConfig = {
334
- stateCalculators: [MyCustomCalculator],
101
+ locale: "fr",
102
+ placeholder: "Commencez à rédiger...",
103
+ toolbar: { ...ATE_DEFAULT_TOOLBAR_CONFIG, highlight: true },
104
+ slashCommands: { heading1: true, table: true },
335
105
  };
336
106
  ```
337
107
 
338
- ```html
339
- <angular-tiptap-editor [config]="editorConfig" />
340
- ```
341
-
342
- **3. Consume the State**:
343
-
344
- ```typescript
345
- @Component({ ... })
346
- export class MyToolbarComponent {
347
- private editorCommands = inject(AteEditorCommandsService);
348
-
349
- // Access your custom data reactively via Signals!
350
- depth = computed(() => this.editorCommands.editorState().custom?.selectionDepth);
351
- }
352
- ```
353
-
354
- ## ✨ Key Features
355
-
356
- ### 📊 Table Management
357
-
358
- Full table support with intuitive bubble menus:
108
+ | Input | Type | Description |
109
+ | ------------ | ----------------- | ---------------------------------- |
110
+ | `[config]` | `AteEditorConfig` | Global config object (recommended) |
111
+ | `[content]` | `string` | Initial HTML content |
112
+ | `[editable]` | `boolean` | Read-only toggle |
113
+ | `[disabled]` | `boolean` | Disabled toggle |
359
114
 
360
- - **Table Creation**: Insert tables via slash commands (`/table`)
361
- - **Cell Selection**: Click and drag to select multiple cells
362
- - **Bubble Menus**: Context-aware menus for table operations
363
- - **Row/Column Management**: Add, remove, and merge cells
364
- - **Styling**: Custom table styling with proper borders
115
+ _See the [API reference](./API.md) for the full list of inputs and configuration options._
365
116
 
366
- ### Slash Commands
117
+ ## 🧩 Advanced Features
367
118
 
368
- Quick content insertion with slash commands:
119
+ For deeper integration patterns and complex use cases, check our **[Advanced Usage Guide](./ADVANCED.md)**.
369
120
 
370
- - **Headings**: `/h1`, `/h2`, `/h3`
371
- - **Lists**: `/bullet`, `/numbered`
372
- - **Blocks**: `/quote`, `/code`, `/line`
373
- - **Media**: `/image`, `/table`
374
- - **Fully Internationalized**: All commands translated
121
+ ### Custom Angular Nodes
375
122
 
376
- #### Custom Slash Commands
377
-
378
- The `slashCommands` object also allows you to add completely custom command items:
123
+ Embed any Angular component as an editor node (e.g., a dynamic counter, a complex widget, IA block, etc.):
379
124
 
380
125
  ```typescript
381
- import { AteSlashCommandsConfig } from "@flogeez/angular-tiptap-editor";
382
-
383
- slashCommands: AteSlashCommandsConfig = {
384
- // Toggle native commands
385
- heading1: true,
386
- image: false,
387
- // Add custom ones
388
- custom: [
389
- {
390
- title: "Magic Action",
391
- description: "Insert some AI magic",
392
- icon: "auto_fix",
393
- keywords: ["magic", "ai"],
394
- command: editor => editor.commands.insertContent("✨ Magic happened!"),
395
- },
396
- ],
397
- };
126
+ angularNodes: [
127
+ {
128
+ component: MyCounterComponent,
129
+ name: "counter",
130
+ attributes: { count: { default: 0 } },
131
+ group: "block",
132
+ },
133
+ ];
398
134
  ```
399
135
 
400
- ### 🖼️ Advanced Image Handling
401
-
402
- Professional image management:
403
-
404
- - **Drag & Drop**: Drag images directly into the editor
405
- - **File Selection**: Click to select images from device
406
- - **Auto-Compression**: Images automatically compressed (max 1920x1080)
407
- - **Resizable**: Images can be resized with handles
408
- - **Bubble Menu**: Context menu for image operations
409
- - **Custom Upload Handler**: Upload images to your own server instead of base64
410
-
411
- #### Custom Image Upload Handler
412
-
413
- By default, images are converted to base64 and embedded directly in the HTML content. You can provide a custom upload handler to upload images to your own server (S3, Cloudinary, custom API, etc.) and use the returned URL instead.
414
-
415
- The handler can return either an **Observable** or a **Promise**.
416
-
417
- #### Using Observable (recommended for Angular)
418
-
419
- ```typescript
420
- import { Component, inject } from "@angular/core";
421
- import { HttpClient } from "@angular/common/http";
422
- import { map } from "rxjs/operators";
423
- import {
424
- AngularTiptapEditorComponent,
425
- AteImageUploadHandler,
426
- } from "@flogeez/angular-tiptap-editor";
427
-
428
- @Component({
429
- selector: "app-custom-upload",
430
- standalone: true,
431
- imports: [AngularTiptapEditorComponent],
432
- template: `
433
- <angular-tiptap-editor
434
- [content]="content"
435
- [imageUploadHandler]="uploadHandler"
436
- (contentChange)="onContentChange($event)" />
437
- `,
438
- })
439
- export class CustomUploadComponent {
440
- private http = inject(HttpClient);
441
- content = "";
442
-
443
- uploadHandler: AteImageUploadHandler = ctx => {
444
- const formData = new FormData();
445
- formData.append("image", ctx.file);
446
-
447
- return this.http
448
- .post<{ url: string }>("/api/upload", formData)
449
- .pipe(map(result => ({ src: result.url })));
450
- };
451
-
452
- onContentChange(newContent: string) {
453
- this.content = newContent;
454
- }
455
- }
456
- ```
136
+ ### Image Upload Handler
457
137
 
458
- #### Using Promise (async/await)
138
+ Avoid base64 by providing a custom uploader (S3, Cloudinary, etc.):
459
139
 
460
140
  ```typescript
461
- uploadHandler: AteImageUploadHandler = async ctx => {
462
- const formData = new FormData();
463
- formData.append("image", ctx.file);
464
-
465
- const result = await firstValueFrom(this.http.post<{ url: string }>("/api/upload", formData));
466
-
467
- return { src: result.url };
141
+ uploadHandler: AteImageUploadHandler = ctx => {
142
+ return this.http.post<any>("/api/upload", ctx.file).pipe(map(res => ({ src: res.url })));
468
143
  };
469
144
  ```
470
145
 
471
- The `ImageUploadContext` provides:
472
-
473
- - `file`: The original File object
474
- - `width`: Processed image width
475
- - `height`: Processed image height
476
- - `type`: MIME type (e.g., 'image/jpeg')
477
- - `base64`: Base64 data URL of the processed image (fallback)
478
-
479
- The handler must return an `ImageUploadHandlerResult` with at least a `src` property containing the image URL.
480
-
481
- ---
482
-
483
- ### 📝 Word & Character Counting
484
-
485
- Real-time content statistics:
486
-
487
- - **Live Updates**: Counters update as you type
488
- - **Proper Pluralization**: "1 word" vs "2 words"
489
- - **Separate Counts**: Independent word and character counts
490
- - **Configurable**: Show/hide individual counters
491
-
492
- ## 🎨 Demo
493
-
494
- ### 🌐 Live Demo
495
-
496
- Try the interactive demo online: **[https://flogeez.github.io/angular-tiptap-editor/](https://flogeez.github.io/angular-tiptap-editor/)**
497
-
498
- ### 🖥️ Run Locally
499
-
500
- ```bash
501
- git clone https://github.com/FloGeez/angular-tiptap-editor.git
502
- cd angular-tiptap-editor
503
- npm install
504
- npm start
505
- ```
506
-
507
- Open [http://localhost:4200](http://localhost:4200) to view the demo.
508
-
509
- ## 📖 Documentation
510
-
511
- ### API Reference
512
-
513
- #### Inputs
514
-
515
- | Input | Type | Default | Description |
516
- | --------------------- | ------------------------------------------------ | ------------------- | --------------------------------------------- |
517
- | `config` | `AteEditorConfig` | `{}` | **Global configuration object** (Recommended) |
518
- | `content` | `string` | `""` | Initial HTML content |
519
- | `placeholder` | `string` | `"Start typing..."` | Placeholder text (overrides config) |
520
- | `locale` | `'en' \| 'fr'` | Auto-detect | Editor language (overrides config) |
521
- | `editable` | `boolean` | `true` | Whether editor is editable |
522
- | `height` | `string` | `undefined` | Editor height (e.g. '400px', 'auto') |
523
- | `maxHeight` | `string` | `undefined` | Maximum height (e.g. '80vh') |
524
- | `minHeight` | `string` | `undefined` | Minimum height |
525
- | `maxCharacters` | `number` | `undefined` | Character limit |
526
- | `fillContainer` | `boolean` | `false` | Fill parent container height |
527
- | `autofocus` | `boolean \| 'start' \| 'end' \| 'all' \| number` | `false` | Auto-focus behavior |
528
- | `disabled` | `boolean` | `false` | Disabled state (for forms) |
529
- | `spellcheck` | `boolean` | `true` | Enable browser spellcheck |
530
- | `showToolbar` | `boolean` | `true` | Show toolbar |
531
- | `showFooter` | `boolean` | `true` | Show footer (counters) |
532
- | `showBubbleMenu` | `boolean` | `true` | Show text bubble menu |
533
- | `showImageBubbleMenu` | `boolean` | `true` | Show image bubble menu |
534
- | `showTableMenu` | `boolean` | `true` | Show table bubble menu |
535
- | `showCellMenu` | `boolean` | `true` | Show cell bubble menu |
536
- | `enableSlashCommands` | `boolean` | `true` | Enable slash commands functionality |
537
- | `enableOfficePaste` | `boolean` | `true` | Enable smart Office pasting |
538
- | `showCharacterCount` | `boolean` | `true` | Show character counter |
539
- | `showWordCount` | `boolean` | `true` | Show word counter |
540
- | `toolbar` | `AteToolbarConfig` | All enabled | Detailed toolbar configuration |
541
- | `bubbleMenu` | `AteBubbleMenuConfig` | All enabled | Detailed bubble menu configuration |
542
- | `slashCommands` | `AteSlashCommandsConfig` | All enabled | Detailed slash commands config |
543
- | `imageUploadHandler` | `AteImageUploadHandler` | `undefined` | Custom image upload function |
544
- | `stateCalculators` | `AteStateCalculator[]` | `[]` | Custom reactive state logic |
545
- | `tiptapExtensions` | `(Extension \| Node \| Mark)[]` | `[]` | Additional Tiptap extensions |
546
- | `tiptapOptions` | `Partial<EditorOptions>` | `{}` | Additional Tiptap editor options |
547
-
548
- > **Note on Precedence**: Values provided in individual inputs (e.g., `[editable]="false"`) always take precedence over values defined inside the `[config]` object.
549
-
550
- #### AteEditorConfig Reference
551
-
552
- The `AteEditorConfig` nested structure allows for complex configurations while remaining flat for core settings:
553
-
554
- ```typescript
555
- export interface AteEditorConfig {
556
- // Core Settings
557
- theme?: "light" | "dark" | "auto";
558
- height?: string;
559
- minHeight?: string;
560
- maxHeight?: string;
561
- fillContainer?: boolean;
562
- autofocus?: "start" | "end" | "all" | boolean | number;
563
- placeholder?: string;
564
- editable?: boolean;
565
- disabled?: boolean;
566
- locale?: string;
567
- spellcheck?: boolean;
568
- enableOfficePaste?: boolean;
569
-
570
- // Visibility Options
571
- showToolbar?: boolean;
572
- showFooter?: boolean;
573
- showCharacterCount?: boolean;
574
- showWordCount?: boolean;
575
- showEditToggle?: boolean;
576
- showBubbleMenu?: boolean;
577
- showImageBubbleMenu?: boolean;
578
- showTableMenu?: boolean;
579
- showCellMenu?: boolean;
580
- enableSlashCommands?: boolean;
581
- maxCharacters?: number;
582
-
583
- // Complex Modules
584
- toolbar?: AteToolbarConfig;
585
- bubbleMenu?: AteBubbleMenuConfig;
586
- imageBubbleMenu?: AteImageBubbleMenuConfig;
587
- tableBubbleMenu?: AteTableBubbleMenuConfig;
588
- cellBubbleMenu?: AteCellBubbleMenuConfig;
589
- slashCommands?: AteSlashCommandsConfig;
590
- imageUpload?: AteImageUploadConfig;
591
- }
592
- ```
593
-
594
- #### Image Upload Configuration
595
-
596
- The `imageUpload` property in `AteEditorConfig` provides fine-grained control over the image processing pipeline:
597
-
598
- ```typescript
599
- export interface AteImageUploadConfig {
600
- /** Custom handler to upload files to a server */
601
- handler?: AteImageUploadHandler;
602
- /** Maximum file size in bytes (default: 10MB) */
603
- maxFileSize?: number;
604
- /** Accepted file types (default: 'image/*') */
605
- accept?: string;
606
- /** Whether to automatically compress images before upload (default: true) */
607
- autoCompress?: boolean;
608
- }
609
- ```
610
-
611
- #### Outputs
612
-
613
- | Output | Type | Description |
614
- | --------------- | ----------------- | ------------------------------- |
615
- | `contentChange` | `string` | Emitted when content changes |
616
- | `editorCreated` | `Editor` | Emitted when editor is created |
617
- | `editorUpdate` | `{editor, trans}` | Emitted on every editor update |
618
- | `editorFocus` | `{editor, event}` | Emitted when editor gains focus |
619
- | `editorBlur` | `{editor, event}` | Emitted when editor loses focus |
620
-
621
- ## 🌍 Internationalization
622
-
623
- The editor comes with built-in support for **English (en)** and **French (fr)**, featuring automatic browser language detection.
624
-
625
- ### Basic Usage
626
-
627
- ```typescript
628
- // Force a specific language
629
- <angular-tiptap-editor [locale]="'fr'" />
630
-
631
- // Auto-detect (default)
632
- <angular-tiptap-editor />
633
- ```
634
-
635
- ### Adding Custom Languages
636
-
637
- You can easily extend the editor with new languages or override existing labels using the `AteI18nService`:
638
-
639
- ```typescript
640
- import { AteI18nService } from "@flogeez/angular-tiptap-editor";
641
-
642
- @Component({ ... })
643
- export class MyComponent {
644
- constructor(private i18nService: AteI18nService) {
645
- // Add Spanish support
646
- this.i18nService.addTranslations('es', {
647
- toolbar: { bold: 'Negrita', italic: 'Cursiva', ... },
648
- editor: { placeholder: 'Empieza a escribir...' }
649
- });
650
-
651
- // Switch to Spanish
652
- this.i18nService.setLocale('es');
653
- }
654
- }
655
- ```
656
-
657
- ### 🎨 CSS Custom Properties
658
-
659
- Customize the editor appearance using CSS variables with the `--ate-` prefix:
660
-
661
- ```css
662
- /* In your global styles or component styles */
663
- angular-tiptap-editor {
664
- --ate-primary: #2563eb;
665
- --ate-primary-contrast: #ffffff;
666
- --ate-primary-light: color-mix(in srgb, var(--ate-primary), transparent 90%);
667
- --ate-primary-lighter: color-mix(in srgb, var(--ate-primary), transparent 95%);
668
- --ate-primary-light-alpha: color-mix(in srgb, var(--ate-primary), transparent 85%);
669
-
670
- --ate-surface: #ffffff;
671
- --ate-surface-secondary: #f8f9fa;
672
- --ate-surface-tertiary: #f1f5f9;
673
-
674
- --ate-text: #2d3748;
675
- --ate-text-secondary: #64748b;
676
- --ate-text-muted: #a0aec0;
677
-
678
- --ate-border: #e2e8f0;
679
-
680
- /* And More... */
681
- }
682
- ```
683
-
684
- #### Dark Mode Support
685
-
686
- The editor supports dark mode in two ways:
687
-
688
- **1. With CSS Class**
689
-
690
- ```html
691
- <angular-tiptap-editor [class.dark]="isDarkMode" />
692
- ```
693
-
694
- **2. With Data Attribute**
695
-
696
- ```html
697
- <angular-tiptap-editor [attr.data-theme]="isDarkMode ? 'dark' : null" />
698
- ```
699
-
700
- #### Example: Custom Dark Theme
701
-
702
- ```css
703
- angular-tiptap-editor.dark {
704
- --ate-background: #1a1a2e;
705
- --ate-border-color: #3d3d5c;
706
- --ate-focus-color: #6366f1;
707
- --ate-text-color: #e2e8f0;
708
- --ate-placeholder-color: #64748b;
709
- --ate-counter-background: #2d2d44;
710
- --ate-counter-color: #94a3b8;
711
- --ate-blockquote-background: #2d2d44;
712
- --ate-code-background: #2d2d44;
713
- }
714
- ```
715
-
716
- #### Example: Custom Brand Colors
717
-
718
- ```css
719
- angular-tiptap-editor {
720
- --ate-focus-color: #8b5cf6;
721
- --ate-image-selected-color: #8b5cf6;
722
- --ate-border-radius: 12px;
723
- }
724
- ```
725
-
726
- ### ⚡ Reactive State & OnPush
727
-
728
- The library exposes a reactive `editorState` signal via the `AteEditorCommandsService`. This signal contains everything you need to build custom UIs around the editor:
729
-
730
- - **Active State**: Check if `bold`, `italic`, or custom marks are active.
731
- - **Commands Availability**: Check if `undo`, `redo`, or custom commands can be executed.
732
- - **Structural Data**: Access table status, image attributes, or selection details.
733
-
734
- Since it's built with Signals, your custom toolbar items or UI overlays will only re-render when the specific data they consume changes, making it extremely efficient for `OnPush` applications.
735
-
736
- ## 🏗️ Architecture
146
+ ## 🌍 i18n & 🎨 Styling
737
147
 
738
- ### Reactive State Management
739
-
740
- The library uses a **Snapshot & Signal** pattern to bridge Tiptap and Angular.
741
-
742
- 1. **State Snapshot**: Every editor transaction triggers a set of "Calculators" that produce a single immutable state object.
743
- 2. **Specialized Calculators**: Logic is modularized into specialized functions (Marks, Table, Image, etc.) and a **Discovery Calculator** for automatic extension detection.
744
- 3. **Signals Integration**: This snapshot is stored in a single Angular Signal. Sub-components (toolbar, menus) consume this signal only where needed.
745
- 4. **Change Detection Optimization**: A custom equality check on the signal prevents unnecessary re-renders when the visual state of the editor hasn't changed.
746
-
747
- ### Core Services
748
-
749
- - **`AteEditorCommandsService`**: Exposes the `editorState` signal and provides a centralized API for executing Tiptap commands.
750
- - **`AteImageService`**: Manages the image processing pipeline (selection, compression, and server-side upload handling).
751
- - **`AteI18nService`**: Reactive translation service with support for browser locale auto-detection.
752
-
753
- ### Isolated Instances
754
-
755
- Each component instance provides its own set of services (`AteEditorCommandsService`, `AteImageService`, etc.) at the component level. This ensures that multiple editors on the same page maintain independent states and configurations without interference.
756
-
757
- ### Modern Angular Integration
758
-
759
- - **Signals**: Native reactivity for efficient UI updates.
760
- - **OnPush**: Designed for `ChangeDetectionStrategy.OnPush` throughout.
761
- - **Typed State**: Fully typed interfaces for the editor state and configurations.
762
-
763
- ### Default Configurations
764
-
765
- The library provides default configurations that can be imported and customized:
766
-
767
- ```typescript
768
- import {
769
- ATE_DEFAULT_TOOLBAR_CONFIG,
770
- ATE_DEFAULT_BUBBLE_MENU_CONFIG,
771
- ATE_DEFAULT_IMAGE_BUBBLE_MENU_CONFIG,
772
- ATE_DEFAULT_TABLE_MENU_CONFIG,
773
- ATE_DEFAULT_SLASH_COMMANDS_CONFIG,
774
- } from "@flogeez/angular-tiptap-editor";
775
- ```
148
+ - **Languages**: Detects browser language automatically. Extend with `AteI18nService.addTranslations()`.
149
+ - **Styling**: Customize via CSS variables (e.g., `--ate-primary`, `--ate-border-radius`, and much more).
150
+ - **Dark Mode**: Works natively by adding `.dark` or `[data-theme="dark"]`.
776
151
 
777
152
  ## 🔧 Development
778
153
 
779
- ### Build Library
780
-
781
154
  ```bash
782
- npm run build:lib
783
- ```
784
-
785
- ### Watch Mode (Development)
786
-
787
- ```bash
788
- npm run dev
155
+ npm install
156
+ npm run build:lib # Build library
157
+ npm start # Run demo
789
158
  ```
790
159
 
791
- This runs the library in watch mode and starts the demo application.
792
-
793
- ### Available Scripts
794
-
795
- - `npm start` - Start demo application
796
- - `npm run build` - Build demo application
797
- - `npm run build:lib` - Build library
798
- - `npm run watch:lib` - Watch library changes
799
- - `npm run dev` - Development mode (watch + serve)
800
-
801
160
  ## 📝 License
802
161
 
803
162
  MIT License - see [LICENSE](LICENSE) file for details.
@@ -809,19 +168,8 @@ Contributions are welcome! Please feel free to submit a Pull Request.
809
168
  ## 🔗 Links
810
169
 
811
170
  - 📖 [Tiptap Documentation](https://tiptap.dev/)
812
- - 🅰️ [Angular Documentation](https://angular.dev/)
813
171
  - 📦 [NPM Package](https://www.npmjs.com/package/@flogeez/angular-tiptap-editor)
814
172
  - 📖 [Live Demo](https://flogeez.github.io/angular-tiptap-editor/)
815
- - 🐛 [Report Issues](https://github.com/FloGeez/angular-tiptap-editor/issues)
816
- - 💡 [Feature Requests](https://github.com/FloGeez/angular-tiptap-editor/issues)
817
-
818
- ## 🆕 What's New
819
-
820
- ### Latest Updates
821
-
822
- - ✅ **Seamless Integration**: Drastically simplified setup with `provideAteEditor()` and declarative `nodeViews`.
823
- - ✅ **Universal Component Engine**: Embed any Angular component as an editor node.
824
-
825
- ---
173
+ - 🐛 [Report Issues / Feature Requests](https://github.com/FloGeez/angular-tiptap-editor/issues)
826
174
 
827
175
  Made with ❤️ by [FloGeez](https://github.com/FloGeez)