@flogeez/angular-tiptap-editor 2.3.0 → 3.0.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/CHANGELOG.md +30 -0
- package/README.md +135 -93
- package/fesm2022/flogeez-angular-tiptap-editor.mjs +992 -840
- package/fesm2022/flogeez-angular-tiptap-editor.mjs.map +1 -1
- package/index.d.ts +303 -263
- package/package.json +13 -21
|
@@ -1,26 +1,22 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, output, ChangeDetectionStrategy, Component, signal, computed, Injectable, inject, viewChild, effect, Directive, DestroyRef,
|
|
2
|
+
import { input, output, ChangeDetectionStrategy, Component, signal, computed, Injectable, inject, viewChild, effect, Directive, ApplicationRef, EnvironmentInjector, createComponent, InjectionToken, DestroyRef, Injector, untracked, makeEnvironmentProviders, provideEnvironmentInitializer } from '@angular/core';
|
|
3
3
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
4
4
|
import { Node as Node$1, nodeInputRule, mergeAttributes, Extension, getAttributes, Editor } from '@tiptap/core';
|
|
5
5
|
import StarterKit from '@tiptap/starter-kit';
|
|
6
|
-
import Placeholder from '@tiptap/
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import Color from '@tiptap/extension-color';
|
|
6
|
+
import { Placeholder, CharacterCount } from '@tiptap/extensions';
|
|
7
|
+
import { Underline } from '@tiptap/extension-underline';
|
|
8
|
+
import { Superscript } from '@tiptap/extension-superscript';
|
|
9
|
+
import { Subscript } from '@tiptap/extension-subscript';
|
|
10
|
+
import { TextAlign } from '@tiptap/extension-text-align';
|
|
11
|
+
import { Link } from '@tiptap/extension-link';
|
|
12
|
+
import { Highlight } from '@tiptap/extension-highlight';
|
|
13
|
+
import { TextStyle } from '@tiptap/extension-text-style';
|
|
14
|
+
import { Color } from '@tiptap/extension-color';
|
|
16
15
|
import OfficePaste from '@intevation/tiptap-extension-office-paste';
|
|
17
16
|
import { Plugin, PluginKey, TextSelection, NodeSelection } from '@tiptap/pm/state';
|
|
18
17
|
import { DecorationSet, Decoration } from '@tiptap/pm/view';
|
|
19
|
-
import Table from '@tiptap/extension-table';
|
|
20
|
-
import
|
|
21
|
-
import TableCell from '@tiptap/extension-table-cell';
|
|
22
|
-
import TableHeader from '@tiptap/extension-table-header';
|
|
23
|
-
import { isObservable, firstValueFrom, concat, defer, of, tap, Subscription } from 'rxjs';
|
|
18
|
+
import { Table, TableRow, TableHeader, TableCell } from '@tiptap/extension-table';
|
|
19
|
+
import { isObservable, firstValueFrom, Subscription, concat, defer, of, tap } from 'rxjs';
|
|
24
20
|
import { CommonModule } from '@angular/common';
|
|
25
21
|
import tippy, { sticky } from 'tippy.js';
|
|
26
22
|
import * as i1 from '@angular/forms';
|
|
@@ -1679,7 +1675,8 @@ class AteColorPickerService {
|
|
|
1679
1675
|
let trigger;
|
|
1680
1676
|
if (event && typeof event !== "string") {
|
|
1681
1677
|
const target = event.target;
|
|
1682
|
-
trigger =
|
|
1678
|
+
trigger =
|
|
1679
|
+
event.currentTarget || target?.closest("button") || target;
|
|
1683
1680
|
}
|
|
1684
1681
|
this.open(mode, trigger);
|
|
1685
1682
|
}
|
|
@@ -1883,7 +1880,9 @@ class AteLinkService {
|
|
|
1883
1880
|
if (urlOrEvent && typeof urlOrEvent !== "string") {
|
|
1884
1881
|
const target = urlOrEvent.target;
|
|
1885
1882
|
trigger =
|
|
1886
|
-
urlOrEvent.currentTarget ||
|
|
1883
|
+
urlOrEvent.currentTarget ||
|
|
1884
|
+
target?.closest("button") ||
|
|
1885
|
+
target;
|
|
1887
1886
|
}
|
|
1888
1887
|
// Open the edit menu
|
|
1889
1888
|
this.open(trigger);
|
|
@@ -2375,7 +2374,7 @@ class AteEditorCommandsService {
|
|
|
2375
2374
|
if (!editor) {
|
|
2376
2375
|
return;
|
|
2377
2376
|
}
|
|
2378
|
-
editor.commands.setContent(content, emitUpdate);
|
|
2377
|
+
editor.commands.setContent(content, { emitUpdate });
|
|
2379
2378
|
}
|
|
2380
2379
|
setEditable(editor, editable) {
|
|
2381
2380
|
if (!editor) {
|
|
@@ -3138,7 +3137,7 @@ class AteBaseBubbleMenu {
|
|
|
3138
3137
|
content: this.menuRef().nativeElement,
|
|
3139
3138
|
trigger: "manual",
|
|
3140
3139
|
placement: "top-start",
|
|
3141
|
-
appendTo: () =>
|
|
3140
|
+
appendTo: () => ed?.options?.element || document.body,
|
|
3142
3141
|
interactive: true,
|
|
3143
3142
|
hideOnClick: false,
|
|
3144
3143
|
arrow: false,
|
|
@@ -3151,7 +3150,7 @@ class AteBaseBubbleMenu {
|
|
|
3151
3150
|
{
|
|
3152
3151
|
name: "preventOverflow",
|
|
3153
3152
|
options: {
|
|
3154
|
-
boundary: this.editor().options.element,
|
|
3153
|
+
boundary: this.editor().options.element || document.body,
|
|
3155
3154
|
padding: 8,
|
|
3156
3155
|
},
|
|
3157
3156
|
},
|
|
@@ -3808,7 +3807,9 @@ class AteTableBubbleMenuComponent extends AteBaseBubbleMenu {
|
|
|
3808
3807
|
// 1. Direct ProseMirror approach: get DOM node at position
|
|
3809
3808
|
const dom = ed.view.domAtPos(from).node;
|
|
3810
3809
|
// Find closest table element
|
|
3811
|
-
const tableElement = dom instanceof HTMLElement
|
|
3810
|
+
const tableElement = dom instanceof HTMLElement
|
|
3811
|
+
? dom.closest("table")
|
|
3812
|
+
: dom.parentElement?.closest("table");
|
|
3812
3813
|
if (tableElement) {
|
|
3813
3814
|
return tableElement.getBoundingClientRect();
|
|
3814
3815
|
}
|
|
@@ -4209,7 +4210,7 @@ class AteBaseSubBubbleMenu {
|
|
|
4209
4210
|
content: this.menuRef().nativeElement,
|
|
4210
4211
|
trigger: "manual",
|
|
4211
4212
|
placement: "bottom-start",
|
|
4212
|
-
appendTo: () => ed
|
|
4213
|
+
appendTo: () => ed?.options?.element || document.body,
|
|
4213
4214
|
interactive: true,
|
|
4214
4215
|
arrow: false,
|
|
4215
4216
|
offset: [0, 8],
|
|
@@ -4221,7 +4222,10 @@ class AteBaseSubBubbleMenu {
|
|
|
4221
4222
|
modifiers: [
|
|
4222
4223
|
{
|
|
4223
4224
|
name: "preventOverflow",
|
|
4224
|
-
options: {
|
|
4225
|
+
options: {
|
|
4226
|
+
boundary: ed?.options?.element || document.body,
|
|
4227
|
+
padding: 8,
|
|
4228
|
+
},
|
|
4225
4229
|
},
|
|
4226
4230
|
{
|
|
4227
4231
|
name: "flip",
|
|
@@ -5134,7 +5138,7 @@ class AteSlashCommandsComponent {
|
|
|
5134
5138
|
{
|
|
5135
5139
|
name: "preventOverflow",
|
|
5136
5140
|
options: {
|
|
5137
|
-
boundary: this.editor().options.element,
|
|
5141
|
+
boundary: this.editor().options.element || document.body,
|
|
5138
5142
|
padding: 8,
|
|
5139
5143
|
},
|
|
5140
5144
|
},
|
|
@@ -5391,202 +5395,638 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
5391
5395
|
}]
|
|
5392
5396
|
}] });
|
|
5393
5397
|
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5398
|
+
/**
|
|
5399
|
+
* Base abstract class for custom 'Angular Nodes'.
|
|
5400
|
+
*
|
|
5401
|
+
* Extend this class in your custom components to automatically receive TipTap editor
|
|
5402
|
+
* properties (node attributes, selection state, etc.) as reactive Signals.
|
|
5403
|
+
*
|
|
5404
|
+
* @example
|
|
5405
|
+
* ```typescript
|
|
5406
|
+
* @Component({
|
|
5407
|
+
* selector: 'app-counter',
|
|
5408
|
+
* template: `
|
|
5409
|
+
* <div>
|
|
5410
|
+
* <button (click)="increment()">Count: {{ node().attrs['count'] }}</button>
|
|
5411
|
+
* </div>
|
|
5412
|
+
* `
|
|
5413
|
+
* })
|
|
5414
|
+
* export class CounterComponent extends AteAngularNodeView {
|
|
5415
|
+
* increment() {
|
|
5416
|
+
* const count = this.node().attrs['count'] || 0;
|
|
5417
|
+
* this.updateAttributes({ count: count + 1 });
|
|
5418
|
+
* }
|
|
5419
|
+
* }
|
|
5420
|
+
* ```
|
|
5421
|
+
*/
|
|
5422
|
+
class AteAngularNodeView {
|
|
5423
|
+
/**
|
|
5424
|
+
* Internal method to initialize the component with NodeView props.
|
|
5425
|
+
* This is called by the AngularNodeViewRenderer.
|
|
5426
|
+
*
|
|
5427
|
+
* @internal
|
|
5428
|
+
*/
|
|
5429
|
+
_initNodeView(props) {
|
|
5430
|
+
// Create signals from the props
|
|
5431
|
+
const editorSignal = signal(props.editor, ...(ngDevMode ? [{ debugName: "editorSignal" }] : []));
|
|
5432
|
+
const nodeSignal = signal(props.node, ...(ngDevMode ? [{ debugName: "nodeSignal" }] : []));
|
|
5433
|
+
const decorationsSignal = signal(props.decorations, ...(ngDevMode ? [{ debugName: "decorationsSignal" }] : []));
|
|
5434
|
+
const selectedSignal = signal(props.selected, ...(ngDevMode ? [{ debugName: "selectedSignal" }] : []));
|
|
5435
|
+
const extensionSignal = signal(props.extension, ...(ngDevMode ? [{ debugName: "extensionSignal" }] : []));
|
|
5436
|
+
// Assign to the component
|
|
5437
|
+
this.editor = editorSignal.asReadonly();
|
|
5438
|
+
this.node = nodeSignal.asReadonly();
|
|
5439
|
+
this.decorations = decorationsSignal.asReadonly();
|
|
5440
|
+
this.selected = selectedSignal.asReadonly();
|
|
5441
|
+
this.extension = extensionSignal.asReadonly();
|
|
5442
|
+
this.getPos = props.getPos;
|
|
5443
|
+
this.updateAttributes = props.updateAttributes;
|
|
5444
|
+
this.deleteNode = props.deleteNode;
|
|
5445
|
+
// Store writable signals for updates
|
|
5446
|
+
this._writableSignals = {
|
|
5447
|
+
node: nodeSignal,
|
|
5448
|
+
decorations: decorationsSignal,
|
|
5449
|
+
selected: selectedSignal,
|
|
5450
|
+
};
|
|
5400
5451
|
}
|
|
5401
|
-
|
|
5402
|
-
|
|
5452
|
+
/**
|
|
5453
|
+
* Internal method to update the component when the node changes.
|
|
5454
|
+
* This is called by the AngularNodeViewRenderer.
|
|
5455
|
+
*
|
|
5456
|
+
* @internal
|
|
5457
|
+
*/
|
|
5458
|
+
_updateNodeView(node, decorations) {
|
|
5459
|
+
if (this._writableSignals) {
|
|
5460
|
+
this._writableSignals.node.set(node);
|
|
5461
|
+
this._writableSignals.decorations.set(decorations);
|
|
5462
|
+
}
|
|
5403
5463
|
}
|
|
5404
|
-
|
|
5405
|
-
|
|
5464
|
+
/**
|
|
5465
|
+
* Internal method to update the selection state.
|
|
5466
|
+
* This is called by the AngularNodeViewRenderer.
|
|
5467
|
+
*
|
|
5468
|
+
* @internal
|
|
5469
|
+
*/
|
|
5470
|
+
_selectNodeView() {
|
|
5471
|
+
if (this._writableSignals) {
|
|
5472
|
+
this._writableSignals.selected.set(true);
|
|
5473
|
+
}
|
|
5406
5474
|
}
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5475
|
+
/**
|
|
5476
|
+
* Internal method to update the deselection state.
|
|
5477
|
+
* This is called by the AngularNodeViewRenderer.
|
|
5478
|
+
*
|
|
5479
|
+
* @internal
|
|
5480
|
+
*/
|
|
5481
|
+
_deselectNodeView() {
|
|
5482
|
+
if (this._writableSignals) {
|
|
5483
|
+
this._writableSignals.selected.set(false);
|
|
5484
|
+
}
|
|
5410
5485
|
}
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
type:
|
|
5416
|
-
|
|
5417
|
-
to,
|
|
5418
|
-
empty: selection.empty || (selectionType === "text" && from === to),
|
|
5419
|
-
isSingleCell: isSingleCell,
|
|
5420
|
-
},
|
|
5421
|
-
nodes: {
|
|
5422
|
-
activeNodeName: editor.state.doc.nodeAt(editor.state.selection.$head.pos)?.type.name || null,
|
|
5423
|
-
},
|
|
5424
|
-
};
|
|
5425
|
-
};
|
|
5486
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AteAngularNodeView, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
5487
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.16", type: AteAngularNodeView, isStandalone: true, ngImport: i0 }); }
|
|
5488
|
+
}
|
|
5489
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AteAngularNodeView, decorators: [{
|
|
5490
|
+
type: Directive
|
|
5491
|
+
}] });
|
|
5426
5492
|
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5493
|
+
/**
|
|
5494
|
+
* Universal Renderer for Angular Components as TipTap NodeViews.
|
|
5495
|
+
*
|
|
5496
|
+
* Supports:
|
|
5497
|
+
* - TipTap-Aware components (extending AteAngularNodeView)
|
|
5498
|
+
* - Standard library components (automatic @Input() sync)
|
|
5499
|
+
* - Signal-based inputs and outputs (Angular 18+)
|
|
5500
|
+
* - Content projection (editableContent)
|
|
5501
|
+
* - Unified lifecycle and event management
|
|
5502
|
+
*/
|
|
5503
|
+
function AteNodeViewRenderer(component, options) {
|
|
5504
|
+
return props => {
|
|
5505
|
+
const { node, view: _view, getPos, decorations, editor, extension } = props;
|
|
5506
|
+
const { injector, inputs = {}, wrapperTag = "div", wrapperClass, editableContent = false, contentSelector, onOutput, ignoreMutation = true, } = options;
|
|
5507
|
+
const dom = document.createElement(wrapperTag);
|
|
5508
|
+
if (wrapperClass) {
|
|
5509
|
+
dom.className = wrapperClass;
|
|
5440
5510
|
}
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5511
|
+
const applicationRef = injector.get(ApplicationRef);
|
|
5512
|
+
const environmentInjector = injector.get(EnvironmentInjector);
|
|
5513
|
+
// 1. Setup Content Projection (ng-content)
|
|
5514
|
+
let initialNodes = [];
|
|
5515
|
+
let contentDOM = null;
|
|
5516
|
+
if (editableContent && !contentSelector) {
|
|
5517
|
+
contentDOM = document.createElement("div");
|
|
5518
|
+
contentDOM.setAttribute("data-node-view-content", "");
|
|
5519
|
+
initialNodes = [[contentDOM]];
|
|
5445
5520
|
}
|
|
5446
|
-
|
|
5447
|
-
|
|
5521
|
+
// 2. Create the Angular Component
|
|
5522
|
+
const componentRef = createComponent(component, {
|
|
5523
|
+
environmentInjector,
|
|
5524
|
+
elementInjector: injector,
|
|
5525
|
+
projectableNodes: initialNodes,
|
|
5526
|
+
});
|
|
5527
|
+
const instance = componentRef.instance;
|
|
5528
|
+
const subscriptions = [];
|
|
5529
|
+
// 3. Initialize TipTap-Aware instances
|
|
5530
|
+
if (instance instanceof AteAngularNodeView) {
|
|
5531
|
+
instance._initNodeView({
|
|
5532
|
+
editor,
|
|
5533
|
+
node,
|
|
5534
|
+
decorations,
|
|
5535
|
+
selected: false,
|
|
5536
|
+
extension,
|
|
5537
|
+
getPos,
|
|
5538
|
+
updateAttributes: attrs => editor.commands.updateAttributes(node.type.name, attrs),
|
|
5539
|
+
deleteNode: () => {
|
|
5540
|
+
const pos = getPos();
|
|
5541
|
+
if (pos !== undefined) {
|
|
5542
|
+
editor.commands.deleteRange({ from: pos, to: pos + node.nodeSize });
|
|
5543
|
+
}
|
|
5544
|
+
},
|
|
5545
|
+
});
|
|
5448
5546
|
}
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5547
|
+
// 4. Synchronize Inputs (Handles standard @Input and Signal-based inputs)
|
|
5548
|
+
const syncInputs = (nodeToSync) => {
|
|
5549
|
+
// Combine base inputs from options with dynamic node attributes
|
|
5550
|
+
const mergedInputs = { ...inputs, ...nodeToSync.attrs };
|
|
5551
|
+
Object.entries(mergedInputs).forEach(([key, value]) => {
|
|
5552
|
+
if (key !== "id" && value !== null && value !== undefined) {
|
|
5553
|
+
try {
|
|
5554
|
+
componentRef.setInput(key, value);
|
|
5555
|
+
}
|
|
5556
|
+
catch {
|
|
5557
|
+
// Silently ignore inputs that don't exist on the component
|
|
5558
|
+
}
|
|
5559
|
+
}
|
|
5560
|
+
});
|
|
5561
|
+
};
|
|
5562
|
+
syncInputs(node);
|
|
5563
|
+
// 5. Setup Outputs (Handles EventEmitter and OutputRef)
|
|
5564
|
+
if (onOutput) {
|
|
5565
|
+
Object.entries(instance).forEach(([key, potentialOutput]) => {
|
|
5566
|
+
if (potentialOutput &&
|
|
5567
|
+
typeof potentialOutput
|
|
5568
|
+
.subscribe === "function") {
|
|
5569
|
+
const sub = potentialOutput.subscribe((value) => {
|
|
5570
|
+
onOutput(key, value);
|
|
5571
|
+
});
|
|
5572
|
+
if (sub instanceof Subscription) {
|
|
5573
|
+
subscriptions.push(sub);
|
|
5574
|
+
}
|
|
5575
|
+
}
|
|
5576
|
+
});
|
|
5456
5577
|
}
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
code: isCode,
|
|
5469
|
-
superscript: editor.isActive("superscript"),
|
|
5470
|
-
subscript: editor.isActive("subscript"),
|
|
5471
|
-
highlight: editor.isActive("highlight"),
|
|
5472
|
-
link: editor.isActive("link"),
|
|
5473
|
-
linkHref: editor.getAttributes("link")["href"] || null,
|
|
5474
|
-
color: colorMark,
|
|
5475
|
-
computedColor: colorMark || getStyle("color"),
|
|
5476
|
-
background: backgroundMark,
|
|
5477
|
-
computedBackground: backgroundMark || getStyle("background-color"),
|
|
5478
|
-
linkOpenOnClick: editor.extensionManager.extensions.find(ext => ext.name === "link")?.options?.openOnClick ??
|
|
5479
|
-
false,
|
|
5480
|
-
},
|
|
5481
|
-
can: {
|
|
5482
|
-
toggleBold: marksAllowed && !isInsideInlineCode && editor.can().toggleBold(),
|
|
5483
|
-
toggleItalic: marksAllowed && !isInsideInlineCode && editor.can().toggleItalic(),
|
|
5484
|
-
toggleUnderline: marksAllowed && !isInsideInlineCode && editor.can().toggleUnderline(),
|
|
5485
|
-
toggleStrike: marksAllowed && !isInsideInlineCode && editor.can().toggleStrike(),
|
|
5486
|
-
toggleCode: marksAllowed && editor.can().toggleCode(),
|
|
5487
|
-
toggleHighlight: marksAllowed && !isInsideInlineCode && editor.can().toggleHighlight(),
|
|
5488
|
-
toggleLink: marksAllowed &&
|
|
5489
|
-
!isInsideInlineCode &&
|
|
5490
|
-
(editor.can().setLink({ href: "" }) || editor.can().unsetLink()),
|
|
5491
|
-
toggleSuperscript: marksAllowed && !isInsideInlineCode && editor.can().toggleSuperscript(),
|
|
5492
|
-
toggleSubscript: marksAllowed && !isInsideInlineCode && editor.can().toggleSubscript(),
|
|
5493
|
-
setColor: marksAllowed && !isInsideInlineCode && editor.can().setColor(""),
|
|
5494
|
-
setHighlight: marksAllowed && !isInsideInlineCode && editor.can().setHighlight(),
|
|
5495
|
-
undo: editor.can().undo(),
|
|
5496
|
-
redo: editor.can().redo(),
|
|
5497
|
-
},
|
|
5498
|
-
};
|
|
5499
|
-
};
|
|
5500
|
-
|
|
5501
|
-
const AteTableCalculator = editor => {
|
|
5502
|
-
const isTable = editor.isActive("table");
|
|
5503
|
-
if (!isTable) {
|
|
5578
|
+
// 6. Attach to DOM and ApplicationRef
|
|
5579
|
+
applicationRef.attachView(componentRef.hostView);
|
|
5580
|
+
const componentElement = componentRef.location.nativeElement;
|
|
5581
|
+
// Target specific element for content if selector provided
|
|
5582
|
+
if (editableContent && contentSelector) {
|
|
5583
|
+
contentDOM = componentElement.querySelector(contentSelector);
|
|
5584
|
+
}
|
|
5585
|
+
dom.appendChild(componentElement);
|
|
5586
|
+
// Initial detection to ensure the component is rendered
|
|
5587
|
+
componentRef.changeDetectorRef.detectChanges();
|
|
5588
|
+
// 7. Return the TipTap NodeView Interface
|
|
5504
5589
|
return {
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5590
|
+
dom,
|
|
5591
|
+
contentDOM,
|
|
5592
|
+
update: (updatedNode, updatedDecorations) => {
|
|
5593
|
+
if (updatedNode.type !== node.type) {
|
|
5594
|
+
return false;
|
|
5595
|
+
}
|
|
5596
|
+
// Update Aware component signals
|
|
5597
|
+
if (instance instanceof AteAngularNodeView) {
|
|
5598
|
+
instance._updateNodeView(updatedNode, updatedDecorations);
|
|
5599
|
+
}
|
|
5600
|
+
// Update inputs
|
|
5601
|
+
syncInputs(updatedNode);
|
|
5602
|
+
// Notify and Detect changes
|
|
5603
|
+
componentRef.changeDetectorRef.detectChanges();
|
|
5604
|
+
if (options.onUpdate) {
|
|
5605
|
+
options.onUpdate(updatedNode);
|
|
5606
|
+
}
|
|
5607
|
+
return true;
|
|
5608
|
+
},
|
|
5609
|
+
selectNode: () => {
|
|
5610
|
+
if (instance instanceof AteAngularNodeView) {
|
|
5611
|
+
instance._selectNodeView();
|
|
5612
|
+
}
|
|
5613
|
+
dom.classList.add("ProseMirror-selectednode");
|
|
5614
|
+
},
|
|
5615
|
+
deselectNode: () => {
|
|
5616
|
+
if (instance instanceof AteAngularNodeView) {
|
|
5617
|
+
instance._deselectNodeView();
|
|
5618
|
+
}
|
|
5619
|
+
dom.classList.remove("ProseMirror-selectednode");
|
|
5620
|
+
},
|
|
5621
|
+
destroy: () => {
|
|
5622
|
+
if (options.onDestroy) {
|
|
5623
|
+
options.onDestroy();
|
|
5624
|
+
}
|
|
5625
|
+
subscriptions.forEach(s => s.unsubscribe());
|
|
5626
|
+
applicationRef.detachView(componentRef.hostView);
|
|
5627
|
+
componentRef.destroy();
|
|
5628
|
+
},
|
|
5629
|
+
stopEvent: event => {
|
|
5630
|
+
const target = event.target;
|
|
5631
|
+
const isEditable = target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable;
|
|
5632
|
+
return isEditable;
|
|
5633
|
+
},
|
|
5634
|
+
ignoreMutation: mutation => {
|
|
5635
|
+
if (typeof ignoreMutation === "function") {
|
|
5636
|
+
return ignoreMutation(mutation);
|
|
5637
|
+
}
|
|
5638
|
+
return ignoreMutation;
|
|
5510
5639
|
},
|
|
5511
5640
|
};
|
|
5512
|
-
}
|
|
5513
|
-
const { selection } = editor.state;
|
|
5514
|
-
return {
|
|
5515
|
-
nodes: {
|
|
5516
|
-
isTable: true,
|
|
5517
|
-
isTableNodeSelected: selection instanceof NodeSelection && selection.node.type.name === "table",
|
|
5518
|
-
isTableCell: editor.isActive("tableCell") || editor.isActive("tableHeader"),
|
|
5519
|
-
isTableHeaderRow: editor.isActive("tableHeader", { row: true }),
|
|
5520
|
-
isTableHeaderColumn: editor.isActive("tableHeader", { column: true }),
|
|
5521
|
-
},
|
|
5522
|
-
can: {
|
|
5523
|
-
addRowBefore: editor.can().addRowBefore(),
|
|
5524
|
-
addRowAfter: editor.can().addRowAfter(),
|
|
5525
|
-
deleteRow: editor.can().deleteRow(),
|
|
5526
|
-
addColumnBefore: editor.can().addColumnBefore(),
|
|
5527
|
-
addColumnAfter: editor.can().addColumnAfter(),
|
|
5528
|
-
deleteColumn: editor.can().deleteColumn(),
|
|
5529
|
-
deleteTable: editor.can().deleteTable(),
|
|
5530
|
-
mergeCells: editor.can().mergeCells(),
|
|
5531
|
-
splitCell: editor.can().splitCell(),
|
|
5532
|
-
toggleHeaderRow: editor.can().toggleHeaderRow(),
|
|
5533
|
-
toggleHeaderColumn: editor.can().toggleHeaderColumn(),
|
|
5534
|
-
},
|
|
5535
5641
|
};
|
|
5536
|
-
}
|
|
5642
|
+
}
|
|
5537
5643
|
|
|
5538
|
-
const
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5644
|
+
const RESERVED_NAMES = [
|
|
5645
|
+
"doc",
|
|
5646
|
+
"paragraph",
|
|
5647
|
+
"text",
|
|
5648
|
+
"hardBreak",
|
|
5649
|
+
"bulletList",
|
|
5650
|
+
"orderedList",
|
|
5651
|
+
"listItem",
|
|
5652
|
+
"blockquote",
|
|
5653
|
+
"codeBlock",
|
|
5654
|
+
"heading",
|
|
5655
|
+
"horizontalRule",
|
|
5656
|
+
];
|
|
5657
|
+
/**
|
|
5658
|
+
* Derives the TipTap node name and HTML tag from a component class.
|
|
5659
|
+
*/
|
|
5660
|
+
function deriveMetadata(component, customName) {
|
|
5661
|
+
let nodeName = customName;
|
|
5662
|
+
if (!nodeName) {
|
|
5663
|
+
nodeName = component.name
|
|
5664
|
+
.replace(/Component$/, "")
|
|
5665
|
+
.replace(/Node$/, "")
|
|
5666
|
+
.replace(/^([A-Z])/, m => m.toLowerCase());
|
|
5667
|
+
console.warn(`[ATE] Auto-deriving node name '${nodeName}' for component ${component.name}. ` +
|
|
5668
|
+
`Provide an explicit 'name' in options to avoid potential naming collisions.`);
|
|
5669
|
+
}
|
|
5670
|
+
if (RESERVED_NAMES.includes(nodeName)) {
|
|
5671
|
+
throw new Error(`[ATE] The name '${nodeName}' is a reserved TipTap node name. ` +
|
|
5672
|
+
`Please provide a unique name for your custom component.`);
|
|
5673
|
+
}
|
|
5674
|
+
const tag = nodeName.toLowerCase().replace(/([A-Z])/g, "-$1");
|
|
5675
|
+
return { nodeName, tag };
|
|
5676
|
+
}
|
|
5677
|
+
/**
|
|
5678
|
+
* Creates TipTap attributes from component inputs (Standard Mode).
|
|
5679
|
+
*/
|
|
5680
|
+
function createStandardAttributes(defaultInputs = {}) {
|
|
5681
|
+
const tiptapAttributes = {};
|
|
5682
|
+
Object.entries(defaultInputs).forEach(([key, defaultValue]) => {
|
|
5683
|
+
tiptapAttributes[key] = {
|
|
5684
|
+
default: defaultValue,
|
|
5685
|
+
parseHTML: (element) => {
|
|
5686
|
+
const attr = element.getAttribute(`data-${key}`);
|
|
5687
|
+
if (attr === null) {
|
|
5688
|
+
return defaultValue;
|
|
5689
|
+
}
|
|
5690
|
+
try {
|
|
5691
|
+
return JSON.parse(attr);
|
|
5692
|
+
}
|
|
5693
|
+
catch {
|
|
5694
|
+
return attr;
|
|
5695
|
+
}
|
|
5696
|
+
},
|
|
5697
|
+
renderHTML: (attrs) => {
|
|
5698
|
+
const value = attrs[key];
|
|
5699
|
+
if (value === undefined || value === null) {
|
|
5700
|
+
return {};
|
|
5701
|
+
}
|
|
5702
|
+
const serialized = typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
5703
|
+
return { [`data-${key}`]: serialized };
|
|
5704
|
+
},
|
|
5705
|
+
};
|
|
5706
|
+
});
|
|
5707
|
+
return tiptapAttributes;
|
|
5708
|
+
}
|
|
5709
|
+
/**
|
|
5710
|
+
* Factory to transform an Angular component into a TipTap Node extension.
|
|
5711
|
+
* Supports both "TipTap-Aware" and "Standard" modes.
|
|
5712
|
+
*
|
|
5713
|
+
* @internal
|
|
5714
|
+
*/
|
|
5715
|
+
function createAngularComponentExtension(injector, options) {
|
|
5716
|
+
const { component, name: customName, attributes, defaultInputs, contentSelector, contentMode = "block", editableContent = false, group = "block", draggable = true, ignoreMutation = true, onOutput, HTMLAttributes = {}, parseHTML: customParseHTML, renderHTML: customRenderHTML, } = options;
|
|
5717
|
+
const { nodeName, tag } = deriveMetadata(component, customName);
|
|
5718
|
+
const isTipTapAware = Object.prototype.isPrototypeOf.call(AteAngularNodeView, component);
|
|
5719
|
+
const atom = !editableContent;
|
|
5720
|
+
// 1. Prepare Attributes
|
|
5721
|
+
const tiptapAttributes = isTipTapAware
|
|
5722
|
+
? attributes || {}
|
|
5723
|
+
: createStandardAttributes(defaultInputs);
|
|
5724
|
+
// 2. Create Node Extension
|
|
5725
|
+
return Node$1.create({
|
|
5726
|
+
name: nodeName,
|
|
5727
|
+
group,
|
|
5728
|
+
inline: group === "inline",
|
|
5729
|
+
atom,
|
|
5730
|
+
draggable,
|
|
5731
|
+
content: editableContent ? (contentMode === "inline" ? "inline*" : "block*") : undefined,
|
|
5732
|
+
addAttributes() {
|
|
5733
|
+
return tiptapAttributes;
|
|
5542
5734
|
},
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
nodes: {
|
|
5549
|
-
isBlockquote: editor.isActive("blockquote"),
|
|
5550
|
-
isCodeBlock: editor.isActive("codeBlock"),
|
|
5551
|
-
isBulletList: editor.isActive("bulletList"),
|
|
5552
|
-
isOrderedList: editor.isActive("orderedList"),
|
|
5553
|
-
h1: editor.isActive("heading", { level: 1 }),
|
|
5554
|
-
h2: editor.isActive("heading", { level: 2 }),
|
|
5555
|
-
h3: editor.isActive("heading", { level: 3 }),
|
|
5556
|
-
alignLeft: editor.isActive({ textAlign: "left" }),
|
|
5557
|
-
alignCenter: editor.isActive({ textAlign: "center" }),
|
|
5558
|
-
alignRight: editor.isActive({ textAlign: "right" }),
|
|
5559
|
-
alignJustify: editor.isActive({ textAlign: "justify" }),
|
|
5735
|
+
parseHTML() {
|
|
5736
|
+
if (customParseHTML) {
|
|
5737
|
+
return customParseHTML.call(this);
|
|
5738
|
+
}
|
|
5739
|
+
return [{ tag }, { tag: `div[data-component="${nodeName}"]` }];
|
|
5560
5740
|
},
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
toggleOrderedList: editor.can().toggleOrderedList(),
|
|
5567
|
-
toggleBlockquote: editor.can().toggleBlockquote(),
|
|
5568
|
-
toggleCodeBlock: editor.can().toggleCodeBlock(),
|
|
5569
|
-
setTextAlignLeft: editor.can().setTextAlign("left"),
|
|
5570
|
-
setTextAlignCenter: editor.can().setTextAlign("center"),
|
|
5571
|
-
setTextAlignRight: editor.can().setTextAlign("right"),
|
|
5572
|
-
setTextAlignJustify: editor.can().setTextAlign("justify"),
|
|
5573
|
-
insertHorizontalRule: editor.can().setHorizontalRule(),
|
|
5574
|
-
insertTable: editor.can().insertTable(),
|
|
5575
|
-
insertImage: editor.can().setResizableImage({ src: "" }),
|
|
5741
|
+
renderHTML({ node, HTMLAttributes: attrs }) {
|
|
5742
|
+
if (customRenderHTML) {
|
|
5743
|
+
return customRenderHTML.call(this, { node, HTMLAttributes: attrs });
|
|
5744
|
+
}
|
|
5745
|
+
return [tag, mergeAttributes(HTMLAttributes, attrs, { "data-component": nodeName })];
|
|
5576
5746
|
},
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5747
|
+
addNodeView() {
|
|
5748
|
+
return AteNodeViewRenderer(component, {
|
|
5749
|
+
injector,
|
|
5750
|
+
inputs: defaultInputs,
|
|
5751
|
+
wrapperTag: group === "inline" ? "span" : "div",
|
|
5752
|
+
wrapperClass: isTipTapAware
|
|
5753
|
+
? `ate-node-${nodeName}`
|
|
5754
|
+
: `embedded-component embedded-${nodeName}`,
|
|
5755
|
+
atom,
|
|
5756
|
+
editableContent,
|
|
5757
|
+
contentSelector,
|
|
5758
|
+
contentMode,
|
|
5759
|
+
onOutput,
|
|
5760
|
+
ignoreMutation,
|
|
5761
|
+
});
|
|
5762
|
+
},
|
|
5763
|
+
});
|
|
5764
|
+
}
|
|
5765
|
+
|
|
5766
|
+
/**
|
|
5767
|
+
* Internal registry to store the root injector for the editor.
|
|
5768
|
+
* This allows registerAngularComponent to work without explicitly passing the injector.
|
|
5769
|
+
* @internal
|
|
5770
|
+
*/
|
|
5771
|
+
let globalInjector = null;
|
|
5772
|
+
function setGlobalInjector(injector) {
|
|
5773
|
+
globalInjector = injector;
|
|
5774
|
+
}
|
|
5775
|
+
function getGlobalInjector() {
|
|
5776
|
+
if (!globalInjector) {
|
|
5777
|
+
throw new Error("[ATE] Global Injector not found. Make sure to call provideAteEditor() in your app.config.ts or main.ts.");
|
|
5778
|
+
}
|
|
5779
|
+
return globalInjector;
|
|
5780
|
+
}
|
|
5781
|
+
|
|
5782
|
+
/**
|
|
5783
|
+
* Registers ANY Angular component as a TipTap node extension.
|
|
5784
|
+
*
|
|
5785
|
+
* This function is the single public entry point for adding custom components.
|
|
5786
|
+
* It supports two distinct modes:
|
|
5787
|
+
*
|
|
5788
|
+
* 1. **TipTap-Aware Mode** (the component extends `AteAngularNodeView`):
|
|
5789
|
+
* Grants direct access to the TipTap API (`editor`, `node`, `updateAttributes`, etc.) within the component.
|
|
5790
|
+
*
|
|
5791
|
+
* 2. **Standard Mode** (library or legacy components):
|
|
5792
|
+
* Allows using any existing Angular component. Its `@Input()` properties are automatically
|
|
5793
|
+
* synchronized with TipTap node attributes.
|
|
5794
|
+
*
|
|
5795
|
+
* @param injectorOrOptions - The Angular Injector OR the configuration options
|
|
5796
|
+
* @param maybeOptions - Configuration options (if the first param was an injector)
|
|
5797
|
+
* @returns A TipTap Node extension ready to be used
|
|
5798
|
+
*
|
|
5799
|
+
* @example
|
|
5800
|
+
* ```typescript
|
|
5801
|
+
* // Modern way (requires provideAteEditor())
|
|
5802
|
+
* registerAngularComponent({
|
|
5803
|
+
* component: MyComponent,
|
|
5804
|
+
* name: 'myComponent'
|
|
5805
|
+
* });
|
|
5806
|
+
*
|
|
5807
|
+
* // Classic way
|
|
5808
|
+
* registerAngularComponent(injector, {
|
|
5809
|
+
* component: MyComponent
|
|
5810
|
+
* });
|
|
5811
|
+
* ```
|
|
5812
|
+
*/
|
|
5813
|
+
function registerAngularComponent(injectorOrOptions, maybeOptions) {
|
|
5814
|
+
let injector;
|
|
5815
|
+
let options;
|
|
5816
|
+
// Duck-typing check: Injectors have a 'get' method
|
|
5817
|
+
const isInjector = (obj) => obj !== null && typeof obj === "object" && typeof obj.get === "function";
|
|
5818
|
+
if (isInjector(injectorOrOptions)) {
|
|
5819
|
+
injector = injectorOrOptions;
|
|
5820
|
+
options = maybeOptions;
|
|
5821
|
+
}
|
|
5822
|
+
else {
|
|
5823
|
+
injector = getGlobalInjector();
|
|
5824
|
+
options = injectorOrOptions;
|
|
5825
|
+
}
|
|
5826
|
+
return createAngularComponentExtension(injector, options);
|
|
5827
|
+
}
|
|
5828
|
+
|
|
5829
|
+
/**
|
|
5830
|
+
* Injection Token for global editor configuration.
|
|
5831
|
+
*/
|
|
5832
|
+
const ATE_GLOBAL_CONFIG = new InjectionToken("ATE_GLOBAL_CONFIG");
|
|
5833
|
+
|
|
5834
|
+
const AteSelectionCalculator = editor => {
|
|
5835
|
+
const { selection } = editor.state;
|
|
5836
|
+
const { from, to } = selection;
|
|
5837
|
+
let selectionType = "none";
|
|
5838
|
+
if (selection instanceof TextSelection) {
|
|
5839
|
+
selectionType = "text";
|
|
5840
|
+
}
|
|
5841
|
+
else if (selection instanceof NodeSelection) {
|
|
5842
|
+
selectionType = "node";
|
|
5843
|
+
}
|
|
5844
|
+
else if (selection instanceof CellSelection) {
|
|
5845
|
+
selectionType = "cell";
|
|
5846
|
+
}
|
|
5847
|
+
let isSingleCell = false;
|
|
5848
|
+
if (selection instanceof CellSelection) {
|
|
5849
|
+
isSingleCell = selection.$anchorCell.pos === selection.$headCell.pos;
|
|
5850
|
+
}
|
|
5851
|
+
return {
|
|
5852
|
+
isFocused: editor.view.hasFocus(),
|
|
5853
|
+
isEditable: editor.isEditable,
|
|
5854
|
+
selection: {
|
|
5855
|
+
type: selectionType,
|
|
5856
|
+
from,
|
|
5857
|
+
to,
|
|
5858
|
+
empty: selection.empty || (selectionType === "text" && from === to),
|
|
5859
|
+
isSingleCell: isSingleCell,
|
|
5860
|
+
},
|
|
5861
|
+
nodes: {
|
|
5862
|
+
activeNodeName: editor.state.doc.nodeAt(editor.state.selection.$head.pos)?.type.name || null,
|
|
5863
|
+
},
|
|
5864
|
+
};
|
|
5865
|
+
};
|
|
5866
|
+
|
|
5867
|
+
const AteMarksCalculator = editor => {
|
|
5868
|
+
const isCodeBlock = editor.isActive("codeBlock");
|
|
5869
|
+
const isCode = editor.isActive("code"); // Inline code mark
|
|
5870
|
+
const isImage = editor.isActive("image") || editor.isActive("resizableImage");
|
|
5871
|
+
// Check if marks are generally allowed based on context
|
|
5872
|
+
const marksAllowed = !isCodeBlock && !isImage;
|
|
5873
|
+
// For inline code specifically, we don't allow nesting OTHER marks inside it,
|
|
5874
|
+
// but the code mark ITSELF must be togglable to be removed.
|
|
5875
|
+
const isInsideInlineCode = isCode;
|
|
5876
|
+
// 1. Resolve target element once for this calculation
|
|
5877
|
+
const getTargetElement = () => {
|
|
5878
|
+
if (typeof window === "undefined" || !editor.view?.dom) {
|
|
5879
|
+
return null;
|
|
5880
|
+
}
|
|
5881
|
+
try {
|
|
5882
|
+
const { from } = editor.state.selection;
|
|
5883
|
+
const { node } = editor.view.domAtPos(from);
|
|
5884
|
+
return node.nodeType === Node.TEXT_NODE ? node.parentElement : node;
|
|
5885
|
+
}
|
|
5886
|
+
catch (_e) {
|
|
5887
|
+
return null;
|
|
5888
|
+
}
|
|
5889
|
+
};
|
|
5890
|
+
const targetEl = getTargetElement() || (typeof window !== "undefined" ? editor.view?.dom : null);
|
|
5891
|
+
const computedStyle = targetEl && typeof window !== "undefined" ? window.getComputedStyle(targetEl) : null;
|
|
5892
|
+
// 2. Lightweight helper to extract properties from the pre-calculated style object
|
|
5893
|
+
const getStyle = (prop) => {
|
|
5894
|
+
if (!computedStyle) {
|
|
5895
|
+
return null;
|
|
5896
|
+
}
|
|
5897
|
+
const val = computedStyle.getPropertyValue(prop);
|
|
5898
|
+
return normalizeColor(val);
|
|
5899
|
+
};
|
|
5900
|
+
const colorMark = editor.getAttributes("textStyle")["color"] || null;
|
|
5901
|
+
const backgroundMark = editor.getAttributes("highlight")["color"] || null;
|
|
5902
|
+
return {
|
|
5903
|
+
marks: {
|
|
5904
|
+
bold: editor.isActive("bold"),
|
|
5905
|
+
italic: editor.isActive("italic"),
|
|
5906
|
+
underline: editor.isActive("underline"),
|
|
5907
|
+
strike: editor.isActive("strike"),
|
|
5908
|
+
code: isCode,
|
|
5909
|
+
superscript: editor.isActive("superscript"),
|
|
5910
|
+
subscript: editor.isActive("subscript"),
|
|
5911
|
+
highlight: editor.isActive("highlight"),
|
|
5912
|
+
link: editor.isActive("link"),
|
|
5913
|
+
linkHref: editor.getAttributes("link")["href"] || null,
|
|
5914
|
+
color: colorMark,
|
|
5915
|
+
computedColor: colorMark || getStyle("color"),
|
|
5916
|
+
background: backgroundMark,
|
|
5917
|
+
computedBackground: backgroundMark || getStyle("background-color"),
|
|
5918
|
+
linkOpenOnClick: editor.extensionManager.extensions.find(ext => ext.name === "link")?.options?.openOnClick ??
|
|
5919
|
+
false,
|
|
5920
|
+
},
|
|
5921
|
+
can: {
|
|
5922
|
+
toggleBold: marksAllowed && !isInsideInlineCode && editor.can().toggleBold(),
|
|
5923
|
+
toggleItalic: marksAllowed && !isInsideInlineCode && editor.can().toggleItalic(),
|
|
5924
|
+
toggleUnderline: marksAllowed && !isInsideInlineCode && editor.can().toggleUnderline(),
|
|
5925
|
+
toggleStrike: marksAllowed && !isInsideInlineCode && editor.can().toggleStrike(),
|
|
5926
|
+
toggleCode: marksAllowed && editor.can().toggleCode(),
|
|
5927
|
+
toggleHighlight: marksAllowed && !isInsideInlineCode && editor.can().toggleHighlight(),
|
|
5928
|
+
toggleLink: marksAllowed &&
|
|
5929
|
+
!isInsideInlineCode &&
|
|
5930
|
+
(editor.can().setLink({ href: "" }) || editor.can().unsetLink()),
|
|
5931
|
+
toggleSuperscript: marksAllowed && !isInsideInlineCode && editor.can().toggleSuperscript(),
|
|
5932
|
+
toggleSubscript: marksAllowed && !isInsideInlineCode && editor.can().toggleSubscript(),
|
|
5933
|
+
setColor: marksAllowed && !isInsideInlineCode && editor.can().setColor(""),
|
|
5934
|
+
setHighlight: marksAllowed && !isInsideInlineCode && editor.can().setHighlight(),
|
|
5935
|
+
undo: editor.can().undo(),
|
|
5936
|
+
redo: editor.can().redo(),
|
|
5937
|
+
},
|
|
5938
|
+
};
|
|
5939
|
+
};
|
|
5940
|
+
|
|
5941
|
+
const AteTableCalculator = editor => {
|
|
5942
|
+
const isTable = editor.isActive("table");
|
|
5943
|
+
if (!isTable) {
|
|
5944
|
+
return {
|
|
5945
|
+
nodes: {
|
|
5946
|
+
isTable: false,
|
|
5947
|
+
isTableCell: false,
|
|
5948
|
+
isTableHeaderRow: false,
|
|
5949
|
+
isTableHeaderColumn: false,
|
|
5950
|
+
},
|
|
5951
|
+
};
|
|
5952
|
+
}
|
|
5953
|
+
const { selection } = editor.state;
|
|
5954
|
+
return {
|
|
5955
|
+
nodes: {
|
|
5956
|
+
isTable: true,
|
|
5957
|
+
isTableNodeSelected: selection instanceof NodeSelection && selection.node.type.name === "table",
|
|
5958
|
+
isTableCell: editor.isActive("tableCell") || editor.isActive("tableHeader"),
|
|
5959
|
+
isTableHeaderRow: editor.isActive("tableHeader", { row: true }),
|
|
5960
|
+
isTableHeaderColumn: editor.isActive("tableHeader", { column: true }),
|
|
5961
|
+
},
|
|
5962
|
+
can: {
|
|
5963
|
+
addRowBefore: editor.can().addRowBefore(),
|
|
5964
|
+
addRowAfter: editor.can().addRowAfter(),
|
|
5965
|
+
deleteRow: editor.can().deleteRow(),
|
|
5966
|
+
addColumnBefore: editor.can().addColumnBefore(),
|
|
5967
|
+
addColumnAfter: editor.can().addColumnAfter(),
|
|
5968
|
+
deleteColumn: editor.can().deleteColumn(),
|
|
5969
|
+
deleteTable: editor.can().deleteTable(),
|
|
5970
|
+
mergeCells: editor.can().mergeCells(),
|
|
5971
|
+
splitCell: editor.can().splitCell(),
|
|
5972
|
+
toggleHeaderRow: editor.can().toggleHeaderRow(),
|
|
5973
|
+
toggleHeaderColumn: editor.can().toggleHeaderColumn(),
|
|
5974
|
+
},
|
|
5975
|
+
};
|
|
5976
|
+
};
|
|
5977
|
+
|
|
5978
|
+
const AteImageCalculator = editor => {
|
|
5979
|
+
return {
|
|
5980
|
+
nodes: {
|
|
5981
|
+
isImage: editor.isActive("image") || editor.isActive("resizableImage"),
|
|
5982
|
+
},
|
|
5983
|
+
};
|
|
5984
|
+
};
|
|
5985
|
+
|
|
5986
|
+
const AteStructureCalculator = editor => {
|
|
5987
|
+
return {
|
|
5988
|
+
nodes: {
|
|
5989
|
+
isBlockquote: editor.isActive("blockquote"),
|
|
5990
|
+
isCodeBlock: editor.isActive("codeBlock"),
|
|
5991
|
+
isBulletList: editor.isActive("bulletList"),
|
|
5992
|
+
isOrderedList: editor.isActive("orderedList"),
|
|
5993
|
+
h1: editor.isActive("heading", { level: 1 }),
|
|
5994
|
+
h2: editor.isActive("heading", { level: 2 }),
|
|
5995
|
+
h3: editor.isActive("heading", { level: 3 }),
|
|
5996
|
+
alignLeft: editor.isActive({ textAlign: "left" }),
|
|
5997
|
+
alignCenter: editor.isActive({ textAlign: "center" }),
|
|
5998
|
+
alignRight: editor.isActive({ textAlign: "right" }),
|
|
5999
|
+
alignJustify: editor.isActive({ textAlign: "justify" }),
|
|
6000
|
+
},
|
|
6001
|
+
can: {
|
|
6002
|
+
toggleHeading1: editor.can().toggleHeading({ level: 1 }),
|
|
6003
|
+
toggleHeading2: editor.can().toggleHeading({ level: 2 }),
|
|
6004
|
+
toggleHeading3: editor.can().toggleHeading({ level: 3 }),
|
|
6005
|
+
toggleBulletList: editor.can().toggleBulletList(),
|
|
6006
|
+
toggleOrderedList: editor.can().toggleOrderedList(),
|
|
6007
|
+
toggleBlockquote: editor.can().toggleBlockquote(),
|
|
6008
|
+
toggleCodeBlock: editor.can().toggleCodeBlock(),
|
|
6009
|
+
setTextAlignLeft: editor.can().setTextAlign("left"),
|
|
6010
|
+
setTextAlignCenter: editor.can().setTextAlign("center"),
|
|
6011
|
+
setTextAlignRight: editor.can().setTextAlign("right"),
|
|
6012
|
+
setTextAlignJustify: editor.can().setTextAlign("justify"),
|
|
6013
|
+
insertHorizontalRule: editor.can().setHorizontalRule(),
|
|
6014
|
+
insertTable: editor.can().insertTable(),
|
|
6015
|
+
insertImage: editor.can().setResizableImage({ src: "" }),
|
|
6016
|
+
},
|
|
6017
|
+
};
|
|
6018
|
+
};
|
|
6019
|
+
|
|
6020
|
+
/**
|
|
6021
|
+
* DiscoveryCalculator automatically detects and tracks the state of any TipTap extension.
|
|
6022
|
+
* It provides a "fallback" reactive state for any mark or node not explicitly handled
|
|
6023
|
+
* by specialized calculators.
|
|
6024
|
+
*/
|
|
6025
|
+
const AteDiscoveryCalculator = (editor) => {
|
|
6026
|
+
const state = {
|
|
6027
|
+
marks: {},
|
|
6028
|
+
nodes: {},
|
|
6029
|
+
};
|
|
5590
6030
|
// We skip core extensions that are already handled by specialized calculators
|
|
5591
6031
|
// to avoid redundant calculations and maintain precise attribute tracking.
|
|
5592
6032
|
const handled = [
|
|
@@ -5744,8 +6184,58 @@ const ATE_DEFAULT_CELL_MENU_CONFIG = {
|
|
|
5744
6184
|
mergeCells: true,
|
|
5745
6185
|
splitCell: true,
|
|
5746
6186
|
};
|
|
6187
|
+
/**
|
|
6188
|
+
* Ultimate default configuration for the Angular Tiptap Editor.
|
|
6189
|
+
* This serves as the 'Level 4' fallback in the configuration hierarchy:
|
|
6190
|
+
* 1. Component Inputs (Le Roi)
|
|
6191
|
+
* 2. Component [config] (Le Prince)
|
|
6192
|
+
* 3. Global provideAteEditor() (Le Duc)
|
|
6193
|
+
* 4. ATE_DEFAULT_CONFIG (Le Peuple)
|
|
6194
|
+
*/
|
|
6195
|
+
const ATE_DEFAULT_CONFIG = {
|
|
6196
|
+
theme: "auto",
|
|
6197
|
+
mode: "classic",
|
|
6198
|
+
height: "auto",
|
|
6199
|
+
minHeight: "200px",
|
|
6200
|
+
maxHeight: "none",
|
|
6201
|
+
fillContainer: false,
|
|
6202
|
+
autofocus: false,
|
|
6203
|
+
editable: true,
|
|
6204
|
+
disabled: false,
|
|
6205
|
+
spellcheck: true,
|
|
6206
|
+
enableOfficePaste: true,
|
|
6207
|
+
showToolbar: true,
|
|
6208
|
+
showFooter: true,
|
|
6209
|
+
showCharacterCount: true,
|
|
6210
|
+
showWordCount: true,
|
|
6211
|
+
showEditToggle: false,
|
|
6212
|
+
showBubbleMenu: true,
|
|
6213
|
+
showImageBubbleMenu: true,
|
|
6214
|
+
showTableMenu: true,
|
|
6215
|
+
showCellMenu: true,
|
|
6216
|
+
enableSlashCommands: true,
|
|
6217
|
+
floatingToolbar: false,
|
|
6218
|
+
toolbar: ATE_DEFAULT_TOOLBAR_CONFIG,
|
|
6219
|
+
bubbleMenu: ATE_DEFAULT_BUBBLE_MENU_CONFIG,
|
|
6220
|
+
imageBubbleMenu: ATE_DEFAULT_IMAGE_BUBBLE_MENU_CONFIG,
|
|
6221
|
+
tableBubbleMenu: ATE_DEFAULT_TABLE_MENU_CONFIG,
|
|
6222
|
+
cellBubbleMenu: ATE_DEFAULT_CELL_MENU_CONFIG,
|
|
6223
|
+
slashCommands: {},
|
|
6224
|
+
tiptapExtensions: [],
|
|
6225
|
+
tiptapOptions: {},
|
|
6226
|
+
stateCalculators: [],
|
|
6227
|
+
angularNodes: [],
|
|
6228
|
+
};
|
|
5747
6229
|
|
|
5748
6230
|
// Slash commands configuration is handled dynamically via slashCommandsConfigComputed
|
|
6231
|
+
/**
|
|
6232
|
+
* The main rich-text editor component for Angular.
|
|
6233
|
+
*
|
|
6234
|
+
* Powered by Tiptap and built with a native Signal-based architecture, it provides
|
|
6235
|
+
* a seamless, high-performance editing experience. Supports automatic registration
|
|
6236
|
+
* of Angular components as interactive nodes ('Angular Nodes'), full Reactive Forms
|
|
6237
|
+
* integration, and extensive customization via the AteEditorConfig.
|
|
6238
|
+
*/
|
|
5749
6239
|
class AngularTiptapEditorComponent {
|
|
5750
6240
|
// ============================================
|
|
5751
6241
|
// Toolbar / Bubble Menu Coordination
|
|
@@ -5760,48 +6250,48 @@ class AngularTiptapEditorComponent {
|
|
|
5760
6250
|
/** Configuration globale de l'éditeur */
|
|
5761
6251
|
this.config = input({}, ...(ngDevMode ? [{ debugName: "config" }] : []));
|
|
5762
6252
|
this.content = input("", ...(ngDevMode ? [{ debugName: "content" }] : []));
|
|
5763
|
-
this.placeholder = input(
|
|
5764
|
-
this.editable = input(
|
|
5765
|
-
this.disabled = input(
|
|
5766
|
-
this.minHeight = input(
|
|
6253
|
+
this.placeholder = input(undefined, ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
|
|
6254
|
+
this.editable = input(undefined, ...(ngDevMode ? [{ debugName: "editable" }] : []));
|
|
6255
|
+
this.disabled = input(undefined, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
6256
|
+
this.minHeight = input(undefined, ...(ngDevMode ? [{ debugName: "minHeight" }] : []));
|
|
5767
6257
|
this.height = input(undefined, ...(ngDevMode ? [{ debugName: "height" }] : []));
|
|
5768
6258
|
this.maxHeight = input(undefined, ...(ngDevMode ? [{ debugName: "maxHeight" }] : []));
|
|
5769
|
-
this.fillContainer = input(
|
|
5770
|
-
this.showToolbar = input(
|
|
5771
|
-
this.showFooter = input(
|
|
5772
|
-
this.showCharacterCount = input(
|
|
5773
|
-
this.showWordCount = input(
|
|
6259
|
+
this.fillContainer = input(undefined, ...(ngDevMode ? [{ debugName: "fillContainer" }] : []));
|
|
6260
|
+
this.showToolbar = input(undefined, ...(ngDevMode ? [{ debugName: "showToolbar" }] : []));
|
|
6261
|
+
this.showFooter = input(undefined, ...(ngDevMode ? [{ debugName: "showFooter" }] : []));
|
|
6262
|
+
this.showCharacterCount = input(undefined, ...(ngDevMode ? [{ debugName: "showCharacterCount" }] : []));
|
|
6263
|
+
this.showWordCount = input(undefined, ...(ngDevMode ? [{ debugName: "showWordCount" }] : []));
|
|
5774
6264
|
this.maxCharacters = input(undefined, ...(ngDevMode ? [{ debugName: "maxCharacters" }] : []));
|
|
5775
|
-
this.enableOfficePaste = input(
|
|
5776
|
-
this.enableSlashCommands = input(
|
|
5777
|
-
this.slashCommands = input(
|
|
6265
|
+
this.enableOfficePaste = input(undefined, ...(ngDevMode ? [{ debugName: "enableOfficePaste" }] : []));
|
|
6266
|
+
this.enableSlashCommands = input(undefined, ...(ngDevMode ? [{ debugName: "enableSlashCommands" }] : []));
|
|
6267
|
+
this.slashCommands = input(undefined, ...(ngDevMode ? [{ debugName: "slashCommands" }] : []));
|
|
5778
6268
|
this.customSlashCommands = input(undefined, ...(ngDevMode ? [{ debugName: "customSlashCommands" }] : []));
|
|
5779
6269
|
this.locale = input(undefined, ...(ngDevMode ? [{ debugName: "locale" }] : []));
|
|
5780
|
-
this.autofocus = input(
|
|
5781
|
-
this.seamless = input(
|
|
5782
|
-
this.floatingToolbar = input(
|
|
5783
|
-
this.showEditToggle = input(
|
|
5784
|
-
this.spellcheck = input(
|
|
5785
|
-
this.tiptapExtensions = input(
|
|
5786
|
-
this.tiptapOptions = input(
|
|
6270
|
+
this.autofocus = input(undefined, ...(ngDevMode ? [{ debugName: "autofocus" }] : []));
|
|
6271
|
+
this.seamless = input(undefined, ...(ngDevMode ? [{ debugName: "seamless" }] : []));
|
|
6272
|
+
this.floatingToolbar = input(undefined, ...(ngDevMode ? [{ debugName: "floatingToolbar" }] : []));
|
|
6273
|
+
this.showEditToggle = input(undefined, ...(ngDevMode ? [{ debugName: "showEditToggle" }] : []));
|
|
6274
|
+
this.spellcheck = input(undefined, ...(ngDevMode ? [{ debugName: "spellcheck" }] : []));
|
|
6275
|
+
this.tiptapExtensions = input(undefined, ...(ngDevMode ? [{ debugName: "tiptapExtensions" }] : []));
|
|
6276
|
+
this.tiptapOptions = input(undefined, ...(ngDevMode ? [{ debugName: "tiptapOptions" }] : []));
|
|
5787
6277
|
// Nouveaux inputs pour les bubble menus
|
|
5788
|
-
this.showBubbleMenu = input(
|
|
5789
|
-
this.bubbleMenu = input(
|
|
5790
|
-
this.showImageBubbleMenu = input(
|
|
5791
|
-
this.imageBubbleMenu = input(
|
|
6278
|
+
this.showBubbleMenu = input(undefined, ...(ngDevMode ? [{ debugName: "showBubbleMenu" }] : []));
|
|
6279
|
+
this.bubbleMenu = input(undefined, ...(ngDevMode ? [{ debugName: "bubbleMenu" }] : []));
|
|
6280
|
+
this.showImageBubbleMenu = input(undefined, ...(ngDevMode ? [{ debugName: "showImageBubbleMenu" }] : []));
|
|
6281
|
+
this.imageBubbleMenu = input(undefined, ...(ngDevMode ? [{ debugName: "imageBubbleMenu" }] : []));
|
|
5792
6282
|
// Configuration de la toolbar
|
|
5793
|
-
this.toolbar = input(
|
|
6283
|
+
this.toolbar = input(undefined, ...(ngDevMode ? [{ debugName: "toolbar" }] : []));
|
|
5794
6284
|
// Configuration des menus de table
|
|
5795
|
-
this.showTableBubbleMenu = input(
|
|
5796
|
-
this.tableBubbleMenu = input(
|
|
5797
|
-
this.showCellBubbleMenu = input(
|
|
5798
|
-
this.cellBubbleMenu = input(
|
|
6285
|
+
this.showTableBubbleMenu = input(undefined, ...(ngDevMode ? [{ debugName: "showTableBubbleMenu" }] : []));
|
|
6286
|
+
this.tableBubbleMenu = input(undefined, ...(ngDevMode ? [{ debugName: "tableBubbleMenu" }] : []));
|
|
6287
|
+
this.showCellBubbleMenu = input(undefined, ...(ngDevMode ? [{ debugName: "showCellBubbleMenu" }] : []));
|
|
6288
|
+
this.cellBubbleMenu = input(undefined, ...(ngDevMode ? [{ debugName: "cellBubbleMenu" }] : []));
|
|
5799
6289
|
/**
|
|
5800
6290
|
* Additionnal state calculators to extend the reactive editor state.
|
|
5801
6291
|
*/
|
|
5802
|
-
this.stateCalculators = input(
|
|
6292
|
+
this.stateCalculators = input(undefined, ...(ngDevMode ? [{ debugName: "stateCalculators" }] : []));
|
|
5803
6293
|
// Nouveau input pour la configuration de l'upload d'images
|
|
5804
|
-
this.imageUpload = input(
|
|
6294
|
+
this.imageUpload = input(undefined, ...(ngDevMode ? [{ debugName: "imageUpload" }] : []));
|
|
5805
6295
|
/**
|
|
5806
6296
|
* Custom handler for image uploads.
|
|
5807
6297
|
* When provided, images will be processed through this handler instead of being converted to base64.
|
|
@@ -5848,7 +6338,7 @@ class AngularTiptapEditorComponent {
|
|
|
5848
6338
|
this._isFormControlDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_isFormControlDisabled" }] : []));
|
|
5849
6339
|
this.isFormControlDisabled = this._isFormControlDisabled.asReadonly();
|
|
5850
6340
|
// Combined disabled state (Input + FormControl)
|
|
5851
|
-
this.mergedDisabled = computed(() => (this.
|
|
6341
|
+
this.mergedDisabled = computed(() => (this.disabled() ?? this.effectiveConfig().disabled) || this.isFormControlDisabled(), ...(ngDevMode ? [{ debugName: "mergedDisabled" }] : []));
|
|
5852
6342
|
// Computed for editor states
|
|
5853
6343
|
this.isEditorReady = computed(() => this.editor() !== null, ...(ngDevMode ? [{ debugName: "isEditorReady" }] : []));
|
|
5854
6344
|
// ============================================
|
|
@@ -5856,102 +6346,132 @@ class AngularTiptapEditorComponent {
|
|
|
5856
6346
|
// ============================================
|
|
5857
6347
|
// Appearance & Fundamentals
|
|
5858
6348
|
this.finalSeamless = computed(() => {
|
|
5859
|
-
const
|
|
5860
|
-
if (
|
|
5861
|
-
return
|
|
5862
|
-
|
|
5863
|
-
return
|
|
6349
|
+
const inputVal = this.seamless();
|
|
6350
|
+
if (inputVal !== undefined)
|
|
6351
|
+
return inputVal;
|
|
6352
|
+
const fromConfig = this.effectiveConfig().mode;
|
|
6353
|
+
return fromConfig === "seamless";
|
|
5864
6354
|
}, ...(ngDevMode ? [{ debugName: "finalSeamless" }] : []));
|
|
5865
|
-
this.finalEditable = computed(() => this.
|
|
5866
|
-
this.finalPlaceholder = computed(() => this.
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
this.
|
|
5870
|
-
this.
|
|
5871
|
-
this.
|
|
5872
|
-
this.
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
6355
|
+
this.finalEditable = computed(() => this.editable() ?? this.effectiveConfig().editable, ...(ngDevMode ? [{ debugName: "finalEditable" }] : []));
|
|
6356
|
+
this.finalPlaceholder = computed(() => this.placeholder() ??
|
|
6357
|
+
this.effectiveConfig().placeholder ??
|
|
6358
|
+
this.currentTranslations().editor.placeholder, ...(ngDevMode ? [{ debugName: "finalPlaceholder" }] : []));
|
|
6359
|
+
this.finalFillContainer = computed(() => this.fillContainer() ?? this.effectiveConfig().fillContainer, ...(ngDevMode ? [{ debugName: "finalFillContainer" }] : []));
|
|
6360
|
+
this.finalShowFooter = computed(() => this.showFooter() ?? this.effectiveConfig().showFooter, ...(ngDevMode ? [{ debugName: "finalShowFooter" }] : []));
|
|
6361
|
+
this.finalShowEditToggle = computed(() => this.showEditToggle() ?? this.effectiveConfig().showEditToggle, ...(ngDevMode ? [{ debugName: "finalShowEditToggle" }] : []));
|
|
6362
|
+
this.finalHeight = computed(() => {
|
|
6363
|
+
const h = this.height() ?? this.effectiveConfig().height;
|
|
6364
|
+
return typeof h === "number" ? `${h}px` : h;
|
|
6365
|
+
}, ...(ngDevMode ? [{ debugName: "finalHeight" }] : []));
|
|
6366
|
+
this.finalMinHeight = computed(() => {
|
|
6367
|
+
const mh = this.minHeight() ?? this.effectiveConfig().minHeight;
|
|
6368
|
+
return typeof mh === "number" ? `${mh}px` : mh;
|
|
6369
|
+
}, ...(ngDevMode ? [{ debugName: "finalMinHeight" }] : []));
|
|
6370
|
+
this.finalMaxHeight = computed(() => {
|
|
6371
|
+
const mh = this.maxHeight() ?? this.effectiveConfig().maxHeight;
|
|
6372
|
+
return typeof mh === "number" ? `${mh}px` : mh;
|
|
6373
|
+
}, ...(ngDevMode ? [{ debugName: "finalMaxHeight" }] : []));
|
|
6374
|
+
this.finalSpellcheck = computed(() => this.spellcheck() ?? this.effectiveConfig().spellcheck, ...(ngDevMode ? [{ debugName: "finalSpellcheck" }] : []));
|
|
6375
|
+
this.finalEnableOfficePaste = computed(() => this.enableOfficePaste() ?? this.effectiveConfig().enableOfficePaste, ...(ngDevMode ? [{ debugName: "finalEnableOfficePaste" }] : []));
|
|
5876
6376
|
// Features
|
|
5877
|
-
this.finalShowToolbar = computed(() => this.
|
|
6377
|
+
this.finalShowToolbar = computed(() => this.showToolbar() ?? this.effectiveConfig().showToolbar, ...(ngDevMode ? [{ debugName: "finalShowToolbar" }] : []));
|
|
5878
6378
|
this.finalToolbarConfig = computed(() => {
|
|
5879
|
-
const
|
|
6379
|
+
const fromInput = this.toolbar();
|
|
6380
|
+
const fromConfig = this.effectiveConfig().toolbar;
|
|
5880
6381
|
const base = ATE_DEFAULT_TOOLBAR_CONFIG;
|
|
6382
|
+
if (fromInput && Object.keys(fromInput).length > 0) {
|
|
6383
|
+
return { ...base, ...fromInput };
|
|
6384
|
+
}
|
|
5881
6385
|
if (fromConfig) {
|
|
5882
6386
|
return { ...base, ...fromConfig };
|
|
5883
6387
|
}
|
|
5884
|
-
|
|
5885
|
-
return Object.keys(fromInput).length === 0 ? base : { ...base, ...fromInput };
|
|
6388
|
+
return base;
|
|
5886
6389
|
}, ...(ngDevMode ? [{ debugName: "finalToolbarConfig" }] : []));
|
|
5887
|
-
this.finalFloatingToolbar = computed(() => this.
|
|
5888
|
-
this.finalShowBubbleMenu = computed(() => this.
|
|
6390
|
+
this.finalFloatingToolbar = computed(() => this.floatingToolbar() ?? this.effectiveConfig().floatingToolbar, ...(ngDevMode ? [{ debugName: "finalFloatingToolbar" }] : []));
|
|
6391
|
+
this.finalShowBubbleMenu = computed(() => this.showBubbleMenu() ?? this.effectiveConfig().showBubbleMenu, ...(ngDevMode ? [{ debugName: "finalShowBubbleMenu" }] : []));
|
|
5889
6392
|
this.finalBubbleMenuConfig = computed(() => {
|
|
5890
|
-
const
|
|
6393
|
+
const fromInput = this.bubbleMenu();
|
|
6394
|
+
const fromConfig = this.effectiveConfig().bubbleMenu;
|
|
5891
6395
|
const base = ATE_DEFAULT_BUBBLE_MENU_CONFIG;
|
|
6396
|
+
if (fromInput && Object.keys(fromInput).length > 0) {
|
|
6397
|
+
return { ...base, ...fromInput };
|
|
6398
|
+
}
|
|
5892
6399
|
if (fromConfig) {
|
|
5893
6400
|
return { ...base, ...fromConfig };
|
|
5894
6401
|
}
|
|
5895
|
-
return
|
|
6402
|
+
return base;
|
|
5896
6403
|
}, ...(ngDevMode ? [{ debugName: "finalBubbleMenuConfig" }] : []));
|
|
5897
|
-
this.finalShowImageBubbleMenu = computed(() => this.
|
|
6404
|
+
this.finalShowImageBubbleMenu = computed(() => this.showImageBubbleMenu() ?? this.effectiveConfig().showImageBubbleMenu, ...(ngDevMode ? [{ debugName: "finalShowImageBubbleMenu" }] : []));
|
|
5898
6405
|
this.finalImageBubbleMenuConfig = computed(() => {
|
|
5899
|
-
const
|
|
6406
|
+
const fromInput = this.imageBubbleMenu();
|
|
6407
|
+
const fromConfig = this.effectiveConfig().imageBubbleMenu;
|
|
5900
6408
|
const base = ATE_DEFAULT_IMAGE_BUBBLE_MENU_CONFIG;
|
|
6409
|
+
if (fromInput && Object.keys(fromInput).length > 0) {
|
|
6410
|
+
return { ...base, ...fromInput };
|
|
6411
|
+
}
|
|
5901
6412
|
if (fromConfig) {
|
|
5902
6413
|
return { ...base, ...fromConfig };
|
|
5903
6414
|
}
|
|
5904
|
-
return
|
|
5905
|
-
? base
|
|
5906
|
-
: { ...base, ...this.imageBubbleMenu() };
|
|
6415
|
+
return base;
|
|
5907
6416
|
}, ...(ngDevMode ? [{ debugName: "finalImageBubbleMenuConfig" }] : []));
|
|
5908
|
-
this.finalShowTableBubbleMenu = computed(() => this.
|
|
6417
|
+
this.finalShowTableBubbleMenu = computed(() => this.showTableBubbleMenu() ?? this.effectiveConfig().showTableMenu, ...(ngDevMode ? [{ debugName: "finalShowTableBubbleMenu" }] : []));
|
|
5909
6418
|
this.finalTableBubbleMenuConfig = computed(() => {
|
|
5910
|
-
const
|
|
6419
|
+
const fromInput = this.tableBubbleMenu();
|
|
6420
|
+
const fromConfig = this.effectiveConfig().tableBubbleMenu;
|
|
5911
6421
|
const base = ATE_DEFAULT_TABLE_MENU_CONFIG;
|
|
6422
|
+
if (fromInput && Object.keys(fromInput).length > 0) {
|
|
6423
|
+
return { ...base, ...fromInput };
|
|
6424
|
+
}
|
|
5912
6425
|
if (fromConfig) {
|
|
5913
6426
|
return { ...base, ...fromConfig };
|
|
5914
6427
|
}
|
|
5915
|
-
return
|
|
5916
|
-
? base
|
|
5917
|
-
: { ...base, ...this.tableBubbleMenu() };
|
|
6428
|
+
return base;
|
|
5918
6429
|
}, ...(ngDevMode ? [{ debugName: "finalTableBubbleMenuConfig" }] : []));
|
|
5919
|
-
this.finalShowCellBubbleMenu = computed(() => this.
|
|
6430
|
+
this.finalShowCellBubbleMenu = computed(() => this.showCellBubbleMenu() ?? this.effectiveConfig().showCellMenu, ...(ngDevMode ? [{ debugName: "finalShowCellBubbleMenu" }] : []));
|
|
5920
6431
|
this.finalCellBubbleMenuConfig = computed(() => {
|
|
5921
|
-
const
|
|
6432
|
+
const fromInput = this.cellBubbleMenu();
|
|
6433
|
+
const fromConfig = this.effectiveConfig().cellBubbleMenu;
|
|
5922
6434
|
const base = ATE_DEFAULT_CELL_MENU_CONFIG;
|
|
6435
|
+
if (fromInput && Object.keys(fromInput).length > 0) {
|
|
6436
|
+
return { ...base, ...fromInput };
|
|
6437
|
+
}
|
|
5923
6438
|
if (fromConfig) {
|
|
5924
6439
|
return { ...base, ...fromConfig };
|
|
5925
6440
|
}
|
|
5926
|
-
return
|
|
5927
|
-
? base
|
|
5928
|
-
: { ...base, ...this.cellBubbleMenu() };
|
|
6441
|
+
return base;
|
|
5929
6442
|
}, ...(ngDevMode ? [{ debugName: "finalCellBubbleMenuConfig" }] : []));
|
|
5930
|
-
this.finalEnableSlashCommands = computed(() => this.
|
|
6443
|
+
this.finalEnableSlashCommands = computed(() => this.enableSlashCommands() ?? this.effectiveConfig().enableSlashCommands, ...(ngDevMode ? [{ debugName: "finalEnableSlashCommands" }] : []));
|
|
5931
6444
|
this.finalSlashCommandsConfig = computed(() => {
|
|
5932
|
-
const
|
|
5933
|
-
const
|
|
6445
|
+
const fromInputComponent = this.customSlashCommands();
|
|
6446
|
+
const fromConfigComponent = this.effectiveConfig().customSlashCommands;
|
|
6447
|
+
const customConfig = fromInputComponent ?? fromConfigComponent;
|
|
5934
6448
|
if (customConfig) {
|
|
5935
6449
|
return customConfig;
|
|
5936
6450
|
}
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
6451
|
+
const fromInputOptions = this.slashCommands();
|
|
6452
|
+
const fromConfigOptions = this.effectiveConfig().slashCommands;
|
|
6453
|
+
const baseConfig = fromInputOptions && Object.keys(fromInputOptions).length > 0
|
|
6454
|
+
? fromInputOptions
|
|
6455
|
+
: fromConfigOptions;
|
|
5941
6456
|
return {
|
|
5942
|
-
commands: filterSlashCommands(baseConfig, this.i18nService, this.editorCommandsService, this.finalImageUploadConfig()),
|
|
6457
|
+
commands: filterSlashCommands(baseConfig || {}, this.i18nService, this.editorCommandsService, this.finalImageUploadConfig()),
|
|
5943
6458
|
};
|
|
5944
6459
|
}, ...(ngDevMode ? [{ debugName: "finalSlashCommandsConfig" }] : []));
|
|
5945
6460
|
// Behavior
|
|
5946
|
-
this.finalAutofocus = computed(() => this.
|
|
5947
|
-
this.finalMaxCharacters = computed(() => this.
|
|
5948
|
-
this.finalShowCharacterCount = computed(() => this.
|
|
5949
|
-
this.finalShowWordCount = computed(() => this.
|
|
5950
|
-
this.finalLocale = computed(() => this.
|
|
6461
|
+
this.finalAutofocus = computed(() => this.autofocus() ?? this.effectiveConfig().autofocus, ...(ngDevMode ? [{ debugName: "finalAutofocus" }] : []));
|
|
6462
|
+
this.finalMaxCharacters = computed(() => this.maxCharacters() ?? this.effectiveConfig().maxCharacters, ...(ngDevMode ? [{ debugName: "finalMaxCharacters" }] : []));
|
|
6463
|
+
this.finalShowCharacterCount = computed(() => this.showCharacterCount() ?? this.effectiveConfig().showCharacterCount, ...(ngDevMode ? [{ debugName: "finalShowCharacterCount" }] : []));
|
|
6464
|
+
this.finalShowWordCount = computed(() => this.showWordCount() ?? this.effectiveConfig().showWordCount, ...(ngDevMode ? [{ debugName: "finalShowWordCount" }] : []));
|
|
6465
|
+
this.finalLocale = computed(() => this.locale() ?? this.effectiveConfig().locale, ...(ngDevMode ? [{ debugName: "finalLocale" }] : []));
|
|
6466
|
+
// Extensions & Options
|
|
6467
|
+
this.finalTiptapExtensions = computed(() => this.tiptapExtensions() ?? this.effectiveConfig().tiptapExtensions ?? [], ...(ngDevMode ? [{ debugName: "finalTiptapExtensions" }] : []));
|
|
6468
|
+
this.finalTiptapOptions = computed(() => this.tiptapOptions() ?? this.effectiveConfig().tiptapOptions ?? {}, ...(ngDevMode ? [{ debugName: "finalTiptapOptions" }] : []));
|
|
6469
|
+
this.finalStateCalculators = computed(() => this.stateCalculators() ?? this.effectiveConfig().stateCalculators ?? [], ...(ngDevMode ? [{ debugName: "finalStateCalculators" }] : []));
|
|
6470
|
+
this.finalAngularNodesConfig = computed(() => this.effectiveConfig().angularNodes ?? [], ...(ngDevMode ? [{ debugName: "finalAngularNodesConfig" }] : []));
|
|
5951
6471
|
// Image Upload
|
|
5952
6472
|
this.finalImageUploadConfig = computed(() => {
|
|
5953
|
-
const fromConfig = this.config().imageUpload;
|
|
5954
6473
|
const fromInput = this.imageUpload();
|
|
6474
|
+
const fromConfig = this.effectiveConfig().imageUpload;
|
|
5955
6475
|
const merged = {
|
|
5956
6476
|
maxSize: 5, // Default 5MB
|
|
5957
6477
|
maxWidth: 1920,
|
|
@@ -5962,15 +6482,15 @@ class AngularTiptapEditorComponent {
|
|
|
5962
6482
|
multiple: false,
|
|
5963
6483
|
compressImages: true,
|
|
5964
6484
|
quality: 0.8,
|
|
5965
|
-
...fromInput,
|
|
5966
6485
|
...fromConfig,
|
|
6486
|
+
...fromInput,
|
|
5967
6487
|
};
|
|
5968
6488
|
return {
|
|
5969
6489
|
...merged,
|
|
5970
6490
|
maxSize: merged.maxSize * 1024 * 1024, // Convert MB to bytes for internal service
|
|
5971
6491
|
};
|
|
5972
6492
|
}, ...(ngDevMode ? [{ debugName: "finalImageUploadConfig" }] : []));
|
|
5973
|
-
this.finalImageUploadHandler = computed(() => this.
|
|
6493
|
+
this.finalImageUploadHandler = computed(() => this.imageUploadHandler() ?? this.effectiveConfig().imageUpload?.handler, ...(ngDevMode ? [{ debugName: "finalImageUploadHandler" }] : []));
|
|
5974
6494
|
// Computed for current translations (allows per-instance override via config or input)
|
|
5975
6495
|
this.currentTranslations = computed(() => {
|
|
5976
6496
|
const localeOverride = this.finalLocale();
|
|
@@ -5987,6 +6507,17 @@ class AngularTiptapEditorComponent {
|
|
|
5987
6507
|
this.editorCommandsService = inject(AteEditorCommandsService);
|
|
5988
6508
|
// Access editor state via service
|
|
5989
6509
|
this.editorState = this.editorCommandsService.editorState;
|
|
6510
|
+
this.injector = inject(Injector);
|
|
6511
|
+
this.globalConfig = inject(ATE_GLOBAL_CONFIG, { optional: true });
|
|
6512
|
+
/**
|
|
6513
|
+
* Final merged configuration.
|
|
6514
|
+
* Priority: Input [config] > Global config via provideAteEditor()
|
|
6515
|
+
*/
|
|
6516
|
+
this.effectiveConfig = computed(() => {
|
|
6517
|
+
const fromInput = this.config();
|
|
6518
|
+
const fromGlobal = this.globalConfig || {};
|
|
6519
|
+
return { ...ATE_DEFAULT_CONFIG, ...fromGlobal, ...fromInput };
|
|
6520
|
+
}, ...(ngDevMode ? [{ debugName: "effectiveConfig" }] : []));
|
|
5990
6521
|
// Effect to update editor content (with anti-echo)
|
|
5991
6522
|
effect(() => {
|
|
5992
6523
|
const content = this.content(); // Sole reactive dependency
|
|
@@ -6008,7 +6539,7 @@ class AngularTiptapEditorComponent {
|
|
|
6008
6539
|
if (hasFormControl && !content) {
|
|
6009
6540
|
return;
|
|
6010
6541
|
}
|
|
6011
|
-
editor.commands.setContent(content, false);
|
|
6542
|
+
editor.commands.setContent(content, { emitUpdate: false });
|
|
6012
6543
|
});
|
|
6013
6544
|
});
|
|
6014
6545
|
// Effect to update height properties
|
|
@@ -6053,11 +6584,12 @@ class AngularTiptapEditorComponent {
|
|
|
6053
6584
|
}
|
|
6054
6585
|
}
|
|
6055
6586
|
});
|
|
6056
|
-
// Effect to re-initialize editor when
|
|
6587
|
+
// Effect to re-initialize editor when technical configuration changes
|
|
6057
6588
|
effect(() => {
|
|
6058
|
-
// Monitor
|
|
6059
|
-
this.
|
|
6060
|
-
this.
|
|
6589
|
+
// Monitor technical dependencies
|
|
6590
|
+
this.finalTiptapExtensions();
|
|
6591
|
+
this.finalTiptapOptions();
|
|
6592
|
+
this.finalAngularNodesConfig();
|
|
6061
6593
|
untracked(() => {
|
|
6062
6594
|
// Only if already initialized (post AfterViewInit)
|
|
6063
6595
|
if (this.editorFullyInitialized()) {
|
|
@@ -6136,7 +6668,7 @@ class AngularTiptapEditorComponent {
|
|
|
6136
6668
|
AteImageCalculator,
|
|
6137
6669
|
AteStructureCalculator,
|
|
6138
6670
|
AteDiscoveryCalculator,
|
|
6139
|
-
...this.
|
|
6671
|
+
...this.finalStateCalculators(),
|
|
6140
6672
|
],
|
|
6141
6673
|
}),
|
|
6142
6674
|
];
|
|
@@ -6153,20 +6685,34 @@ class AngularTiptapEditorComponent {
|
|
|
6153
6685
|
limit: this.finalMaxCharacters(),
|
|
6154
6686
|
}));
|
|
6155
6687
|
}
|
|
6688
|
+
// Register automatic node views from config
|
|
6689
|
+
const autoNodeViews = this.finalAngularNodesConfig();
|
|
6690
|
+
autoNodeViews.forEach((reg) => {
|
|
6691
|
+
const options = typeof reg === "function"
|
|
6692
|
+
? { component: reg }
|
|
6693
|
+
: reg;
|
|
6694
|
+
try {
|
|
6695
|
+
const extension = registerAngularComponent(this.injector, options);
|
|
6696
|
+
extensions.push(extension);
|
|
6697
|
+
}
|
|
6698
|
+
catch (e) {
|
|
6699
|
+
console.error("[ATE] Failed to auto-register node view:", e);
|
|
6700
|
+
}
|
|
6701
|
+
});
|
|
6156
6702
|
// Allow addition of custom extensions, but avoid duplicates by filtering by name
|
|
6157
|
-
const customExtensions = this.
|
|
6703
|
+
const customExtensions = this.finalTiptapExtensions();
|
|
6158
6704
|
if (customExtensions.length > 0) {
|
|
6159
6705
|
const existingNames = new Set(extensions
|
|
6160
|
-
.map(ext => ext?.name)
|
|
6706
|
+
.map((ext) => ext?.name)
|
|
6161
6707
|
.filter((name) => !!name));
|
|
6162
|
-
const toAdd = customExtensions.filter(ext => {
|
|
6708
|
+
const toAdd = customExtensions.filter((ext) => {
|
|
6163
6709
|
const name = ext?.name;
|
|
6164
6710
|
return !name || !existingNames.has(name);
|
|
6165
6711
|
});
|
|
6166
6712
|
extensions.push(...toAdd);
|
|
6167
6713
|
}
|
|
6168
6714
|
// Also allow any tiptap user options
|
|
6169
|
-
const userOptions = this.
|
|
6715
|
+
const userOptions = this.finalTiptapOptions();
|
|
6170
6716
|
const newEditor = new Editor({
|
|
6171
6717
|
...userOptions,
|
|
6172
6718
|
element: this.editorElement().nativeElement,
|
|
@@ -6562,568 +7108,174 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
6562
7108
|
<!-- Table Menu -->
|
|
6563
7109
|
@if (finalEditable() && finalShowTableBubbleMenu() && editor()) {
|
|
6564
7110
|
<ate-table-bubble-menu
|
|
6565
|
-
[editor]="editor()!"
|
|
6566
|
-
[config]="finalTableBubbleMenuConfig()"
|
|
6567
|
-
[style.display]="editorFullyInitialized() ? 'block' : 'none'"></ate-table-bubble-menu>
|
|
6568
|
-
}
|
|
6569
|
-
|
|
6570
|
-
<!-- Cell Menu -->
|
|
6571
|
-
@if (finalEditable() && finalShowCellBubbleMenu() && editor()) {
|
|
6572
|
-
<ate-cell-bubble-menu
|
|
6573
|
-
[editor]="editor()!"
|
|
6574
|
-
[config]="finalCellBubbleMenuConfig()"
|
|
6575
|
-
[style.display]="editorFullyInitialized() ? 'block' : 'none'"></ate-cell-bubble-menu>
|
|
6576
|
-
}
|
|
6577
|
-
|
|
6578
|
-
<!-- Counters -->
|
|
6579
|
-
@if (
|
|
6580
|
-
finalEditable() &&
|
|
6581
|
-
!mergedDisabled() &&
|
|
6582
|
-
finalShowFooter() &&
|
|
6583
|
-
(finalShowCharacterCount() || finalShowWordCount())
|
|
6584
|
-
) {
|
|
6585
|
-
<div
|
|
6586
|
-
class="character-count"
|
|
6587
|
-
[class.limit-reached]="finalMaxCharacters() && characterCount() >= finalMaxCharacters()!">
|
|
6588
|
-
@if (finalShowCharacterCount()) {
|
|
6589
|
-
{{ characterCount() }}
|
|
6590
|
-
{{ currentTranslations().editor.character }}{{ characterCount() > 1 ? "s" : "" }}
|
|
6591
|
-
@if (finalMaxCharacters()) {
|
|
6592
|
-
/ {{ finalMaxCharacters() }}
|
|
6593
|
-
}
|
|
6594
|
-
}
|
|
6595
|
-
|
|
6596
|
-
@if (finalShowCharacterCount() && finalShowWordCount()) {
|
|
6597
|
-
,
|
|
6598
|
-
}
|
|
6599
|
-
|
|
6600
|
-
@if (finalShowWordCount()) {
|
|
6601
|
-
{{ wordCount() }}
|
|
6602
|
-
{{ currentTranslations().editor.word }}{{ wordCount() > 1 ? "s" : "" }}
|
|
6603
|
-
}
|
|
6604
|
-
</div>
|
|
6605
|
-
}
|
|
6606
|
-
</div>
|
|
6607
|
-
`, styles: [":host{--ate-primary: #2563eb;--ate-primary-contrast: #ffffff;--ate-primary-light: color-mix(in srgb, var(--ate-primary), transparent 90%);--ate-primary-lighter: color-mix(in srgb, var(--ate-primary), transparent 95%);--ate-primary-light-alpha: color-mix(in srgb, var(--ate-primary), transparent 85%);--ate-surface: #ffffff;--ate-surface-secondary: #f8f9fa;--ate-surface-tertiary: #f1f5f9;--ate-text: #2d3748;--ate-text-secondary: #64748b;--ate-text-muted: #a0aec0;--ate-border: #e2e8f0;--ate-highlight-bg: #fef08a;--ate-highlight-color: #854d0e;--ate-button-hover: #f1f5f9;--ate-button-active: #e2e8f0;--ate-error-color: #c53030;--ate-error-bg: #fed7d7;--ate-error-border: #feb2b2;--ate-border-color: var(--ate-border);--ate-border-width: 2px;--ate-border-radius: 12px;--ate-focus-color: var(--ate-primary);--ate-background: var(--ate-surface);--ate-sub-border-radius: 8px;--ate-text-color: var(--ate-text);--ate-placeholder-color: var(--ate-text-muted);--ate-line-height: 1.6;--ate-content-padding: 16px;--ate-menu-bg: var(--ate-surface);--ate-menu-border-radius: var(--ate-border-radius);--ate-menu-border: var(--ate-border);--ate-menu-shadow: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05);--ate-menu-padding: 6px;--ate-toolbar-padding: var(--ate-menu-padding);--ate-toolbar-background: var(--ate-surface-secondary);--ate-toolbar-border-color: var(--ate-border);--ate-toolbar-button-color: var(--ate-text-secondary);--ate-toolbar-button-hover-background: transparent;--ate-toolbar-button-active-background: var(--ate-primary-light);--ate-toolbar-button-active-color: var(--ate-primary);--ate-counter-color: var(--ate-text-secondary);--ate-counter-background: var(--ate-surface-secondary);--ate-counter-border-color: var(--ate-border);--ate-drag-background: #f0f8ff;--ate-drag-border-color: var(--ate-primary);--ate-blockquote-border-color: var(--ate-border);--ate-blockquote-background: var(--ate-surface-secondary);--ate-code-background: var(--ate-surface-secondary);--ate-code-color: var(--ate-code-color);--ate-code-border-color: var(--ate-border);--ate-code-block-background: #0f172a;--ate-code-block-color: #e2e8f0;--ate-code-block-border-color: var(--ate-border);--ate-image-border-radius: 16px;--ate-image-selected-color: var(--ate-primary);--ate-scrollbar-width: 10px;--ate-scrollbar-thumb: var(--ate-border);--ate-scrollbar-thumb-hover: var(--ate-text-muted);--ate-scrollbar-track: transparent;--ate-table-border-color: var(--ate-border);--ate-table-header-background: var(--ate-surface-secondary);--ate-table-header-color: var(--ate-text);--ate-table-cell-background: var(--ate-surface);--ate-table-cell-selected-background: var(--ate-primary-light);--ate-table-resize-handle-color: var(--ate-primary);--ate-table-row-hover-background: var(--ate-primary-lighter)}:host(.dark),:host([data-theme=\"dark\"]){--ate-primary: #3b82f6;--ate-primary-contrast: #ffffff;--ate-primary-light: color-mix(in srgb, var(--ate-primary), transparent 85%);--ate-primary-lighter: color-mix(in srgb, var(--ate-primary), transparent 92%);--ate-primary-light-alpha: color-mix(in srgb, var(--ate-primary), transparent 80%);--ate-surface: #020617;--ate-surface-secondary: #0f172a;--ate-surface-tertiary: #1e293b;--ate-text: #f8fafc;--ate-text-secondary: #94a3b8;--ate-text-muted: #64748b;--ate-border: #1e293b;--ate-highlight-bg: #854d0e;--ate-highlight-color: #fef08a;--ate-button-hover: #1e293b;--ate-button-active: #0f172a;--ate-menu-border: rgba(255, 255, 255, .1);--ate-menu-shadow: 0 20px 25px -5px rgba(0, 0, 0, .3), 0 10px 10px -5px rgba(0, 0, 0, .2);--ate-error-color: #f87171;--ate-error-bg: #450a0a;--ate-error-border: #7f1d1d;--ate-drag-background: var(--ate-surface-tertiary);--ate-drag-border-color: var(--ate-primary);--ate-blockquote-border-color: var(--ate-primary);--ate-toolbar-button-active-background: var(--ate-primary-light);--ate-toolbar-button-active-color: var(--ate-primary);--ate-button-hover: var(--ate-surface-tertiary);--ate-button-active: var(--ate-surface-secondary);--ate-scrollbar-thumb: var(--ate-surface-tertiary);--ate-scrollbar-thumb-hover: var(--ate-text-muted)}:host(.fill-container){display:block;height:100%}.ate-editor{border:var(--ate-border-width) solid var(--ate-border-color);border-radius:var(--ate-border-radius);background:var(--ate-background);overflow:visible;transition:border-color .2s ease;position:relative}:host(.floating-toolbar) .ate-editor{overflow:visible}:host(.fill-container) .ate-editor{display:flex;flex-direction:column;height:100%}:host(.fill-container) .ate-content-wrapper{flex:1;min-height:0}:host(.fill-container) .ate-content{flex:1;min-height:0;overflow-y:auto}.ate-editor:focus-within{border-color:var(--ate-focus-color)}.ate-content{min-height:var(--editor-min-height, 200px);height:var(--editor-height, auto);max-height:var(--editor-max-height, none);overflow-y:var(--editor-overflow, visible);outline:none;position:relative;scrollbar-width:thin;scrollbar-color:var(--ate-scrollbar-thumb) var(--ate-scrollbar-track)}:host(.is-disabled) .ate-content{cursor:not-allowed;opacity:.7;-webkit-user-select:none;user-select:none;pointer-events:none;background-color:var(--ate-surface-tertiary)}:host(.is-readonly) .ate-content{cursor:default;-webkit-user-select:text;user-select:text}:host(.is-readonly) .ate-content ::ng-deep .ate-link{cursor:pointer;pointer-events:auto}.ate-content::-webkit-scrollbar{width:var(--ate-scrollbar-width)}.ate-content-wrapper{position:relative;display:flex;flex-direction:column;min-height:0}.ate-content-wrapper .ate-content{flex:1}.ate-content::-webkit-scrollbar-track{background:var(--ate-scrollbar-track)}.ate-content::-webkit-scrollbar-thumb{background:var(--ate-scrollbar-thumb);border:3px solid transparent;background-clip:content-box;border-radius:10px}.ate-content::-webkit-scrollbar-thumb:hover{background:var(--ate-scrollbar-thumb-hover);background-clip:content-box}.ate-content.drag-over{background:var(--ate-drag-background);border:2px dashed var(--ate-drag-border-color)}.character-count{padding:6px 8px;font-size:12px;color:var(--ate-counter-color);text-align:right;border-top:1px solid var(--ate-counter-border-color);background:var(--ate-counter-background);transition:color .2s ease;border-bottom-left-radius:calc(var(--ate-border-radius) - var(--ate-border-width));border-bottom-right-radius:calc(var(--ate-border-radius) - var(--ate-border-width))}.character-count.limit-reached{color:var(--ate-error-color, #ef4444);font-weight:600}:host ::ng-deep .ProseMirror{padding:var(--ate-content-padding);outline:none;line-height:var(--ate-line-height);color:var(--ate-text-color);min-height:100%;height:100%;word-wrap:break-word;overflow-wrap:break-word}:host ::ng-deep .ProseMirror h1{font-size:2em;font-weight:700;margin-top:0;margin-bottom:.5em}:host ::ng-deep .ProseMirror h2{font-size:1.5em;font-weight:700;margin-top:1em;margin-bottom:.5em}:host ::ng-deep .ProseMirror h3{font-size:1.25em;font-weight:700;margin-top:1em;margin-bottom:.5em}:host ::ng-deep .ProseMirror p{margin:.5em 0}:host ::ng-deep .ProseMirror ul,:host ::ng-deep .ProseMirror ol{padding-left:2em;margin:.5em 0}:host ::ng-deep .ProseMirror blockquote{border-left:4px solid var(--ate-blockquote-border-color);margin:1em 0;background:var(--ate-blockquote-background);padding:.5em 1em;border-radius:0 4px 4px 0}:host ::ng-deep .ProseMirror code{background:var(--ate-code-background);color:var(--ate-code-color);border:1px solid var(--ate-code-border-color);padding:.15em .4em;border-radius:4px;font-family:JetBrains Mono,Fira Code,Monaco,Consolas,monospace;font-size:.85em;font-weight:500}:host ::ng-deep .ProseMirror pre{background:var(--ate-code-block-background);color:var(--ate-code-block-color);border:1px solid var(--ate-code-block-border-color);padding:1em;border-radius:var(--ate-border-radius);overflow-x:auto;margin:1em 0}:host ::ng-deep .ProseMirror pre code{background:none;color:inherit;border:none;padding:0}:host ::ng-deep .ProseMirror p.is-editor-empty:first-child:before{content:attr(data-placeholder);color:var(--ate-placeholder-color);pointer-events:none;float:left;height:0}:host ::ng-deep .ProseMirror[contenteditable=false]{pointer-events:none}:host ::ng-deep .ProseMirror[contenteditable=false] img{cursor:default;pointer-events:none}:host ::ng-deep .ProseMirror[contenteditable=false] img:hover{transform:none;box-shadow:0 2px 8px #0000001a}:host ::ng-deep .ProseMirror[contenteditable=false] img.ProseMirror-selectednode{outline:none}:host ::ng-deep .ProseMirror img{position:relative;display:inline-block;max-width:100%;height:auto;cursor:pointer;transition:all .2s ease;border:2px solid transparent;border-radius:var(--ate-image-border-radius)}:host ::ng-deep .ProseMirror img:hover{border-color:var(--ate-border-color);box-shadow:0 2px 4px #0000001a}:host ::ng-deep .ProseMirror img.ProseMirror-selectednode{border-color:var(--ate-image-selected-color);box-shadow:0 0 0 3px var(--ate-primary-light-alpha);transition:all .2s ease}:host ::ng-deep .ProseMirror .tiptap-image{max-width:100%;height:auto;border-radius:var(--ate-image-border-radius);box-shadow:0 4px 20px #00000014;margin:.5em 0;cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);display:block;filter:brightness(1) contrast(1)}:host ::ng-deep .ProseMirror .tiptap-image:hover{box-shadow:0 8px 30px #0000001f;filter:brightness(1.02) contrast(1.02)}:host ::ng-deep .ProseMirror .tiptap-image.ProseMirror-selectednode{outline:2px solid var(--ate-primary);outline-offset:2px;border-radius:var(--ate-image-border-radius);box-shadow:0 0 0 4px var(--ate-primary-light-alpha)}:host ::ng-deep .image-container{margin:.5em 0;text-align:center;border-radius:16px;overflow:hidden;transition:all .3s cubic-bezier(.4,0,.2,1)}:host ::ng-deep .image-container.image-align-left{text-align:left}:host ::ng-deep .image-container.image-align-center{text-align:center}:host ::ng-deep .image-container.image-align-right{text-align:right}:host ::ng-deep .image-container img{display:inline-block;max-width:100%;height:auto;border-radius:16px}:host ::ng-deep .resizable-image-container{position:relative;display:inline-block;margin:.5em 0}:host ::ng-deep .resize-controls{position:absolute;inset:0;pointer-events:none;z-index:1000}:host ::ng-deep .resize-handle{position:absolute;width:12px;height:12px;background:var(--ate-primary);border:2px solid var(--ate-surface);border-radius:50%;pointer-events:all;cursor:pointer;z-index:1001;transition:all .15s ease;box-shadow:0 2px 6px #0003}:host ::ng-deep .resize-handle:hover{background:var(--ate-primary);box-shadow:0 3px 8px #0000004d}:host ::ng-deep .resize-handle:active{background:var(--ate-primary)}:host ::ng-deep .resize-handle-n:hover,:host ::ng-deep .resize-handle-s:hover{transform:translate(-50%) scale(1.2)}:host ::ng-deep .resize-handle-w:hover,:host ::ng-deep .resize-handle-e:hover{transform:translateY(-50%) scale(1.2)}:host ::ng-deep .resize-handle-n:active,:host ::ng-deep .resize-handle-s:active{transform:translate(-50%) scale(.9)}:host ::ng-deep .resize-handle-w:active,:host ::ng-deep .resize-handle-e:active{transform:translateY(-50%) scale(.9)}:host ::ng-deep .resize-handle-nw:hover,:host ::ng-deep .resize-handle-ne:hover,:host ::ng-deep .resize-handle-sw:hover,:host ::ng-deep .resize-handle-se:hover{transform:scale(1.2)}:host ::ng-deep .resize-handle-nw:active,:host ::ng-deep .resize-handle-ne:active,:host ::ng-deep .resize-handle-sw:active,:host ::ng-deep .resize-handle-se:active{transform:scale(.9)}:host ::ng-deep .resize-handle-nw{top:0;left:-6px;cursor:nw-resize}:host ::ng-deep .resize-handle-n{top:0;left:50%;transform:translate(-50%);cursor:n-resize}:host ::ng-deep .resize-handle-ne{top:0;right:-6px;cursor:ne-resize}:host ::ng-deep .resize-handle-w{top:50%;left:-6px;transform:translateY(-50%);cursor:w-resize}:host ::ng-deep .resize-handle-e{top:50%;right:-6px;transform:translateY(-50%);cursor:e-resize}:host ::ng-deep .resize-handle-sw{bottom:0;left:-6px;cursor:sw-resize}:host ::ng-deep .resize-handle-s{bottom:0;left:50%;transform:translate(-50%);cursor:s-resize}:host ::ng-deep .resize-handle-se{bottom:0;right:-6px;cursor:se-resize}:host ::ng-deep body.resizing{-webkit-user-select:none;user-select:none;cursor:crosshair}:host ::ng-deep body.resizing .ProseMirror{pointer-events:none}:host ::ng-deep body.resizing .ProseMirror .tiptap-image{pointer-events:none}:host ::ng-deep .image-size-info{position:absolute;bottom:-20px;left:50%;transform:translate(-50%);background:#000c;color:#fff;padding:2px 6px;border-radius:3px;font-size:11px;white-space:nowrap;opacity:0;transition:opacity .2s ease}:host ::ng-deep .image-container:hover .image-size-info{opacity:1}:host ::ng-deep .ProseMirror table{border-collapse:separate;border-spacing:0;margin:0;table-layout:fixed;width:100%;border-radius:8px;overflow:hidden}:host ::ng-deep .ProseMirror table td,:host ::ng-deep .ProseMirror table th{border:none;border-right:1px solid var(--ate-table-border-color);border-bottom:1px solid var(--ate-table-border-color);box-sizing:border-box;min-width:1em;padding:8px 12px;position:relative;vertical-align:top;text-align:left}:host ::ng-deep .ProseMirror table td{background:var(--ate-table-cell-background)}:host ::ng-deep .ProseMirror table td:first-child,:host ::ng-deep .ProseMirror table th:first-child{border-left:1px solid var(--ate-table-border-color)}:host ::ng-deep .ProseMirror table tr:first-child td,:host ::ng-deep .ProseMirror table tr:first-child th{border-top:1px solid var(--ate-table-border-color)}:host ::ng-deep .ProseMirror table tr:first-child th:first-child{border-top-left-radius:8px}:host ::ng-deep .ProseMirror table tr:first-child th:last-child{border-top-right-radius:8px}:host ::ng-deep .ProseMirror table tr:last-child td:first-child{border-bottom-left-radius:8px}:host ::ng-deep .ProseMirror table tr:last-child td:last-child{border-bottom-right-radius:8px}:host ::ng-deep .ProseMirror table th{background:var(--ate-table-header-background);font-weight:600;color:var(--ate-table-header-color)}:host ::ng-deep .ProseMirror table .selectedCell:after{background:var(--ate-table-cell-selected-background);content:\"\";inset:0;pointer-events:none;position:absolute;z-index:2}:host ::ng-deep .ProseMirror table .column-resize-handle{position:absolute;right:-2px;top:0;bottom:0;width:4px;background-color:var(--ate-table-resize-handle-color);opacity:0;transition:opacity .2s ease}:host ::ng-deep .ProseMirror table:hover .column-resize-handle{opacity:1}:host ::ng-deep .ProseMirror table .column-resize-handle:hover{background-color:var(--ate-focus-color)}:host ::ng-deep .ProseMirror .tableWrapper{overflow-x:auto;margin:1em 0;border-radius:8px}:host ::ng-deep .ProseMirror .tableWrapper table{margin:0;border-radius:8px;min-width:600px;overflow:hidden}:host ::ng-deep .ProseMirror table p{margin:0}:host ::ng-deep .ProseMirror table tbody tr:hover td{background-color:var(--ate-table-row-hover-background)}\n"] }]
|
|
6608
|
-
}], ctorParameters: () => [], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], content: [{ type: i0.Input, args: [{ isSignal: true, alias: "content", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], minHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "minHeight", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], fillContainer: [{ type: i0.Input, args: [{ isSignal: true, alias: "fillContainer", required: false }] }], showToolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "showToolbar", required: false }] }], showFooter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFooter", required: false }] }], showCharacterCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCharacterCount", required: false }] }], showWordCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "showWordCount", required: false }] }], maxCharacters: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxCharacters", required: false }] }], enableOfficePaste: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableOfficePaste", required: false }] }], enableSlashCommands: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableSlashCommands", required: false }] }], slashCommands: [{ type: i0.Input, args: [{ isSignal: true, alias: "slashCommands", required: false }] }], customSlashCommands: [{ type: i0.Input, args: [{ isSignal: true, alias: "customSlashCommands", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], autofocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "autofocus", required: false }] }], seamless: [{ type: i0.Input, args: [{ isSignal: true, alias: "seamless", required: false }] }], floatingToolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "floatingToolbar", required: false }] }], showEditToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showEditToggle", required: false }] }], spellcheck: [{ type: i0.Input, args: [{ isSignal: true, alias: "spellcheck", required: false }] }], tiptapExtensions: [{ type: i0.Input, args: [{ isSignal: true, alias: "tiptapExtensions", required: false }] }], tiptapOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "tiptapOptions", required: false }] }], showBubbleMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "showBubbleMenu", required: false }] }], bubbleMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "bubbleMenu", required: false }] }], showImageBubbleMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "showImageBubbleMenu", required: false }] }], imageBubbleMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "imageBubbleMenu", required: false }] }], toolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolbar", required: false }] }], showTableBubbleMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTableBubbleMenu", required: false }] }], tableBubbleMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "tableBubbleMenu", required: false }] }], showCellBubbleMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCellBubbleMenu", required: false }] }], cellBubbleMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellBubbleMenu", required: false }] }], stateCalculators: [{ type: i0.Input, args: [{ isSignal: true, alias: "stateCalculators", required: false }] }], imageUpload: [{ type: i0.Input, args: [{ isSignal: true, alias: "imageUpload", required: false }] }], imageUploadHandler: [{ type: i0.Input, args: [{ isSignal: true, alias: "imageUploadHandler", required: false }] }], contentChange: [{ type: i0.Output, args: ["contentChange"] }], editorCreated: [{ type: i0.Output, args: ["editorCreated"] }], editorUpdate: [{ type: i0.Output, args: ["editorUpdate"] }], editorFocus: [{ type: i0.Output, args: ["editorFocus"] }], editorBlur: [{ type: i0.Output, args: ["editorBlur"] }], editableChange: [{ type: i0.Output, args: ["editableChange"] }], editorElement: [{ type: i0.ViewChild, args: ["editorElement", { isSignal: true }] }] } });
|
|
6609
|
-
|
|
6610
|
-
/**
|
|
6611
|
-
*
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
"redo",
|
|
6640
|
-
"clear",
|
|
6641
|
-
"textColor",
|
|
6642
|
-
"separator",
|
|
6643
|
-
];
|
|
6644
|
-
|
|
6645
|
-
/**
|
|
6646
|
-
* Clés des options du menu bulle de texte
|
|
6647
|
-
*/
|
|
6648
|
-
const ATE_BUBBLE_MENU_KEYS = [
|
|
6649
|
-
"bold",
|
|
6650
|
-
"italic",
|
|
6651
|
-
"underline",
|
|
6652
|
-
"strike",
|
|
6653
|
-
"code",
|
|
6654
|
-
"superscript",
|
|
6655
|
-
"subscript",
|
|
6656
|
-
"highlight",
|
|
6657
|
-
"highlightPicker",
|
|
6658
|
-
"textColor",
|
|
6659
|
-
"link",
|
|
6660
|
-
"separator",
|
|
6661
|
-
];
|
|
6662
|
-
/**
|
|
6663
|
-
* Clés des options du menu bulle d'image
|
|
6664
|
-
*/
|
|
6665
|
-
const ATE_IMAGE_BUBBLE_MENU_KEYS = [
|
|
6666
|
-
"changeImage",
|
|
6667
|
-
"resizeSmall",
|
|
6668
|
-
"resizeMedium",
|
|
6669
|
-
"resizeLarge",
|
|
6670
|
-
"resizeOriginal",
|
|
6671
|
-
"deleteImage",
|
|
6672
|
-
"separator",
|
|
6673
|
-
];
|
|
6674
|
-
/**
|
|
6675
|
-
* Clés des options du menu de table
|
|
6676
|
-
*/
|
|
6677
|
-
const ATE_TABLE_BUBBLE_MENU_KEYS = [
|
|
6678
|
-
"addRowBefore",
|
|
6679
|
-
"addRowAfter",
|
|
6680
|
-
"deleteRow",
|
|
6681
|
-
"addColumnBefore",
|
|
6682
|
-
"addColumnAfter",
|
|
6683
|
-
"deleteColumn",
|
|
6684
|
-
"deleteTable",
|
|
6685
|
-
"toggleHeaderRow",
|
|
6686
|
-
"toggleHeaderColumn",
|
|
6687
|
-
"separator",
|
|
6688
|
-
];
|
|
6689
|
-
/**
|
|
6690
|
-
* Clés des options du menu de cellule
|
|
6691
|
-
*/
|
|
6692
|
-
const ATE_CELL_BUBBLE_MENU_KEYS = ["mergeCells", "splitCell"];
|
|
6693
|
-
|
|
6694
|
-
/**
|
|
6695
|
-
* Base abstract class for Angular components used as TipTap NodeViews.
|
|
6696
|
-
*
|
|
6697
|
-
* Extend this class in your custom components to get access to the TipTap node properties.
|
|
6698
|
-
*
|
|
6699
|
-
* @example
|
|
6700
|
-
* ```typescript
|
|
6701
|
-
* @Component({
|
|
6702
|
-
* selector: 'app-counter',
|
|
6703
|
-
* template: `
|
|
6704
|
-
* <div>
|
|
6705
|
-
* <button (click)="increment()">Count: {{ node().attrs['count'] }}</button>
|
|
6706
|
-
* </div>
|
|
6707
|
-
* `
|
|
6708
|
-
* })
|
|
6709
|
-
* export class CounterComponent extends AteAngularNodeView {
|
|
6710
|
-
* increment() {
|
|
6711
|
-
* const count = this.node().attrs['count'] || 0;
|
|
6712
|
-
* this.updateAttributes({ count: count + 1 });
|
|
6713
|
-
* }
|
|
6714
|
-
* }
|
|
6715
|
-
* ```
|
|
6716
|
-
*/
|
|
6717
|
-
class AteAngularNodeView {
|
|
6718
|
-
/**
|
|
6719
|
-
* Internal method to initialize the component with NodeView props.
|
|
6720
|
-
* This is called by the AngularNodeViewRenderer.
|
|
6721
|
-
*
|
|
6722
|
-
* @internal
|
|
6723
|
-
*/
|
|
6724
|
-
_initNodeView(props) {
|
|
6725
|
-
// Create signals from the props
|
|
6726
|
-
const editorSignal = signal(props.editor, ...(ngDevMode ? [{ debugName: "editorSignal" }] : []));
|
|
6727
|
-
const nodeSignal = signal(props.node, ...(ngDevMode ? [{ debugName: "nodeSignal" }] : []));
|
|
6728
|
-
const decorationsSignal = signal(props.decorations, ...(ngDevMode ? [{ debugName: "decorationsSignal" }] : []));
|
|
6729
|
-
const selectedSignal = signal(props.selected, ...(ngDevMode ? [{ debugName: "selectedSignal" }] : []));
|
|
6730
|
-
const extensionSignal = signal(props.extension, ...(ngDevMode ? [{ debugName: "extensionSignal" }] : []));
|
|
6731
|
-
// Assign to the component
|
|
6732
|
-
this.editor = editorSignal.asReadonly();
|
|
6733
|
-
this.node = nodeSignal.asReadonly();
|
|
6734
|
-
this.decorations = decorationsSignal.asReadonly();
|
|
6735
|
-
this.selected = selectedSignal.asReadonly();
|
|
6736
|
-
this.extension = extensionSignal.asReadonly();
|
|
6737
|
-
this.getPos = props.getPos;
|
|
6738
|
-
this.updateAttributes = props.updateAttributes;
|
|
6739
|
-
this.deleteNode = props.deleteNode;
|
|
6740
|
-
// Store writable signals for updates
|
|
6741
|
-
this._writableSignals = {
|
|
6742
|
-
node: nodeSignal,
|
|
6743
|
-
decorations: decorationsSignal,
|
|
6744
|
-
selected: selectedSignal,
|
|
6745
|
-
};
|
|
6746
|
-
}
|
|
6747
|
-
/**
|
|
6748
|
-
* Internal method to update the component when the node changes.
|
|
6749
|
-
* This is called by the AngularNodeViewRenderer.
|
|
6750
|
-
*
|
|
6751
|
-
* @internal
|
|
6752
|
-
*/
|
|
6753
|
-
_updateNodeView(node, decorations) {
|
|
6754
|
-
if (this._writableSignals) {
|
|
6755
|
-
this._writableSignals.node.set(node);
|
|
6756
|
-
this._writableSignals.decorations.set(decorations);
|
|
6757
|
-
}
|
|
6758
|
-
}
|
|
6759
|
-
/**
|
|
6760
|
-
* Internal method to update the selection state.
|
|
6761
|
-
* This is called by the AngularNodeViewRenderer.
|
|
6762
|
-
*
|
|
6763
|
-
* @internal
|
|
6764
|
-
*/
|
|
6765
|
-
_selectNodeView() {
|
|
6766
|
-
if (this._writableSignals) {
|
|
6767
|
-
this._writableSignals.selected.set(true);
|
|
6768
|
-
}
|
|
6769
|
-
}
|
|
6770
|
-
/**
|
|
6771
|
-
* Internal method to update the deselection state.
|
|
6772
|
-
* This is called by the AngularNodeViewRenderer.
|
|
6773
|
-
*
|
|
6774
|
-
* @internal
|
|
6775
|
-
*/
|
|
6776
|
-
_deselectNodeView() {
|
|
6777
|
-
if (this._writableSignals) {
|
|
6778
|
-
this._writableSignals.selected.set(false);
|
|
6779
|
-
}
|
|
6780
|
-
}
|
|
6781
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AteAngularNodeView, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
6782
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.16", type: AteAngularNodeView, isStandalone: true, ngImport: i0 }); }
|
|
6783
|
-
}
|
|
6784
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AteAngularNodeView, decorators: [{
|
|
6785
|
-
type: Directive
|
|
6786
|
-
}] });
|
|
6787
|
-
|
|
6788
|
-
/**
|
|
6789
|
-
* Universal Renderer for Angular Components as TipTap NodeViews.
|
|
6790
|
-
*
|
|
6791
|
-
* Supports:
|
|
6792
|
-
* - TipTap-Aware components (extending AteAngularNodeView)
|
|
6793
|
-
* - Standard library components (automatic @Input() sync)
|
|
6794
|
-
* - Signal-based inputs and outputs (Angular 18+)
|
|
6795
|
-
* - Content projection (editableContent)
|
|
6796
|
-
* - Unified lifecycle and event management
|
|
6797
|
-
*/
|
|
6798
|
-
function AteNodeViewRenderer(component, options) {
|
|
6799
|
-
return props => {
|
|
6800
|
-
const { node, view: _view, getPos, decorations, editor, extension } = props;
|
|
6801
|
-
const { injector, inputs = {}, wrapperTag = "div", wrapperClass, editableContent = false, contentSelector, onOutput, ignoreMutation = true, } = options;
|
|
6802
|
-
const dom = document.createElement(wrapperTag);
|
|
6803
|
-
if (wrapperClass) {
|
|
6804
|
-
dom.className = wrapperClass;
|
|
6805
|
-
}
|
|
6806
|
-
const applicationRef = injector.get(ApplicationRef);
|
|
6807
|
-
const environmentInjector = injector.get(EnvironmentInjector);
|
|
6808
|
-
// 1. Setup Content Projection (ng-content)
|
|
6809
|
-
let initialNodes = [];
|
|
6810
|
-
let contentDOM = null;
|
|
6811
|
-
if (editableContent && !contentSelector) {
|
|
6812
|
-
contentDOM = document.createElement("div");
|
|
6813
|
-
contentDOM.setAttribute("data-node-view-content", "");
|
|
6814
|
-
initialNodes = [[contentDOM]];
|
|
6815
|
-
}
|
|
6816
|
-
// 2. Create the Angular Component
|
|
6817
|
-
const componentRef = createComponent(component, {
|
|
6818
|
-
environmentInjector,
|
|
6819
|
-
elementInjector: injector,
|
|
6820
|
-
projectableNodes: initialNodes,
|
|
6821
|
-
});
|
|
6822
|
-
const instance = componentRef.instance;
|
|
6823
|
-
const subscriptions = [];
|
|
6824
|
-
// 3. Initialize TipTap-Aware instances
|
|
6825
|
-
if (instance instanceof AteAngularNodeView) {
|
|
6826
|
-
instance._initNodeView({
|
|
6827
|
-
editor,
|
|
6828
|
-
node,
|
|
6829
|
-
decorations,
|
|
6830
|
-
selected: false,
|
|
6831
|
-
extension,
|
|
6832
|
-
getPos,
|
|
6833
|
-
updateAttributes: attrs => editor.commands.updateAttributes(node.type.name, attrs),
|
|
6834
|
-
deleteNode: () => {
|
|
6835
|
-
const pos = getPos();
|
|
6836
|
-
if (pos !== undefined) {
|
|
6837
|
-
editor.commands.deleteRange({ from: pos, to: pos + node.nodeSize });
|
|
6838
|
-
}
|
|
6839
|
-
},
|
|
6840
|
-
});
|
|
6841
|
-
}
|
|
6842
|
-
// 4. Synchronize Inputs (Handles standard @Input and Signal-based inputs)
|
|
6843
|
-
const syncInputs = (nodeToSync) => {
|
|
6844
|
-
// Combine base inputs from options with dynamic node attributes
|
|
6845
|
-
const mergedInputs = { ...inputs, ...nodeToSync.attrs };
|
|
6846
|
-
Object.entries(mergedInputs).forEach(([key, value]) => {
|
|
6847
|
-
if (key !== "id" && value !== null && value !== undefined) {
|
|
6848
|
-
try {
|
|
6849
|
-
componentRef.setInput(key, value);
|
|
6850
|
-
}
|
|
6851
|
-
catch {
|
|
6852
|
-
// Silently ignore inputs that don't exist on the component
|
|
6853
|
-
}
|
|
6854
|
-
}
|
|
6855
|
-
});
|
|
6856
|
-
};
|
|
6857
|
-
syncInputs(node);
|
|
6858
|
-
// 5. Setup Outputs (Handles EventEmitter and OutputRef)
|
|
6859
|
-
if (onOutput) {
|
|
6860
|
-
Object.entries(instance).forEach(([key, potentialOutput]) => {
|
|
6861
|
-
if (potentialOutput &&
|
|
6862
|
-
typeof potentialOutput
|
|
6863
|
-
.subscribe === "function") {
|
|
6864
|
-
const sub = potentialOutput.subscribe((value) => {
|
|
6865
|
-
onOutput(key, value);
|
|
6866
|
-
});
|
|
6867
|
-
if (sub instanceof Subscription) {
|
|
6868
|
-
subscriptions.push(sub);
|
|
6869
|
-
}
|
|
6870
|
-
}
|
|
6871
|
-
});
|
|
6872
|
-
}
|
|
6873
|
-
// 6. Attach to DOM and ApplicationRef
|
|
6874
|
-
applicationRef.attachView(componentRef.hostView);
|
|
6875
|
-
const componentElement = componentRef.location.nativeElement;
|
|
6876
|
-
// Target specific element for content if selector provided
|
|
6877
|
-
if (editableContent && contentSelector) {
|
|
6878
|
-
contentDOM = componentElement.querySelector(contentSelector);
|
|
6879
|
-
}
|
|
6880
|
-
dom.appendChild(componentElement);
|
|
6881
|
-
// Initial detection to ensure the component is rendered
|
|
6882
|
-
componentRef.changeDetectorRef.detectChanges();
|
|
6883
|
-
// 7. Return the TipTap NodeView Interface
|
|
6884
|
-
return {
|
|
6885
|
-
dom,
|
|
6886
|
-
contentDOM,
|
|
6887
|
-
update: (updatedNode, updatedDecorations) => {
|
|
6888
|
-
if (updatedNode.type !== node.type) {
|
|
6889
|
-
return false;
|
|
6890
|
-
}
|
|
6891
|
-
// Update Aware component signals
|
|
6892
|
-
if (instance instanceof AteAngularNodeView) {
|
|
6893
|
-
instance._updateNodeView(updatedNode, updatedDecorations);
|
|
6894
|
-
}
|
|
6895
|
-
// Update inputs
|
|
6896
|
-
syncInputs(updatedNode);
|
|
6897
|
-
// Notify and Detect changes
|
|
6898
|
-
componentRef.changeDetectorRef.detectChanges();
|
|
6899
|
-
if (options.onUpdate) {
|
|
6900
|
-
options.onUpdate(updatedNode);
|
|
6901
|
-
}
|
|
6902
|
-
return true;
|
|
6903
|
-
},
|
|
6904
|
-
selectNode: () => {
|
|
6905
|
-
if (instance instanceof AteAngularNodeView) {
|
|
6906
|
-
instance._selectNodeView();
|
|
6907
|
-
}
|
|
6908
|
-
dom.classList.add("ProseMirror-selectednode");
|
|
6909
|
-
},
|
|
6910
|
-
deselectNode: () => {
|
|
6911
|
-
if (instance instanceof AteAngularNodeView) {
|
|
6912
|
-
instance._deselectNodeView();
|
|
6913
|
-
}
|
|
6914
|
-
dom.classList.remove("ProseMirror-selectednode");
|
|
6915
|
-
},
|
|
6916
|
-
destroy: () => {
|
|
6917
|
-
if (options.onDestroy) {
|
|
6918
|
-
options.onDestroy();
|
|
6919
|
-
}
|
|
6920
|
-
subscriptions.forEach(s => s.unsubscribe());
|
|
6921
|
-
applicationRef.detachView(componentRef.hostView);
|
|
6922
|
-
componentRef.destroy();
|
|
6923
|
-
},
|
|
6924
|
-
stopEvent: event => {
|
|
6925
|
-
const target = event.target;
|
|
6926
|
-
const isEditable = target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable;
|
|
6927
|
-
return isEditable;
|
|
6928
|
-
},
|
|
6929
|
-
ignoreMutation: mutation => {
|
|
6930
|
-
if (typeof ignoreMutation === "function") {
|
|
6931
|
-
return ignoreMutation(mutation);
|
|
6932
|
-
}
|
|
6933
|
-
return ignoreMutation;
|
|
6934
|
-
},
|
|
6935
|
-
};
|
|
6936
|
-
};
|
|
7111
|
+
[editor]="editor()!"
|
|
7112
|
+
[config]="finalTableBubbleMenuConfig()"
|
|
7113
|
+
[style.display]="editorFullyInitialized() ? 'block' : 'none'"></ate-table-bubble-menu>
|
|
7114
|
+
}
|
|
7115
|
+
|
|
7116
|
+
<!-- Cell Menu -->
|
|
7117
|
+
@if (finalEditable() && finalShowCellBubbleMenu() && editor()) {
|
|
7118
|
+
<ate-cell-bubble-menu
|
|
7119
|
+
[editor]="editor()!"
|
|
7120
|
+
[config]="finalCellBubbleMenuConfig()"
|
|
7121
|
+
[style.display]="editorFullyInitialized() ? 'block' : 'none'"></ate-cell-bubble-menu>
|
|
7122
|
+
}
|
|
7123
|
+
|
|
7124
|
+
<!-- Counters -->
|
|
7125
|
+
@if (
|
|
7126
|
+
finalEditable() &&
|
|
7127
|
+
!mergedDisabled() &&
|
|
7128
|
+
finalShowFooter() &&
|
|
7129
|
+
(finalShowCharacterCount() || finalShowWordCount())
|
|
7130
|
+
) {
|
|
7131
|
+
<div
|
|
7132
|
+
class="character-count"
|
|
7133
|
+
[class.limit-reached]="finalMaxCharacters() && characterCount() >= finalMaxCharacters()!">
|
|
7134
|
+
@if (finalShowCharacterCount()) {
|
|
7135
|
+
{{ characterCount() }}
|
|
7136
|
+
{{ currentTranslations().editor.character }}{{ characterCount() > 1 ? "s" : "" }}
|
|
7137
|
+
@if (finalMaxCharacters()) {
|
|
7138
|
+
/ {{ finalMaxCharacters() }}
|
|
7139
|
+
}
|
|
7140
|
+
}
|
|
7141
|
+
|
|
7142
|
+
@if (finalShowCharacterCount() && finalShowWordCount()) {
|
|
7143
|
+
,
|
|
7144
|
+
}
|
|
7145
|
+
|
|
7146
|
+
@if (finalShowWordCount()) {
|
|
7147
|
+
{{ wordCount() }}
|
|
7148
|
+
{{ currentTranslations().editor.word }}{{ wordCount() > 1 ? "s" : "" }}
|
|
7149
|
+
}
|
|
7150
|
+
</div>
|
|
7151
|
+
}
|
|
7152
|
+
</div>
|
|
7153
|
+
`, styles: [":host{--ate-primary: #2563eb;--ate-primary-contrast: #ffffff;--ate-primary-light: color-mix(in srgb, var(--ate-primary), transparent 90%);--ate-primary-lighter: color-mix(in srgb, var(--ate-primary), transparent 95%);--ate-primary-light-alpha: color-mix(in srgb, var(--ate-primary), transparent 85%);--ate-surface: #ffffff;--ate-surface-secondary: #f8f9fa;--ate-surface-tertiary: #f1f5f9;--ate-text: #2d3748;--ate-text-secondary: #64748b;--ate-text-muted: #a0aec0;--ate-border: #e2e8f0;--ate-highlight-bg: #fef08a;--ate-highlight-color: #854d0e;--ate-button-hover: #f1f5f9;--ate-button-active: #e2e8f0;--ate-error-color: #c53030;--ate-error-bg: #fed7d7;--ate-error-border: #feb2b2;--ate-border-color: var(--ate-border);--ate-border-width: 2px;--ate-border-radius: 12px;--ate-focus-color: var(--ate-primary);--ate-background: var(--ate-surface);--ate-sub-border-radius: 8px;--ate-text-color: var(--ate-text);--ate-placeholder-color: var(--ate-text-muted);--ate-line-height: 1.6;--ate-content-padding: 16px;--ate-menu-bg: var(--ate-surface);--ate-menu-border-radius: var(--ate-border-radius);--ate-menu-border: var(--ate-border);--ate-menu-shadow: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05);--ate-menu-padding: 6px;--ate-toolbar-padding: var(--ate-menu-padding);--ate-toolbar-background: var(--ate-surface-secondary);--ate-toolbar-border-color: var(--ate-border);--ate-toolbar-button-color: var(--ate-text-secondary);--ate-toolbar-button-hover-background: transparent;--ate-toolbar-button-active-background: var(--ate-primary-light);--ate-toolbar-button-active-color: var(--ate-primary);--ate-counter-color: var(--ate-text-secondary);--ate-counter-background: var(--ate-surface-secondary);--ate-counter-border-color: var(--ate-border);--ate-drag-background: #f0f8ff;--ate-drag-border-color: var(--ate-primary);--ate-blockquote-border-color: var(--ate-border);--ate-blockquote-background: var(--ate-surface-secondary);--ate-code-background: var(--ate-surface-secondary);--ate-code-color: var(--ate-code-color);--ate-code-border-color: var(--ate-border);--ate-code-block-background: #0f172a;--ate-code-block-color: #e2e8f0;--ate-code-block-border-color: var(--ate-border);--ate-image-border-radius: 16px;--ate-image-selected-color: var(--ate-primary);--ate-scrollbar-width: 10px;--ate-scrollbar-thumb: var(--ate-border);--ate-scrollbar-thumb-hover: var(--ate-text-muted);--ate-scrollbar-track: transparent;--ate-table-border-color: var(--ate-border);--ate-table-header-background: var(--ate-surface-secondary);--ate-table-header-color: var(--ate-text);--ate-table-cell-background: var(--ate-surface);--ate-table-cell-selected-background: var(--ate-primary-light);--ate-table-resize-handle-color: var(--ate-primary);--ate-table-row-hover-background: var(--ate-primary-lighter)}:host(.dark),:host([data-theme=\"dark\"]){--ate-primary: #3b82f6;--ate-primary-contrast: #ffffff;--ate-primary-light: color-mix(in srgb, var(--ate-primary), transparent 85%);--ate-primary-lighter: color-mix(in srgb, var(--ate-primary), transparent 92%);--ate-primary-light-alpha: color-mix(in srgb, var(--ate-primary), transparent 80%);--ate-surface: #020617;--ate-surface-secondary: #0f172a;--ate-surface-tertiary: #1e293b;--ate-text: #f8fafc;--ate-text-secondary: #94a3b8;--ate-text-muted: #64748b;--ate-border: #1e293b;--ate-highlight-bg: #854d0e;--ate-highlight-color: #fef08a;--ate-button-hover: #1e293b;--ate-button-active: #0f172a;--ate-menu-border: rgba(255, 255, 255, .1);--ate-menu-shadow: 0 20px 25px -5px rgba(0, 0, 0, .3), 0 10px 10px -5px rgba(0, 0, 0, .2);--ate-error-color: #f87171;--ate-error-bg: #450a0a;--ate-error-border: #7f1d1d;--ate-drag-background: var(--ate-surface-tertiary);--ate-drag-border-color: var(--ate-primary);--ate-blockquote-border-color: var(--ate-primary);--ate-toolbar-button-active-background: var(--ate-primary-light);--ate-toolbar-button-active-color: var(--ate-primary);--ate-button-hover: var(--ate-surface-tertiary);--ate-button-active: var(--ate-surface-secondary);--ate-scrollbar-thumb: var(--ate-surface-tertiary);--ate-scrollbar-thumb-hover: var(--ate-text-muted)}:host(.fill-container){display:block;height:100%}.ate-editor{border:var(--ate-border-width) solid var(--ate-border-color);border-radius:var(--ate-border-radius);background:var(--ate-background);overflow:visible;transition:border-color .2s ease;position:relative}:host(.floating-toolbar) .ate-editor{overflow:visible}:host(.fill-container) .ate-editor{display:flex;flex-direction:column;height:100%}:host(.fill-container) .ate-content-wrapper{flex:1;min-height:0}:host(.fill-container) .ate-content{flex:1;min-height:0;overflow-y:auto}.ate-editor:focus-within{border-color:var(--ate-focus-color)}.ate-content{min-height:var(--editor-min-height, 200px);height:var(--editor-height, auto);max-height:var(--editor-max-height, none);overflow-y:var(--editor-overflow, visible);outline:none;position:relative;scrollbar-width:thin;scrollbar-color:var(--ate-scrollbar-thumb) var(--ate-scrollbar-track)}:host(.is-disabled) .ate-content{cursor:not-allowed;opacity:.7;-webkit-user-select:none;user-select:none;pointer-events:none;background-color:var(--ate-surface-tertiary)}:host(.is-readonly) .ate-content{cursor:default;-webkit-user-select:text;user-select:text}:host(.is-readonly) .ate-content ::ng-deep .ate-link{cursor:pointer;pointer-events:auto}.ate-content::-webkit-scrollbar{width:var(--ate-scrollbar-width)}.ate-content-wrapper{position:relative;display:flex;flex-direction:column;min-height:0}.ate-content-wrapper .ate-content{flex:1}.ate-content::-webkit-scrollbar-track{background:var(--ate-scrollbar-track)}.ate-content::-webkit-scrollbar-thumb{background:var(--ate-scrollbar-thumb);border:3px solid transparent;background-clip:content-box;border-radius:10px}.ate-content::-webkit-scrollbar-thumb:hover{background:var(--ate-scrollbar-thumb-hover);background-clip:content-box}.ate-content.drag-over{background:var(--ate-drag-background);border:2px dashed var(--ate-drag-border-color)}.character-count{padding:6px 8px;font-size:12px;color:var(--ate-counter-color);text-align:right;border-top:1px solid var(--ate-counter-border-color);background:var(--ate-counter-background);transition:color .2s ease;border-bottom-left-radius:calc(var(--ate-border-radius) - var(--ate-border-width));border-bottom-right-radius:calc(var(--ate-border-radius) - var(--ate-border-width))}.character-count.limit-reached{color:var(--ate-error-color, #ef4444);font-weight:600}:host ::ng-deep .ProseMirror{padding:var(--ate-content-padding);outline:none;line-height:var(--ate-line-height);color:var(--ate-text-color);min-height:100%;height:100%;word-wrap:break-word;overflow-wrap:break-word}:host ::ng-deep .ProseMirror h1{font-size:2em;font-weight:700;margin-top:0;margin-bottom:.5em}:host ::ng-deep .ProseMirror h2{font-size:1.5em;font-weight:700;margin-top:1em;margin-bottom:.5em}:host ::ng-deep .ProseMirror h3{font-size:1.25em;font-weight:700;margin-top:1em;margin-bottom:.5em}:host ::ng-deep .ProseMirror p{margin:.5em 0}:host ::ng-deep .ProseMirror ul,:host ::ng-deep .ProseMirror ol{padding-left:2em;margin:.5em 0}:host ::ng-deep .ProseMirror blockquote{border-left:4px solid var(--ate-blockquote-border-color);margin:1em 0;background:var(--ate-blockquote-background);padding:.5em 1em;border-radius:0 4px 4px 0}:host ::ng-deep .ProseMirror code{background:var(--ate-code-background);color:var(--ate-code-color);border:1px solid var(--ate-code-border-color);padding:.15em .4em;border-radius:4px;font-family:JetBrains Mono,Fira Code,Monaco,Consolas,monospace;font-size:.85em;font-weight:500}:host ::ng-deep .ProseMirror pre{background:var(--ate-code-block-background);color:var(--ate-code-block-color);border:1px solid var(--ate-code-block-border-color);padding:1em;border-radius:var(--ate-border-radius);overflow-x:auto;margin:1em 0}:host ::ng-deep .ProseMirror pre code{background:none;color:inherit;border:none;padding:0}:host ::ng-deep .ProseMirror p.is-editor-empty:first-child:before{content:attr(data-placeholder);color:var(--ate-placeholder-color);pointer-events:none;float:left;height:0}:host ::ng-deep .ProseMirror[contenteditable=false]{pointer-events:none}:host ::ng-deep .ProseMirror[contenteditable=false] img{cursor:default;pointer-events:none}:host ::ng-deep .ProseMirror[contenteditable=false] img:hover{transform:none;box-shadow:0 2px 8px #0000001a}:host ::ng-deep .ProseMirror[contenteditable=false] img.ProseMirror-selectednode{outline:none}:host ::ng-deep .ProseMirror img{position:relative;display:inline-block;max-width:100%;height:auto;cursor:pointer;transition:all .2s ease;border:2px solid transparent;border-radius:var(--ate-image-border-radius)}:host ::ng-deep .ProseMirror img:hover{border-color:var(--ate-border-color);box-shadow:0 2px 4px #0000001a}:host ::ng-deep .ProseMirror img.ProseMirror-selectednode{border-color:var(--ate-image-selected-color);box-shadow:0 0 0 3px var(--ate-primary-light-alpha);transition:all .2s ease}:host ::ng-deep .ProseMirror .tiptap-image{max-width:100%;height:auto;border-radius:var(--ate-image-border-radius);box-shadow:0 4px 20px #00000014;margin:.5em 0;cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);display:block;filter:brightness(1) contrast(1)}:host ::ng-deep .ProseMirror .tiptap-image:hover{box-shadow:0 8px 30px #0000001f;filter:brightness(1.02) contrast(1.02)}:host ::ng-deep .ProseMirror .tiptap-image.ProseMirror-selectednode{outline:2px solid var(--ate-primary);outline-offset:2px;border-radius:var(--ate-image-border-radius);box-shadow:0 0 0 4px var(--ate-primary-light-alpha)}:host ::ng-deep .image-container{margin:.5em 0;text-align:center;border-radius:16px;overflow:hidden;transition:all .3s cubic-bezier(.4,0,.2,1)}:host ::ng-deep .image-container.image-align-left{text-align:left}:host ::ng-deep .image-container.image-align-center{text-align:center}:host ::ng-deep .image-container.image-align-right{text-align:right}:host ::ng-deep .image-container img{display:inline-block;max-width:100%;height:auto;border-radius:16px}:host ::ng-deep .resizable-image-container{position:relative;display:inline-block;margin:.5em 0}:host ::ng-deep .resize-controls{position:absolute;inset:0;pointer-events:none;z-index:1000}:host ::ng-deep .resize-handle{position:absolute;width:12px;height:12px;background:var(--ate-primary);border:2px solid var(--ate-surface);border-radius:50%;pointer-events:all;cursor:pointer;z-index:1001;transition:all .15s ease;box-shadow:0 2px 6px #0003}:host ::ng-deep .resize-handle:hover{background:var(--ate-primary);box-shadow:0 3px 8px #0000004d}:host ::ng-deep .resize-handle:active{background:var(--ate-primary)}:host ::ng-deep .resize-handle-n:hover,:host ::ng-deep .resize-handle-s:hover{transform:translate(-50%) scale(1.2)}:host ::ng-deep .resize-handle-w:hover,:host ::ng-deep .resize-handle-e:hover{transform:translateY(-50%) scale(1.2)}:host ::ng-deep .resize-handle-n:active,:host ::ng-deep .resize-handle-s:active{transform:translate(-50%) scale(.9)}:host ::ng-deep .resize-handle-w:active,:host ::ng-deep .resize-handle-e:active{transform:translateY(-50%) scale(.9)}:host ::ng-deep .resize-handle-nw:hover,:host ::ng-deep .resize-handle-ne:hover,:host ::ng-deep .resize-handle-sw:hover,:host ::ng-deep .resize-handle-se:hover{transform:scale(1.2)}:host ::ng-deep .resize-handle-nw:active,:host ::ng-deep .resize-handle-ne:active,:host ::ng-deep .resize-handle-sw:active,:host ::ng-deep .resize-handle-se:active{transform:scale(.9)}:host ::ng-deep .resize-handle-nw{top:0;left:-6px;cursor:nw-resize}:host ::ng-deep .resize-handle-n{top:0;left:50%;transform:translate(-50%);cursor:n-resize}:host ::ng-deep .resize-handle-ne{top:0;right:-6px;cursor:ne-resize}:host ::ng-deep .resize-handle-w{top:50%;left:-6px;transform:translateY(-50%);cursor:w-resize}:host ::ng-deep .resize-handle-e{top:50%;right:-6px;transform:translateY(-50%);cursor:e-resize}:host ::ng-deep .resize-handle-sw{bottom:0;left:-6px;cursor:sw-resize}:host ::ng-deep .resize-handle-s{bottom:0;left:50%;transform:translate(-50%);cursor:s-resize}:host ::ng-deep .resize-handle-se{bottom:0;right:-6px;cursor:se-resize}:host ::ng-deep body.resizing{-webkit-user-select:none;user-select:none;cursor:crosshair}:host ::ng-deep body.resizing .ProseMirror{pointer-events:none}:host ::ng-deep body.resizing .ProseMirror .tiptap-image{pointer-events:none}:host ::ng-deep .image-size-info{position:absolute;bottom:-20px;left:50%;transform:translate(-50%);background:#000c;color:#fff;padding:2px 6px;border-radius:3px;font-size:11px;white-space:nowrap;opacity:0;transition:opacity .2s ease}:host ::ng-deep .image-container:hover .image-size-info{opacity:1}:host ::ng-deep .ProseMirror table{border-collapse:separate;border-spacing:0;margin:0;table-layout:fixed;width:100%;border-radius:8px;overflow:hidden}:host ::ng-deep .ProseMirror table td,:host ::ng-deep .ProseMirror table th{border:none;border-right:1px solid var(--ate-table-border-color);border-bottom:1px solid var(--ate-table-border-color);box-sizing:border-box;min-width:1em;padding:8px 12px;position:relative;vertical-align:top;text-align:left}:host ::ng-deep .ProseMirror table td{background:var(--ate-table-cell-background)}:host ::ng-deep .ProseMirror table td:first-child,:host ::ng-deep .ProseMirror table th:first-child{border-left:1px solid var(--ate-table-border-color)}:host ::ng-deep .ProseMirror table tr:first-child td,:host ::ng-deep .ProseMirror table tr:first-child th{border-top:1px solid var(--ate-table-border-color)}:host ::ng-deep .ProseMirror table tr:first-child th:first-child{border-top-left-radius:8px}:host ::ng-deep .ProseMirror table tr:first-child th:last-child{border-top-right-radius:8px}:host ::ng-deep .ProseMirror table tr:last-child td:first-child{border-bottom-left-radius:8px}:host ::ng-deep .ProseMirror table tr:last-child td:last-child{border-bottom-right-radius:8px}:host ::ng-deep .ProseMirror table th{background:var(--ate-table-header-background);font-weight:600;color:var(--ate-table-header-color)}:host ::ng-deep .ProseMirror table .selectedCell:after{background:var(--ate-table-cell-selected-background);content:\"\";inset:0;pointer-events:none;position:absolute;z-index:2}:host ::ng-deep .ProseMirror table .column-resize-handle{position:absolute;right:-2px;top:0;bottom:0;width:4px;background-color:var(--ate-table-resize-handle-color);opacity:0;transition:opacity .2s ease}:host ::ng-deep .ProseMirror table:hover .column-resize-handle{opacity:1}:host ::ng-deep .ProseMirror table .column-resize-handle:hover{background-color:var(--ate-focus-color)}:host ::ng-deep .ProseMirror .tableWrapper{overflow-x:auto;margin:1em 0;border-radius:8px}:host ::ng-deep .ProseMirror .tableWrapper table{margin:0;border-radius:8px;min-width:600px;overflow:hidden}:host ::ng-deep .ProseMirror table p{margin:0}:host ::ng-deep .ProseMirror table tbody tr:hover td{background-color:var(--ate-table-row-hover-background)}\n"] }]
|
|
7154
|
+
}], ctorParameters: () => [], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], content: [{ type: i0.Input, args: [{ isSignal: true, alias: "content", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], minHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "minHeight", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], fillContainer: [{ type: i0.Input, args: [{ isSignal: true, alias: "fillContainer", required: false }] }], showToolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "showToolbar", required: false }] }], showFooter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFooter", required: false }] }], showCharacterCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCharacterCount", required: false }] }], showWordCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "showWordCount", required: false }] }], maxCharacters: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxCharacters", required: false }] }], enableOfficePaste: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableOfficePaste", required: false }] }], enableSlashCommands: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableSlashCommands", required: false }] }], slashCommands: [{ type: i0.Input, args: [{ isSignal: true, alias: "slashCommands", required: false }] }], customSlashCommands: [{ type: i0.Input, args: [{ isSignal: true, alias: "customSlashCommands", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], autofocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "autofocus", required: false }] }], seamless: [{ type: i0.Input, args: [{ isSignal: true, alias: "seamless", required: false }] }], floatingToolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "floatingToolbar", required: false }] }], showEditToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showEditToggle", required: false }] }], spellcheck: [{ type: i0.Input, args: [{ isSignal: true, alias: "spellcheck", required: false }] }], tiptapExtensions: [{ type: i0.Input, args: [{ isSignal: true, alias: "tiptapExtensions", required: false }] }], tiptapOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "tiptapOptions", required: false }] }], showBubbleMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "showBubbleMenu", required: false }] }], bubbleMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "bubbleMenu", required: false }] }], showImageBubbleMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "showImageBubbleMenu", required: false }] }], imageBubbleMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "imageBubbleMenu", required: false }] }], toolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolbar", required: false }] }], showTableBubbleMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTableBubbleMenu", required: false }] }], tableBubbleMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "tableBubbleMenu", required: false }] }], showCellBubbleMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCellBubbleMenu", required: false }] }], cellBubbleMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellBubbleMenu", required: false }] }], stateCalculators: [{ type: i0.Input, args: [{ isSignal: true, alias: "stateCalculators", required: false }] }], imageUpload: [{ type: i0.Input, args: [{ isSignal: true, alias: "imageUpload", required: false }] }], imageUploadHandler: [{ type: i0.Input, args: [{ isSignal: true, alias: "imageUploadHandler", required: false }] }], contentChange: [{ type: i0.Output, args: ["contentChange"] }], editorCreated: [{ type: i0.Output, args: ["editorCreated"] }], editorUpdate: [{ type: i0.Output, args: ["editorUpdate"] }], editorFocus: [{ type: i0.Output, args: ["editorFocus"] }], editorBlur: [{ type: i0.Output, args: ["editorBlur"] }], editableChange: [{ type: i0.Output, args: ["editableChange"] }], editorElement: [{ type: i0.ViewChild, args: ["editorElement", { isSignal: true }] }] } });
|
|
7155
|
+
|
|
7156
|
+
/**
|
|
7157
|
+
* Provides the necessary configuration and initialization for the Angular TipTap Editor.
|
|
7158
|
+
* This should be called in your app.config.ts (for standalone) or main.ts.
|
|
7159
|
+
*
|
|
7160
|
+
* This provider is essential for 'Angular Nodes' to work correctly, as it captures the
|
|
7161
|
+
* root Injector required to instantiate custom Angular components within the editor.
|
|
7162
|
+
* @example
|
|
7163
|
+
* ```ts
|
|
7164
|
+
* bootstrapApplication(AppComponent, {
|
|
7165
|
+
* providers: [
|
|
7166
|
+
* provideAteEditor({
|
|
7167
|
+
* theme: 'dark',
|
|
7168
|
+
* mode: 'seamless'
|
|
7169
|
+
* })
|
|
7170
|
+
* ]
|
|
7171
|
+
* });
|
|
7172
|
+
* ```
|
|
7173
|
+
*/
|
|
7174
|
+
function provideAteEditor(config) {
|
|
7175
|
+
return makeEnvironmentProviders([
|
|
7176
|
+
{
|
|
7177
|
+
provide: ATE_GLOBAL_CONFIG,
|
|
7178
|
+
useValue: config || {},
|
|
7179
|
+
},
|
|
7180
|
+
provideEnvironmentInitializer(() => {
|
|
7181
|
+
const injector = inject(Injector);
|
|
7182
|
+
setGlobalInjector(injector);
|
|
7183
|
+
}),
|
|
7184
|
+
]);
|
|
6937
7185
|
}
|
|
6938
7186
|
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
"
|
|
7187
|
+
/**
|
|
7188
|
+
* Clés des boutons de la barre d'outils native
|
|
7189
|
+
*/
|
|
7190
|
+
const ATE_TOOLBAR_KEYS = [
|
|
7191
|
+
"bold",
|
|
7192
|
+
"italic",
|
|
7193
|
+
"underline",
|
|
7194
|
+
"strike",
|
|
7195
|
+
"code",
|
|
7196
|
+
"codeBlock",
|
|
7197
|
+
"superscript",
|
|
7198
|
+
"subscript",
|
|
7199
|
+
"highlight",
|
|
7200
|
+
"highlightPicker",
|
|
7201
|
+
"heading1",
|
|
7202
|
+
"heading2",
|
|
7203
|
+
"heading3",
|
|
6944
7204
|
"bulletList",
|
|
6945
7205
|
"orderedList",
|
|
6946
|
-
"listItem",
|
|
6947
7206
|
"blockquote",
|
|
6948
|
-
"
|
|
6949
|
-
"
|
|
7207
|
+
"alignLeft",
|
|
7208
|
+
"alignCenter",
|
|
7209
|
+
"alignRight",
|
|
7210
|
+
"alignJustify",
|
|
7211
|
+
"link",
|
|
7212
|
+
"image",
|
|
6950
7213
|
"horizontalRule",
|
|
7214
|
+
"table",
|
|
7215
|
+
"undo",
|
|
7216
|
+
"redo",
|
|
7217
|
+
"clear",
|
|
7218
|
+
"textColor",
|
|
7219
|
+
"separator",
|
|
6951
7220
|
];
|
|
7221
|
+
|
|
6952
7222
|
/**
|
|
6953
|
-
*
|
|
7223
|
+
* Clés des options du menu bulle de texte
|
|
6954
7224
|
*/
|
|
6955
|
-
|
|
6956
|
-
|
|
6957
|
-
|
|
6958
|
-
|
|
6959
|
-
|
|
6960
|
-
|
|
6961
|
-
|
|
6962
|
-
|
|
6963
|
-
|
|
6964
|
-
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
const tag = nodeName.toLowerCase().replace(/([A-Z])/g, "-$1");
|
|
6970
|
-
return { nodeName, tag };
|
|
6971
|
-
}
|
|
7225
|
+
const ATE_BUBBLE_MENU_KEYS = [
|
|
7226
|
+
"bold",
|
|
7227
|
+
"italic",
|
|
7228
|
+
"underline",
|
|
7229
|
+
"strike",
|
|
7230
|
+
"code",
|
|
7231
|
+
"superscript",
|
|
7232
|
+
"subscript",
|
|
7233
|
+
"highlight",
|
|
7234
|
+
"highlightPicker",
|
|
7235
|
+
"textColor",
|
|
7236
|
+
"link",
|
|
7237
|
+
"separator",
|
|
7238
|
+
];
|
|
6972
7239
|
/**
|
|
6973
|
-
*
|
|
7240
|
+
* Clés des options du menu bulle d'image
|
|
6974
7241
|
*/
|
|
6975
|
-
|
|
6976
|
-
|
|
6977
|
-
|
|
6978
|
-
|
|
6979
|
-
|
|
6980
|
-
|
|
6981
|
-
|
|
6982
|
-
|
|
6983
|
-
|
|
6984
|
-
}
|
|
6985
|
-
try {
|
|
6986
|
-
return JSON.parse(attr);
|
|
6987
|
-
}
|
|
6988
|
-
catch {
|
|
6989
|
-
return attr;
|
|
6990
|
-
}
|
|
6991
|
-
},
|
|
6992
|
-
renderHTML: (attrs) => {
|
|
6993
|
-
const value = attrs[key];
|
|
6994
|
-
if (value === undefined || value === null) {
|
|
6995
|
-
return {};
|
|
6996
|
-
}
|
|
6997
|
-
const serialized = typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
6998
|
-
return { [`data-${key}`]: serialized };
|
|
6999
|
-
},
|
|
7000
|
-
};
|
|
7001
|
-
});
|
|
7002
|
-
return tiptapAttributes;
|
|
7003
|
-
}
|
|
7242
|
+
const ATE_IMAGE_BUBBLE_MENU_KEYS = [
|
|
7243
|
+
"changeImage",
|
|
7244
|
+
"resizeSmall",
|
|
7245
|
+
"resizeMedium",
|
|
7246
|
+
"resizeLarge",
|
|
7247
|
+
"resizeOriginal",
|
|
7248
|
+
"deleteImage",
|
|
7249
|
+
"separator",
|
|
7250
|
+
];
|
|
7004
7251
|
/**
|
|
7005
|
-
*
|
|
7006
|
-
* Supports both "TipTap-Aware" and "Standard" modes.
|
|
7007
|
-
*
|
|
7008
|
-
* @internal
|
|
7252
|
+
* Clés des options du menu de table
|
|
7009
7253
|
*/
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
|
|
7013
|
-
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
|
|
7022
|
-
group,
|
|
7023
|
-
inline: group === "inline",
|
|
7024
|
-
atom,
|
|
7025
|
-
draggable,
|
|
7026
|
-
content: editableContent ? (contentMode === "inline" ? "inline*" : "block*") : undefined,
|
|
7027
|
-
addAttributes() {
|
|
7028
|
-
return tiptapAttributes;
|
|
7029
|
-
},
|
|
7030
|
-
parseHTML() {
|
|
7031
|
-
if (customParseHTML) {
|
|
7032
|
-
return customParseHTML.call(this);
|
|
7033
|
-
}
|
|
7034
|
-
return [{ tag }, { tag: `div[data-component="${nodeName}"]` }];
|
|
7035
|
-
},
|
|
7036
|
-
renderHTML({ node, HTMLAttributes: attrs }) {
|
|
7037
|
-
if (customRenderHTML) {
|
|
7038
|
-
return customRenderHTML.call(this, { node, HTMLAttributes: attrs });
|
|
7039
|
-
}
|
|
7040
|
-
return [tag, mergeAttributes(HTMLAttributes, attrs, { "data-component": nodeName })];
|
|
7041
|
-
},
|
|
7042
|
-
addNodeView() {
|
|
7043
|
-
return AteNodeViewRenderer(component, {
|
|
7044
|
-
injector,
|
|
7045
|
-
inputs: defaultInputs,
|
|
7046
|
-
wrapperTag: group === "inline" ? "span" : "div",
|
|
7047
|
-
wrapperClass: isTipTapAware
|
|
7048
|
-
? `ate-node-${nodeName}`
|
|
7049
|
-
: `embedded-component embedded-${nodeName}`,
|
|
7050
|
-
atom,
|
|
7051
|
-
editableContent,
|
|
7052
|
-
contentSelector,
|
|
7053
|
-
contentMode,
|
|
7054
|
-
onOutput,
|
|
7055
|
-
ignoreMutation,
|
|
7056
|
-
});
|
|
7057
|
-
},
|
|
7058
|
-
});
|
|
7059
|
-
}
|
|
7060
|
-
|
|
7254
|
+
const ATE_TABLE_BUBBLE_MENU_KEYS = [
|
|
7255
|
+
"addRowBefore",
|
|
7256
|
+
"addRowAfter",
|
|
7257
|
+
"deleteRow",
|
|
7258
|
+
"addColumnBefore",
|
|
7259
|
+
"addColumnAfter",
|
|
7260
|
+
"deleteColumn",
|
|
7261
|
+
"deleteTable",
|
|
7262
|
+
"toggleHeaderRow",
|
|
7263
|
+
"toggleHeaderColumn",
|
|
7264
|
+
"separator",
|
|
7265
|
+
];
|
|
7061
7266
|
/**
|
|
7062
|
-
*
|
|
7063
|
-
*
|
|
7064
|
-
* This function is the single public entry point for adding custom components.
|
|
7065
|
-
* It supports two distinct modes:
|
|
7066
|
-
*
|
|
7067
|
-
* 1. **TipTap-Aware Mode** (the component extends `AteAngularNodeView`):
|
|
7068
|
-
* Grants direct access to the TipTap API (`editor`, `node`, `updateAttributes`, etc.) within the component.
|
|
7069
|
-
*
|
|
7070
|
-
* 2. **Standard Mode** (library or legacy components):
|
|
7071
|
-
* Allows using any existing Angular component. Its `@Input()` properties are automatically
|
|
7072
|
-
* synchronized with TipTap node attributes.
|
|
7073
|
-
*
|
|
7074
|
-
* @param injector - The Angular Injector (obtained via `inject(Injector)`)
|
|
7075
|
-
* @param options - Configuration options for the component extension
|
|
7076
|
-
* @returns A TipTap Node extension ready to be used
|
|
7077
|
-
*
|
|
7078
|
-
* @example
|
|
7079
|
-
* ```typescript
|
|
7080
|
-
* registerAngularComponent(injector, {
|
|
7081
|
-
* component: MyComponent,
|
|
7082
|
-
* name: 'myComponent',
|
|
7083
|
-
* defaultInputs: { color: 'blue' }
|
|
7084
|
-
* });
|
|
7085
|
-
* ```
|
|
7267
|
+
* Clés des options du menu de cellule
|
|
7086
7268
|
*/
|
|
7087
|
-
|
|
7088
|
-
return createAngularComponentExtension(injector, options);
|
|
7089
|
-
}
|
|
7269
|
+
const ATE_CELL_BUBBLE_MENU_KEYS = ["mergeCells", "splitCell"];
|
|
7090
7270
|
|
|
7091
7271
|
/*
|
|
7092
7272
|
* Public API Surface of tiptap-editor
|
|
7093
7273
|
*/
|
|
7094
|
-
// Main component
|
|
7095
|
-
/** @deprecated Renamed to `ATE_INITIAL_EDITOR_STATE`. This alias will be removed in v3.0.0. */
|
|
7096
|
-
const INITIAL_EDITOR_STATE = ATE_INITIAL_EDITOR_STATE;
|
|
7097
|
-
/** @deprecated Renamed to `ATE_SLASH_COMMAND_KEYS`. This alias will be removed in v3.0.0. */
|
|
7098
|
-
const SLASH_COMMAND_KEYS = ATE_SLASH_COMMAND_KEYS;
|
|
7099
|
-
/** @deprecated Renamed to `ATE_DEFAULT_SLASH_COMMANDS_CONFIG`. This alias will be removed in v3.0.0. */
|
|
7100
|
-
const DEFAULT_SLASH_COMMANDS_CONFIG = ATE_DEFAULT_SLASH_COMMANDS_CONFIG;
|
|
7101
|
-
/** @deprecated Renamed to `AteDiscoveryCalculator`. This alias will be removed in v3.0.0. */
|
|
7102
|
-
const DiscoveryCalculator = AteDiscoveryCalculator;
|
|
7103
|
-
/** @deprecated Renamed to `AteImageCalculator`. This alias will be removed in v3.0.0. */
|
|
7104
|
-
const ImageCalculator = AteImageCalculator;
|
|
7105
|
-
/** @deprecated Renamed to `AteMarksCalculator`. This alias will be removed in v3.0.0. */
|
|
7106
|
-
const MarksCalculator = AteMarksCalculator;
|
|
7107
|
-
/** @deprecated Renamed to `AteSelectionCalculator`. This alias will be removed in v3.0.0. */
|
|
7108
|
-
const SelectionCalculator = AteSelectionCalculator;
|
|
7109
|
-
/** @deprecated Renamed to `AteStructureCalculator`. This alias will be removed in v3.0.0. */
|
|
7110
|
-
const StructureCalculator = AteStructureCalculator;
|
|
7111
|
-
/** @deprecated Renamed to `AteTableCalculator`. This alias will be removed in v3.0.0. */
|
|
7112
|
-
const TableCalculator = AteTableCalculator;
|
|
7113
|
-
/** @deprecated Renamed to `ATE_DEFAULT_TOOLBAR_CONFIG`. This alias will be removed in v3.0.0. */
|
|
7114
|
-
const DEFAULT_TOOLBAR_CONFIG = ATE_DEFAULT_TOOLBAR_CONFIG;
|
|
7115
|
-
/** @deprecated Renamed to `ATE_DEFAULT_BUBBLE_MENU_CONFIG`. This alias will be removed in v3.0.0. */
|
|
7116
|
-
const DEFAULT_BUBBLE_MENU_CONFIG = ATE_DEFAULT_BUBBLE_MENU_CONFIG;
|
|
7117
|
-
/** @deprecated Renamed to `ATE_DEFAULT_IMAGE_BUBBLE_MENU_CONFIG`. This alias will be removed in v3.0.0. */
|
|
7118
|
-
const DEFAULT_IMAGE_BUBBLE_MENU_CONFIG = ATE_DEFAULT_IMAGE_BUBBLE_MENU_CONFIG;
|
|
7119
|
-
/** @deprecated Renamed to `ATE_DEFAULT_TABLE_MENU_CONFIG`. This alias will be removed in v3.0.0. */
|
|
7120
|
-
const DEFAULT_TABLE_MENU_CONFIG = ATE_DEFAULT_TABLE_MENU_CONFIG;
|
|
7121
|
-
/** @deprecated Renamed to `ATE_DEFAULT_CELL_MENU_CONFIG`. This alias will be removed in v3.0.0. */
|
|
7122
|
-
const DEFAULT_CELL_MENU_CONFIG = ATE_DEFAULT_CELL_MENU_CONFIG;
|
|
7274
|
+
// Main component & Provider
|
|
7123
7275
|
|
|
7124
7276
|
/**
|
|
7125
7277
|
* Generated bundle index. Do not edit.
|
|
7126
7278
|
*/
|
|
7127
7279
|
|
|
7128
|
-
export { ATE_BUBBLE_MENU_KEYS, ATE_CELL_BUBBLE_MENU_KEYS, ATE_DEFAULT_BUBBLE_MENU_CONFIG, ATE_DEFAULT_CELL_MENU_CONFIG, ATE_DEFAULT_IMAGE_BUBBLE_MENU_CONFIG, ATE_DEFAULT_SLASH_COMMANDS_CONFIG, ATE_DEFAULT_TABLE_MENU_CONFIG, ATE_DEFAULT_TOOLBAR_CONFIG, ATE_IMAGE_BUBBLE_MENU_KEYS, ATE_INITIAL_EDITOR_STATE, ATE_SLASH_COMMAND_KEYS, ATE_TABLE_BUBBLE_MENU_KEYS, ATE_TOOLBAR_KEYS, AngularTiptapEditorComponent, AteAngularNodeView, AteColorPickerService, AteDiscoveryCalculator, AteEditorCommandsService, AteI18nService, AteImageCalculator, AteImageService, AteLinkService, AteMarksCalculator, AteNodeViewRenderer, AteNoopValueAccessorDirective, AteSelectionCalculator, AteStructureCalculator, AteTableCalculator,
|
|
7280
|
+
export { ATE_BUBBLE_MENU_KEYS, ATE_CELL_BUBBLE_MENU_KEYS, ATE_DEFAULT_BUBBLE_MENU_CONFIG, ATE_DEFAULT_CELL_MENU_CONFIG, ATE_DEFAULT_CONFIG, ATE_DEFAULT_IMAGE_BUBBLE_MENU_CONFIG, ATE_DEFAULT_SLASH_COMMANDS_CONFIG, ATE_DEFAULT_TABLE_MENU_CONFIG, ATE_DEFAULT_TOOLBAR_CONFIG, ATE_GLOBAL_CONFIG, ATE_IMAGE_BUBBLE_MENU_KEYS, ATE_INITIAL_EDITOR_STATE, ATE_SLASH_COMMAND_KEYS, ATE_TABLE_BUBBLE_MENU_KEYS, ATE_TOOLBAR_KEYS, AngularTiptapEditorComponent, AteAngularNodeView, AteColorPickerService, AteDiscoveryCalculator, AteEditorCommandsService, AteI18nService, AteImageCalculator, AteImageService, AteLinkService, AteMarksCalculator, AteNodeViewRenderer, AteNoopValueAccessorDirective, AteSelectionCalculator, AteStructureCalculator, AteTableCalculator, createAngularComponentExtension, createDefaultSlashCommands, filterSlashCommands, provideAteEditor, registerAngularComponent };
|
|
7129
7281
|
//# sourceMappingURL=flogeez-angular-tiptap-editor.mjs.map
|