@flogeez/angular-tiptap-editor 2.3.0 → 2.4.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 +14 -0
- package/README.md +132 -93
- package/fesm2022/flogeez-angular-tiptap-editor.mjs +1225 -1114
- package/fesm2022/flogeez-angular-tiptap-editor.mjs.map +1 -1
- package/index.d.ts +265 -154
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
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';
|
|
@@ -20,7 +20,7 @@ import Table from '@tiptap/extension-table';
|
|
|
20
20
|
import TableRow from '@tiptap/extension-table-row';
|
|
21
21
|
import TableCell from '@tiptap/extension-table-cell';
|
|
22
22
|
import TableHeader from '@tiptap/extension-table-header';
|
|
23
|
-
import { isObservable, firstValueFrom, concat, defer, of, tap
|
|
23
|
+
import { isObservable, firstValueFrom, Subscription, concat, defer, of, tap } from 'rxjs';
|
|
24
24
|
import { CommonModule } from '@angular/common';
|
|
25
25
|
import tippy, { sticky } from 'tippy.js';
|
|
26
26
|
import * as i1 from '@angular/forms';
|
|
@@ -5391,407 +5391,851 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
5391
5391
|
}]
|
|
5392
5392
|
}] });
|
|
5393
5393
|
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5394
|
+
/**
|
|
5395
|
+
* Base abstract class for custom 'Angular Nodes'.
|
|
5396
|
+
*
|
|
5397
|
+
* Extend this class in your custom components to automatically receive TipTap editor
|
|
5398
|
+
* properties (node attributes, selection state, etc.) as reactive Signals.
|
|
5399
|
+
*
|
|
5400
|
+
* @example
|
|
5401
|
+
* ```typescript
|
|
5402
|
+
* @Component({
|
|
5403
|
+
* selector: 'app-counter',
|
|
5404
|
+
* template: `
|
|
5405
|
+
* <div>
|
|
5406
|
+
* <button (click)="increment()">Count: {{ node().attrs['count'] }}</button>
|
|
5407
|
+
* </div>
|
|
5408
|
+
* `
|
|
5409
|
+
* })
|
|
5410
|
+
* export class CounterComponent extends AteAngularNodeView {
|
|
5411
|
+
* increment() {
|
|
5412
|
+
* const count = this.node().attrs['count'] || 0;
|
|
5413
|
+
* this.updateAttributes({ count: count + 1 });
|
|
5414
|
+
* }
|
|
5415
|
+
* }
|
|
5416
|
+
* ```
|
|
5417
|
+
*/
|
|
5418
|
+
class AteAngularNodeView {
|
|
5419
|
+
/**
|
|
5420
|
+
* Internal method to initialize the component with NodeView props.
|
|
5421
|
+
* This is called by the AngularNodeViewRenderer.
|
|
5422
|
+
*
|
|
5423
|
+
* @internal
|
|
5424
|
+
*/
|
|
5425
|
+
_initNodeView(props) {
|
|
5426
|
+
// Create signals from the props
|
|
5427
|
+
const editorSignal = signal(props.editor, ...(ngDevMode ? [{ debugName: "editorSignal" }] : []));
|
|
5428
|
+
const nodeSignal = signal(props.node, ...(ngDevMode ? [{ debugName: "nodeSignal" }] : []));
|
|
5429
|
+
const decorationsSignal = signal(props.decorations, ...(ngDevMode ? [{ debugName: "decorationsSignal" }] : []));
|
|
5430
|
+
const selectedSignal = signal(props.selected, ...(ngDevMode ? [{ debugName: "selectedSignal" }] : []));
|
|
5431
|
+
const extensionSignal = signal(props.extension, ...(ngDevMode ? [{ debugName: "extensionSignal" }] : []));
|
|
5432
|
+
// Assign to the component
|
|
5433
|
+
this.editor = editorSignal.asReadonly();
|
|
5434
|
+
this.node = nodeSignal.asReadonly();
|
|
5435
|
+
this.decorations = decorationsSignal.asReadonly();
|
|
5436
|
+
this.selected = selectedSignal.asReadonly();
|
|
5437
|
+
this.extension = extensionSignal.asReadonly();
|
|
5438
|
+
this.getPos = props.getPos;
|
|
5439
|
+
this.updateAttributes = props.updateAttributes;
|
|
5440
|
+
this.deleteNode = props.deleteNode;
|
|
5441
|
+
// Store writable signals for updates
|
|
5442
|
+
this._writableSignals = {
|
|
5443
|
+
node: nodeSignal,
|
|
5444
|
+
decorations: decorationsSignal,
|
|
5445
|
+
selected: selectedSignal,
|
|
5446
|
+
};
|
|
5400
5447
|
}
|
|
5401
|
-
|
|
5402
|
-
|
|
5448
|
+
/**
|
|
5449
|
+
* Internal method to update the component when the node changes.
|
|
5450
|
+
* This is called by the AngularNodeViewRenderer.
|
|
5451
|
+
*
|
|
5452
|
+
* @internal
|
|
5453
|
+
*/
|
|
5454
|
+
_updateNodeView(node, decorations) {
|
|
5455
|
+
if (this._writableSignals) {
|
|
5456
|
+
this._writableSignals.node.set(node);
|
|
5457
|
+
this._writableSignals.decorations.set(decorations);
|
|
5458
|
+
}
|
|
5403
5459
|
}
|
|
5404
|
-
|
|
5405
|
-
|
|
5460
|
+
/**
|
|
5461
|
+
* Internal method to update the selection state.
|
|
5462
|
+
* This is called by the AngularNodeViewRenderer.
|
|
5463
|
+
*
|
|
5464
|
+
* @internal
|
|
5465
|
+
*/
|
|
5466
|
+
_selectNodeView() {
|
|
5467
|
+
if (this._writableSignals) {
|
|
5468
|
+
this._writableSignals.selected.set(true);
|
|
5469
|
+
}
|
|
5406
5470
|
}
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5471
|
+
/**
|
|
5472
|
+
* Internal method to update the deselection state.
|
|
5473
|
+
* This is called by the AngularNodeViewRenderer.
|
|
5474
|
+
*
|
|
5475
|
+
* @internal
|
|
5476
|
+
*/
|
|
5477
|
+
_deselectNodeView() {
|
|
5478
|
+
if (this._writableSignals) {
|
|
5479
|
+
this._writableSignals.selected.set(false);
|
|
5480
|
+
}
|
|
5410
5481
|
}
|
|
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
|
-
};
|
|
5482
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AteAngularNodeView, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
5483
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.16", type: AteAngularNodeView, isStandalone: true, ngImport: i0 }); }
|
|
5484
|
+
}
|
|
5485
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AteAngularNodeView, decorators: [{
|
|
5486
|
+
type: Directive
|
|
5487
|
+
}] });
|
|
5426
5488
|
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5489
|
+
/**
|
|
5490
|
+
* Universal Renderer for Angular Components as TipTap NodeViews.
|
|
5491
|
+
*
|
|
5492
|
+
* Supports:
|
|
5493
|
+
* - TipTap-Aware components (extending AteAngularNodeView)
|
|
5494
|
+
* - Standard library components (automatic @Input() sync)
|
|
5495
|
+
* - Signal-based inputs and outputs (Angular 18+)
|
|
5496
|
+
* - Content projection (editableContent)
|
|
5497
|
+
* - Unified lifecycle and event management
|
|
5498
|
+
*/
|
|
5499
|
+
function AteNodeViewRenderer(component, options) {
|
|
5500
|
+
return props => {
|
|
5501
|
+
const { node, view: _view, getPos, decorations, editor, extension } = props;
|
|
5502
|
+
const { injector, inputs = {}, wrapperTag = "div", wrapperClass, editableContent = false, contentSelector, onOutput, ignoreMutation = true, } = options;
|
|
5503
|
+
const dom = document.createElement(wrapperTag);
|
|
5504
|
+
if (wrapperClass) {
|
|
5505
|
+
dom.className = wrapperClass;
|
|
5440
5506
|
}
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5507
|
+
const applicationRef = injector.get(ApplicationRef);
|
|
5508
|
+
const environmentInjector = injector.get(EnvironmentInjector);
|
|
5509
|
+
// 1. Setup Content Projection (ng-content)
|
|
5510
|
+
let initialNodes = [];
|
|
5511
|
+
let contentDOM = null;
|
|
5512
|
+
if (editableContent && !contentSelector) {
|
|
5513
|
+
contentDOM = document.createElement("div");
|
|
5514
|
+
contentDOM.setAttribute("data-node-view-content", "");
|
|
5515
|
+
initialNodes = [[contentDOM]];
|
|
5445
5516
|
}
|
|
5446
|
-
|
|
5447
|
-
|
|
5517
|
+
// 2. Create the Angular Component
|
|
5518
|
+
const componentRef = createComponent(component, {
|
|
5519
|
+
environmentInjector,
|
|
5520
|
+
elementInjector: injector,
|
|
5521
|
+
projectableNodes: initialNodes,
|
|
5522
|
+
});
|
|
5523
|
+
const instance = componentRef.instance;
|
|
5524
|
+
const subscriptions = [];
|
|
5525
|
+
// 3. Initialize TipTap-Aware instances
|
|
5526
|
+
if (instance instanceof AteAngularNodeView) {
|
|
5527
|
+
instance._initNodeView({
|
|
5528
|
+
editor,
|
|
5529
|
+
node,
|
|
5530
|
+
decorations,
|
|
5531
|
+
selected: false,
|
|
5532
|
+
extension,
|
|
5533
|
+
getPos,
|
|
5534
|
+
updateAttributes: attrs => editor.commands.updateAttributes(node.type.name, attrs),
|
|
5535
|
+
deleteNode: () => {
|
|
5536
|
+
const pos = getPos();
|
|
5537
|
+
if (pos !== undefined) {
|
|
5538
|
+
editor.commands.deleteRange({ from: pos, to: pos + node.nodeSize });
|
|
5539
|
+
}
|
|
5540
|
+
},
|
|
5541
|
+
});
|
|
5448
5542
|
}
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5543
|
+
// 4. Synchronize Inputs (Handles standard @Input and Signal-based inputs)
|
|
5544
|
+
const syncInputs = (nodeToSync) => {
|
|
5545
|
+
// Combine base inputs from options with dynamic node attributes
|
|
5546
|
+
const mergedInputs = { ...inputs, ...nodeToSync.attrs };
|
|
5547
|
+
Object.entries(mergedInputs).forEach(([key, value]) => {
|
|
5548
|
+
if (key !== "id" && value !== null && value !== undefined) {
|
|
5549
|
+
try {
|
|
5550
|
+
componentRef.setInput(key, value);
|
|
5551
|
+
}
|
|
5552
|
+
catch {
|
|
5553
|
+
// Silently ignore inputs that don't exist on the component
|
|
5554
|
+
}
|
|
5555
|
+
}
|
|
5556
|
+
});
|
|
5557
|
+
};
|
|
5558
|
+
syncInputs(node);
|
|
5559
|
+
// 5. Setup Outputs (Handles EventEmitter and OutputRef)
|
|
5560
|
+
if (onOutput) {
|
|
5561
|
+
Object.entries(instance).forEach(([key, potentialOutput]) => {
|
|
5562
|
+
if (potentialOutput &&
|
|
5563
|
+
typeof potentialOutput
|
|
5564
|
+
.subscribe === "function") {
|
|
5565
|
+
const sub = potentialOutput.subscribe((value) => {
|
|
5566
|
+
onOutput(key, value);
|
|
5567
|
+
});
|
|
5568
|
+
if (sub instanceof Subscription) {
|
|
5569
|
+
subscriptions.push(sub);
|
|
5570
|
+
}
|
|
5571
|
+
}
|
|
5572
|
+
});
|
|
5456
5573
|
}
|
|
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) {
|
|
5574
|
+
// 6. Attach to DOM and ApplicationRef
|
|
5575
|
+
applicationRef.attachView(componentRef.hostView);
|
|
5576
|
+
const componentElement = componentRef.location.nativeElement;
|
|
5577
|
+
// Target specific element for content if selector provided
|
|
5578
|
+
if (editableContent && contentSelector) {
|
|
5579
|
+
contentDOM = componentElement.querySelector(contentSelector);
|
|
5580
|
+
}
|
|
5581
|
+
dom.appendChild(componentElement);
|
|
5582
|
+
// Initial detection to ensure the component is rendered
|
|
5583
|
+
componentRef.changeDetectorRef.detectChanges();
|
|
5584
|
+
// 7. Return the TipTap NodeView Interface
|
|
5504
5585
|
return {
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5586
|
+
dom,
|
|
5587
|
+
contentDOM,
|
|
5588
|
+
update: (updatedNode, updatedDecorations) => {
|
|
5589
|
+
if (updatedNode.type !== node.type) {
|
|
5590
|
+
return false;
|
|
5591
|
+
}
|
|
5592
|
+
// Update Aware component signals
|
|
5593
|
+
if (instance instanceof AteAngularNodeView) {
|
|
5594
|
+
instance._updateNodeView(updatedNode, updatedDecorations);
|
|
5595
|
+
}
|
|
5596
|
+
// Update inputs
|
|
5597
|
+
syncInputs(updatedNode);
|
|
5598
|
+
// Notify and Detect changes
|
|
5599
|
+
componentRef.changeDetectorRef.detectChanges();
|
|
5600
|
+
if (options.onUpdate) {
|
|
5601
|
+
options.onUpdate(updatedNode);
|
|
5602
|
+
}
|
|
5603
|
+
return true;
|
|
5604
|
+
},
|
|
5605
|
+
selectNode: () => {
|
|
5606
|
+
if (instance instanceof AteAngularNodeView) {
|
|
5607
|
+
instance._selectNodeView();
|
|
5608
|
+
}
|
|
5609
|
+
dom.classList.add("ProseMirror-selectednode");
|
|
5610
|
+
},
|
|
5611
|
+
deselectNode: () => {
|
|
5612
|
+
if (instance instanceof AteAngularNodeView) {
|
|
5613
|
+
instance._deselectNodeView();
|
|
5614
|
+
}
|
|
5615
|
+
dom.classList.remove("ProseMirror-selectednode");
|
|
5616
|
+
},
|
|
5617
|
+
destroy: () => {
|
|
5618
|
+
if (options.onDestroy) {
|
|
5619
|
+
options.onDestroy();
|
|
5620
|
+
}
|
|
5621
|
+
subscriptions.forEach(s => s.unsubscribe());
|
|
5622
|
+
applicationRef.detachView(componentRef.hostView);
|
|
5623
|
+
componentRef.destroy();
|
|
5624
|
+
},
|
|
5625
|
+
stopEvent: event => {
|
|
5626
|
+
const target = event.target;
|
|
5627
|
+
const isEditable = target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable;
|
|
5628
|
+
return isEditable;
|
|
5629
|
+
},
|
|
5630
|
+
ignoreMutation: mutation => {
|
|
5631
|
+
if (typeof ignoreMutation === "function") {
|
|
5632
|
+
return ignoreMutation(mutation);
|
|
5633
|
+
}
|
|
5634
|
+
return ignoreMutation;
|
|
5510
5635
|
},
|
|
5511
5636
|
};
|
|
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
5637
|
};
|
|
5536
|
-
}
|
|
5638
|
+
}
|
|
5537
5639
|
|
|
5538
|
-
const
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5640
|
+
const RESERVED_NAMES = [
|
|
5641
|
+
"doc",
|
|
5642
|
+
"paragraph",
|
|
5643
|
+
"text",
|
|
5644
|
+
"hardBreak",
|
|
5645
|
+
"bulletList",
|
|
5646
|
+
"orderedList",
|
|
5647
|
+
"listItem",
|
|
5648
|
+
"blockquote",
|
|
5649
|
+
"codeBlock",
|
|
5650
|
+
"heading",
|
|
5651
|
+
"horizontalRule",
|
|
5652
|
+
];
|
|
5653
|
+
/**
|
|
5654
|
+
* Derives the TipTap node name and HTML tag from a component class.
|
|
5655
|
+
*/
|
|
5656
|
+
function deriveMetadata(component, customName) {
|
|
5657
|
+
let nodeName = customName;
|
|
5658
|
+
if (!nodeName) {
|
|
5659
|
+
nodeName = component.name
|
|
5660
|
+
.replace(/Component$/, "")
|
|
5661
|
+
.replace(/Node$/, "")
|
|
5662
|
+
.replace(/^([A-Z])/, m => m.toLowerCase());
|
|
5663
|
+
console.warn(`[ATE] Auto-deriving node name '${nodeName}' for component ${component.name}. ` +
|
|
5664
|
+
`Provide an explicit 'name' in options to avoid potential naming collisions.`);
|
|
5665
|
+
}
|
|
5666
|
+
if (RESERVED_NAMES.includes(nodeName)) {
|
|
5667
|
+
throw new Error(`[ATE] The name '${nodeName}' is a reserved TipTap node name. ` +
|
|
5668
|
+
`Please provide a unique name for your custom component.`);
|
|
5669
|
+
}
|
|
5670
|
+
const tag = nodeName.toLowerCase().replace(/([A-Z])/g, "-$1");
|
|
5671
|
+
return { nodeName, tag };
|
|
5672
|
+
}
|
|
5673
|
+
/**
|
|
5674
|
+
* Creates TipTap attributes from component inputs (Standard Mode).
|
|
5675
|
+
*/
|
|
5676
|
+
function createStandardAttributes(defaultInputs = {}) {
|
|
5677
|
+
const tiptapAttributes = {};
|
|
5678
|
+
Object.entries(defaultInputs).forEach(([key, defaultValue]) => {
|
|
5679
|
+
tiptapAttributes[key] = {
|
|
5680
|
+
default: defaultValue,
|
|
5681
|
+
parseHTML: (element) => {
|
|
5682
|
+
const attr = element.getAttribute(`data-${key}`);
|
|
5683
|
+
if (attr === null) {
|
|
5684
|
+
return defaultValue;
|
|
5685
|
+
}
|
|
5686
|
+
try {
|
|
5687
|
+
return JSON.parse(attr);
|
|
5688
|
+
}
|
|
5689
|
+
catch {
|
|
5690
|
+
return attr;
|
|
5691
|
+
}
|
|
5692
|
+
},
|
|
5693
|
+
renderHTML: (attrs) => {
|
|
5694
|
+
const value = attrs[key];
|
|
5695
|
+
if (value === undefined || value === null) {
|
|
5696
|
+
return {};
|
|
5697
|
+
}
|
|
5698
|
+
const serialized = typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
5699
|
+
return { [`data-${key}`]: serialized };
|
|
5700
|
+
},
|
|
5701
|
+
};
|
|
5702
|
+
});
|
|
5703
|
+
return tiptapAttributes;
|
|
5704
|
+
}
|
|
5705
|
+
/**
|
|
5706
|
+
* Factory to transform an Angular component into a TipTap Node extension.
|
|
5707
|
+
* Supports both "TipTap-Aware" and "Standard" modes.
|
|
5708
|
+
*
|
|
5709
|
+
* @internal
|
|
5710
|
+
*/
|
|
5711
|
+
function createAngularComponentExtension(injector, options) {
|
|
5712
|
+
const { component, name: customName, attributes, defaultInputs, contentSelector, contentMode = "block", editableContent = false, group = "block", draggable = true, ignoreMutation = true, onOutput, HTMLAttributes = {}, parseHTML: customParseHTML, renderHTML: customRenderHTML, } = options;
|
|
5713
|
+
const { nodeName, tag } = deriveMetadata(component, customName);
|
|
5714
|
+
const isTipTapAware = Object.prototype.isPrototypeOf.call(AteAngularNodeView, component);
|
|
5715
|
+
const atom = !editableContent;
|
|
5716
|
+
// 1. Prepare Attributes
|
|
5717
|
+
const tiptapAttributes = isTipTapAware
|
|
5718
|
+
? attributes || {}
|
|
5719
|
+
: createStandardAttributes(defaultInputs);
|
|
5720
|
+
// 2. Create Node Extension
|
|
5721
|
+
return Node$1.create({
|
|
5722
|
+
name: nodeName,
|
|
5723
|
+
group,
|
|
5724
|
+
inline: group === "inline",
|
|
5725
|
+
atom,
|
|
5726
|
+
draggable,
|
|
5727
|
+
content: editableContent ? (contentMode === "inline" ? "inline*" : "block*") : undefined,
|
|
5728
|
+
addAttributes() {
|
|
5729
|
+
return tiptapAttributes;
|
|
5542
5730
|
},
|
|
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" }),
|
|
5731
|
+
parseHTML() {
|
|
5732
|
+
if (customParseHTML) {
|
|
5733
|
+
return customParseHTML.call(this);
|
|
5734
|
+
}
|
|
5735
|
+
return [{ tag }, { tag: `div[data-component="${nodeName}"]` }];
|
|
5560
5736
|
},
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5737
|
+
renderHTML({ node, HTMLAttributes: attrs }) {
|
|
5738
|
+
if (customRenderHTML) {
|
|
5739
|
+
return customRenderHTML.call(this, { node, HTMLAttributes: attrs });
|
|
5740
|
+
}
|
|
5741
|
+
return [tag, mergeAttributes(HTMLAttributes, attrs, { "data-component": nodeName })];
|
|
5742
|
+
},
|
|
5743
|
+
addNodeView() {
|
|
5744
|
+
return AteNodeViewRenderer(component, {
|
|
5745
|
+
injector,
|
|
5746
|
+
inputs: defaultInputs,
|
|
5747
|
+
wrapperTag: group === "inline" ? "span" : "div",
|
|
5748
|
+
wrapperClass: isTipTapAware
|
|
5749
|
+
? `ate-node-${nodeName}`
|
|
5750
|
+
: `embedded-component embedded-${nodeName}`,
|
|
5751
|
+
atom,
|
|
5752
|
+
editableContent,
|
|
5753
|
+
contentSelector,
|
|
5754
|
+
contentMode,
|
|
5755
|
+
onOutput,
|
|
5756
|
+
ignoreMutation,
|
|
5757
|
+
});
|
|
5576
5758
|
},
|
|
5577
|
-
};
|
|
5578
|
-
};
|
|
5579
|
-
|
|
5580
|
-
/**
|
|
5581
|
-
* DiscoveryCalculator automatically detects and tracks the state of any TipTap extension.
|
|
5582
|
-
* It provides a "fallback" reactive state for any mark or node not explicitly handled
|
|
5583
|
-
* by specialized calculators.
|
|
5584
|
-
*/
|
|
5585
|
-
const AteDiscoveryCalculator = (editor) => {
|
|
5586
|
-
const state = {
|
|
5587
|
-
marks: {},
|
|
5588
|
-
nodes: {},
|
|
5589
|
-
};
|
|
5590
|
-
// We skip core extensions that are already handled by specialized calculators
|
|
5591
|
-
// to avoid redundant calculations and maintain precise attribute tracking.
|
|
5592
|
-
const handled = [
|
|
5593
|
-
"bold",
|
|
5594
|
-
"italic",
|
|
5595
|
-
"underline",
|
|
5596
|
-
"strike",
|
|
5597
|
-
"code",
|
|
5598
|
-
"link",
|
|
5599
|
-
"highlight",
|
|
5600
|
-
"superscript",
|
|
5601
|
-
"subscript",
|
|
5602
|
-
"table",
|
|
5603
|
-
"image",
|
|
5604
|
-
"resizableImage",
|
|
5605
|
-
"heading",
|
|
5606
|
-
"bulletList",
|
|
5607
|
-
"orderedList",
|
|
5608
|
-
"blockquote",
|
|
5609
|
-
"textAlign",
|
|
5610
|
-
"textStyle",
|
|
5611
|
-
"color",
|
|
5612
|
-
];
|
|
5613
|
-
editor.extensionManager.extensions.forEach(extension => {
|
|
5614
|
-
const name = extension.name;
|
|
5615
|
-
const type = extension.type;
|
|
5616
|
-
// Skip internal/core extensions or already handled ones
|
|
5617
|
-
if ([
|
|
5618
|
-
"selection",
|
|
5619
|
-
"editable",
|
|
5620
|
-
"focus",
|
|
5621
|
-
"undo",
|
|
5622
|
-
"redo",
|
|
5623
|
-
"history",
|
|
5624
|
-
"placeholder",
|
|
5625
|
-
"characterCount",
|
|
5626
|
-
].includes(name)) {
|
|
5627
|
-
return;
|
|
5628
|
-
}
|
|
5629
|
-
if (handled.includes(name)) {
|
|
5630
|
-
return;
|
|
5631
|
-
}
|
|
5632
|
-
if (type === "mark") {
|
|
5633
|
-
state.marks[name] = editor.isActive(name);
|
|
5634
|
-
}
|
|
5635
|
-
else if (type === "node") {
|
|
5636
|
-
state.nodes[name] = editor.isActive(name);
|
|
5637
|
-
}
|
|
5638
5759
|
});
|
|
5639
|
-
|
|
5640
|
-
};
|
|
5760
|
+
}
|
|
5641
5761
|
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
return false;
|
|
5658
|
-
}
|
|
5659
|
-
const attrs = getAttributes(view.state, "link");
|
|
5660
|
-
if (attrs["href"]) {
|
|
5661
|
-
window.open(attrs["href"], "_blank", "noopener,noreferrer");
|
|
5662
|
-
return true;
|
|
5663
|
-
}
|
|
5664
|
-
return false;
|
|
5665
|
-
},
|
|
5666
|
-
},
|
|
5667
|
-
}),
|
|
5668
|
-
];
|
|
5669
|
-
},
|
|
5670
|
-
});
|
|
5762
|
+
/**
|
|
5763
|
+
* Internal registry to store the root injector for the editor.
|
|
5764
|
+
* This allows registerAngularComponent to work without explicitly passing the injector.
|
|
5765
|
+
* @internal
|
|
5766
|
+
*/
|
|
5767
|
+
let globalInjector = null;
|
|
5768
|
+
function setGlobalInjector(injector) {
|
|
5769
|
+
globalInjector = injector;
|
|
5770
|
+
}
|
|
5771
|
+
function getGlobalInjector() {
|
|
5772
|
+
if (!globalInjector) {
|
|
5773
|
+
throw new Error("[ATE] Global Injector not found. Make sure to call provideAteEditor() in your app.config.ts or main.ts.");
|
|
5774
|
+
}
|
|
5775
|
+
return globalInjector;
|
|
5776
|
+
}
|
|
5671
5777
|
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
};
|
|
5719
|
-
// Default image bubble menu configuration
|
|
5720
|
-
const ATE_DEFAULT_IMAGE_BUBBLE_MENU_CONFIG = {
|
|
5721
|
-
changeImage: true,
|
|
5722
|
-
resizeSmall: true,
|
|
5723
|
-
resizeMedium: true,
|
|
5724
|
-
resizeLarge: true,
|
|
5725
|
-
resizeOriginal: true,
|
|
5726
|
-
deleteImage: true,
|
|
5727
|
-
separator: true,
|
|
5728
|
-
};
|
|
5729
|
-
// Default table bubble menu configuration
|
|
5730
|
-
const ATE_DEFAULT_TABLE_MENU_CONFIG = {
|
|
5731
|
-
addRowBefore: true,
|
|
5732
|
-
addRowAfter: true,
|
|
5733
|
-
deleteRow: true,
|
|
5734
|
-
addColumnBefore: true,
|
|
5735
|
-
addColumnAfter: true,
|
|
5736
|
-
deleteColumn: true,
|
|
5737
|
-
toggleHeaderRow: true,
|
|
5738
|
-
toggleHeaderColumn: true,
|
|
5739
|
-
deleteTable: true,
|
|
5740
|
-
separator: true,
|
|
5741
|
-
};
|
|
5742
|
-
// Default cell bubble menu configuration
|
|
5743
|
-
const ATE_DEFAULT_CELL_MENU_CONFIG = {
|
|
5744
|
-
mergeCells: true,
|
|
5745
|
-
splitCell: true,
|
|
5746
|
-
};
|
|
5778
|
+
/**
|
|
5779
|
+
* Registers ANY Angular component as a TipTap node extension.
|
|
5780
|
+
*
|
|
5781
|
+
* This function is the single public entry point for adding custom components.
|
|
5782
|
+
* It supports two distinct modes:
|
|
5783
|
+
*
|
|
5784
|
+
* 1. **TipTap-Aware Mode** (the component extends `AteAngularNodeView`):
|
|
5785
|
+
* Grants direct access to the TipTap API (`editor`, `node`, `updateAttributes`, etc.) within the component.
|
|
5786
|
+
*
|
|
5787
|
+
* 2. **Standard Mode** (library or legacy components):
|
|
5788
|
+
* Allows using any existing Angular component. Its `@Input()` properties are automatically
|
|
5789
|
+
* synchronized with TipTap node attributes.
|
|
5790
|
+
*
|
|
5791
|
+
* @param injectorOrOptions - The Angular Injector OR the configuration options
|
|
5792
|
+
* @param maybeOptions - Configuration options (if the first param was an injector)
|
|
5793
|
+
* @returns A TipTap Node extension ready to be used
|
|
5794
|
+
*
|
|
5795
|
+
* @example
|
|
5796
|
+
* ```typescript
|
|
5797
|
+
* // Modern way (requires provideAteEditor())
|
|
5798
|
+
* registerAngularComponent({
|
|
5799
|
+
* component: MyComponent,
|
|
5800
|
+
* name: 'myComponent'
|
|
5801
|
+
* });
|
|
5802
|
+
*
|
|
5803
|
+
* // Classic way
|
|
5804
|
+
* registerAngularComponent(injector, {
|
|
5805
|
+
* component: MyComponent
|
|
5806
|
+
* });
|
|
5807
|
+
* ```
|
|
5808
|
+
*/
|
|
5809
|
+
function registerAngularComponent(injectorOrOptions, maybeOptions) {
|
|
5810
|
+
let injector;
|
|
5811
|
+
let options;
|
|
5812
|
+
// Duck-typing check: Injectors have a 'get' method
|
|
5813
|
+
const isInjector = (obj) => obj !== null && typeof obj === "object" && typeof obj.get === "function";
|
|
5814
|
+
if (isInjector(injectorOrOptions)) {
|
|
5815
|
+
injector = injectorOrOptions;
|
|
5816
|
+
options = maybeOptions;
|
|
5817
|
+
}
|
|
5818
|
+
else {
|
|
5819
|
+
injector = getGlobalInjector();
|
|
5820
|
+
options = injectorOrOptions;
|
|
5821
|
+
}
|
|
5822
|
+
return createAngularComponentExtension(injector, options);
|
|
5823
|
+
}
|
|
5747
5824
|
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5825
|
+
/**
|
|
5826
|
+
* Injection Token for global editor configuration.
|
|
5827
|
+
*/
|
|
5828
|
+
const ATE_GLOBAL_CONFIG = new InjectionToken("ATE_GLOBAL_CONFIG");
|
|
5829
|
+
|
|
5830
|
+
const AteSelectionCalculator = editor => {
|
|
5831
|
+
const { selection } = editor.state;
|
|
5832
|
+
const { from, to } = selection;
|
|
5833
|
+
let selectionType = "none";
|
|
5834
|
+
if (selection instanceof TextSelection) {
|
|
5835
|
+
selectionType = "text";
|
|
5755
5836
|
}
|
|
5756
|
-
|
|
5757
|
-
|
|
5837
|
+
else if (selection instanceof NodeSelection) {
|
|
5838
|
+
selectionType = "node";
|
|
5758
5839
|
}
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5840
|
+
else if (selection instanceof CellSelection) {
|
|
5841
|
+
selectionType = "cell";
|
|
5842
|
+
}
|
|
5843
|
+
let isSingleCell = false;
|
|
5844
|
+
if (selection instanceof CellSelection) {
|
|
5845
|
+
isSingleCell = selection.$anchorCell.pos === selection.$headCell.pos;
|
|
5846
|
+
}
|
|
5847
|
+
return {
|
|
5848
|
+
isFocused: editor.view.hasFocus(),
|
|
5849
|
+
isEditable: editor.isEditable,
|
|
5850
|
+
selection: {
|
|
5851
|
+
type: selectionType,
|
|
5852
|
+
from,
|
|
5853
|
+
to,
|
|
5854
|
+
empty: selection.empty || (selectionType === "text" && from === to),
|
|
5855
|
+
isSingleCell: isSingleCell,
|
|
5856
|
+
},
|
|
5857
|
+
nodes: {
|
|
5858
|
+
activeNodeName: editor.state.doc.nodeAt(editor.state.selection.$head.pos)?.type.name || null,
|
|
5859
|
+
},
|
|
5860
|
+
};
|
|
5861
|
+
};
|
|
5862
|
+
|
|
5863
|
+
const AteMarksCalculator = editor => {
|
|
5864
|
+
const isCodeBlock = editor.isActive("codeBlock");
|
|
5865
|
+
const isCode = editor.isActive("code"); // Inline code mark
|
|
5866
|
+
const isImage = editor.isActive("image") || editor.isActive("resizableImage");
|
|
5867
|
+
// Check if marks are generally allowed based on context
|
|
5868
|
+
const marksAllowed = !isCodeBlock && !isImage;
|
|
5869
|
+
// For inline code specifically, we don't allow nesting OTHER marks inside it,
|
|
5870
|
+
// but the code mark ITSELF must be togglable to be removed.
|
|
5871
|
+
const isInsideInlineCode = isCode;
|
|
5872
|
+
// 1. Resolve target element once for this calculation
|
|
5873
|
+
const getTargetElement = () => {
|
|
5874
|
+
if (typeof window === "undefined" || !editor.view?.dom) {
|
|
5875
|
+
return null;
|
|
5876
|
+
}
|
|
5877
|
+
try {
|
|
5878
|
+
const { from } = editor.state.selection;
|
|
5879
|
+
const { node } = editor.view.domAtPos(from);
|
|
5880
|
+
return node.nodeType === Node.TEXT_NODE ? node.parentElement : node;
|
|
5881
|
+
}
|
|
5882
|
+
catch (_e) {
|
|
5883
|
+
return null;
|
|
5884
|
+
}
|
|
5885
|
+
};
|
|
5886
|
+
const targetEl = getTargetElement() || (typeof window !== "undefined" ? editor.view?.dom : null);
|
|
5887
|
+
const computedStyle = targetEl && typeof window !== "undefined" ? window.getComputedStyle(targetEl) : null;
|
|
5888
|
+
// 2. Lightweight helper to extract properties from the pre-calculated style object
|
|
5889
|
+
const getStyle = (prop) => {
|
|
5890
|
+
if (!computedStyle) {
|
|
5891
|
+
return null;
|
|
5892
|
+
}
|
|
5893
|
+
const val = computedStyle.getPropertyValue(prop);
|
|
5894
|
+
return normalizeColor(val);
|
|
5895
|
+
};
|
|
5896
|
+
const colorMark = editor.getAttributes("textStyle")["color"] || null;
|
|
5897
|
+
const backgroundMark = editor.getAttributes("highlight")["color"] || null;
|
|
5898
|
+
return {
|
|
5899
|
+
marks: {
|
|
5900
|
+
bold: editor.isActive("bold"),
|
|
5901
|
+
italic: editor.isActive("italic"),
|
|
5902
|
+
underline: editor.isActive("underline"),
|
|
5903
|
+
strike: editor.isActive("strike"),
|
|
5904
|
+
code: isCode,
|
|
5905
|
+
superscript: editor.isActive("superscript"),
|
|
5906
|
+
subscript: editor.isActive("subscript"),
|
|
5907
|
+
highlight: editor.isActive("highlight"),
|
|
5908
|
+
link: editor.isActive("link"),
|
|
5909
|
+
linkHref: editor.getAttributes("link")["href"] || null,
|
|
5910
|
+
color: colorMark,
|
|
5911
|
+
computedColor: colorMark || getStyle("color"),
|
|
5912
|
+
background: backgroundMark,
|
|
5913
|
+
computedBackground: backgroundMark || getStyle("background-color"),
|
|
5914
|
+
linkOpenOnClick: editor.extensionManager.extensions.find(ext => ext.name === "link")?.options?.openOnClick ??
|
|
5915
|
+
false,
|
|
5916
|
+
},
|
|
5917
|
+
can: {
|
|
5918
|
+
toggleBold: marksAllowed && !isInsideInlineCode && editor.can().toggleBold(),
|
|
5919
|
+
toggleItalic: marksAllowed && !isInsideInlineCode && editor.can().toggleItalic(),
|
|
5920
|
+
toggleUnderline: marksAllowed && !isInsideInlineCode && editor.can().toggleUnderline(),
|
|
5921
|
+
toggleStrike: marksAllowed && !isInsideInlineCode && editor.can().toggleStrike(),
|
|
5922
|
+
toggleCode: marksAllowed && editor.can().toggleCode(),
|
|
5923
|
+
toggleHighlight: marksAllowed && !isInsideInlineCode && editor.can().toggleHighlight(),
|
|
5924
|
+
toggleLink: marksAllowed &&
|
|
5925
|
+
!isInsideInlineCode &&
|
|
5926
|
+
(editor.can().setLink({ href: "" }) || editor.can().unsetLink()),
|
|
5927
|
+
toggleSuperscript: marksAllowed && !isInsideInlineCode && editor.can().toggleSuperscript(),
|
|
5928
|
+
toggleSubscript: marksAllowed && !isInsideInlineCode && editor.can().toggleSubscript(),
|
|
5929
|
+
setColor: marksAllowed && !isInsideInlineCode && editor.can().setColor(""),
|
|
5930
|
+
setHighlight: marksAllowed && !isInsideInlineCode && editor.can().setHighlight(),
|
|
5931
|
+
undo: editor.can().undo(),
|
|
5932
|
+
redo: editor.can().redo(),
|
|
5933
|
+
},
|
|
5934
|
+
};
|
|
5935
|
+
};
|
|
5936
|
+
|
|
5937
|
+
const AteTableCalculator = editor => {
|
|
5938
|
+
const isTable = editor.isActive("table");
|
|
5939
|
+
if (!isTable) {
|
|
5940
|
+
return {
|
|
5941
|
+
nodes: {
|
|
5942
|
+
isTable: false,
|
|
5943
|
+
isTableCell: false,
|
|
5944
|
+
isTableHeaderRow: false,
|
|
5945
|
+
isTableHeaderColumn: false,
|
|
5946
|
+
},
|
|
5947
|
+
};
|
|
5948
|
+
}
|
|
5949
|
+
const { selection } = editor.state;
|
|
5950
|
+
return {
|
|
5951
|
+
nodes: {
|
|
5952
|
+
isTable: true,
|
|
5953
|
+
isTableNodeSelected: selection instanceof NodeSelection && selection.node.type.name === "table",
|
|
5954
|
+
isTableCell: editor.isActive("tableCell") || editor.isActive("tableHeader"),
|
|
5955
|
+
isTableHeaderRow: editor.isActive("tableHeader", { row: true }),
|
|
5956
|
+
isTableHeaderColumn: editor.isActive("tableHeader", { column: true }),
|
|
5957
|
+
},
|
|
5958
|
+
can: {
|
|
5959
|
+
addRowBefore: editor.can().addRowBefore(),
|
|
5960
|
+
addRowAfter: editor.can().addRowAfter(),
|
|
5961
|
+
deleteRow: editor.can().deleteRow(),
|
|
5962
|
+
addColumnBefore: editor.can().addColumnBefore(),
|
|
5963
|
+
addColumnAfter: editor.can().addColumnAfter(),
|
|
5964
|
+
deleteColumn: editor.can().deleteColumn(),
|
|
5965
|
+
deleteTable: editor.can().deleteTable(),
|
|
5966
|
+
mergeCells: editor.can().mergeCells(),
|
|
5967
|
+
splitCell: editor.can().splitCell(),
|
|
5968
|
+
toggleHeaderRow: editor.can().toggleHeaderRow(),
|
|
5969
|
+
toggleHeaderColumn: editor.can().toggleHeaderColumn(),
|
|
5970
|
+
},
|
|
5971
|
+
};
|
|
5972
|
+
};
|
|
5973
|
+
|
|
5974
|
+
const AteImageCalculator = editor => {
|
|
5975
|
+
return {
|
|
5976
|
+
nodes: {
|
|
5977
|
+
isImage: editor.isActive("image") || editor.isActive("resizableImage"),
|
|
5978
|
+
},
|
|
5979
|
+
};
|
|
5980
|
+
};
|
|
5981
|
+
|
|
5982
|
+
const AteStructureCalculator = editor => {
|
|
5983
|
+
return {
|
|
5984
|
+
nodes: {
|
|
5985
|
+
isBlockquote: editor.isActive("blockquote"),
|
|
5986
|
+
isCodeBlock: editor.isActive("codeBlock"),
|
|
5987
|
+
isBulletList: editor.isActive("bulletList"),
|
|
5988
|
+
isOrderedList: editor.isActive("orderedList"),
|
|
5989
|
+
h1: editor.isActive("heading", { level: 1 }),
|
|
5990
|
+
h2: editor.isActive("heading", { level: 2 }),
|
|
5991
|
+
h3: editor.isActive("heading", { level: 3 }),
|
|
5992
|
+
alignLeft: editor.isActive({ textAlign: "left" }),
|
|
5993
|
+
alignCenter: editor.isActive({ textAlign: "center" }),
|
|
5994
|
+
alignRight: editor.isActive({ textAlign: "right" }),
|
|
5995
|
+
alignJustify: editor.isActive({ textAlign: "justify" }),
|
|
5996
|
+
},
|
|
5997
|
+
can: {
|
|
5998
|
+
toggleHeading1: editor.can().toggleHeading({ level: 1 }),
|
|
5999
|
+
toggleHeading2: editor.can().toggleHeading({ level: 2 }),
|
|
6000
|
+
toggleHeading3: editor.can().toggleHeading({ level: 3 }),
|
|
6001
|
+
toggleBulletList: editor.can().toggleBulletList(),
|
|
6002
|
+
toggleOrderedList: editor.can().toggleOrderedList(),
|
|
6003
|
+
toggleBlockquote: editor.can().toggleBlockquote(),
|
|
6004
|
+
toggleCodeBlock: editor.can().toggleCodeBlock(),
|
|
6005
|
+
setTextAlignLeft: editor.can().setTextAlign("left"),
|
|
6006
|
+
setTextAlignCenter: editor.can().setTextAlign("center"),
|
|
6007
|
+
setTextAlignRight: editor.can().setTextAlign("right"),
|
|
6008
|
+
setTextAlignJustify: editor.can().setTextAlign("justify"),
|
|
6009
|
+
insertHorizontalRule: editor.can().setHorizontalRule(),
|
|
6010
|
+
insertTable: editor.can().insertTable(),
|
|
6011
|
+
insertImage: editor.can().setResizableImage({ src: "" }),
|
|
6012
|
+
},
|
|
6013
|
+
};
|
|
6014
|
+
};
|
|
6015
|
+
|
|
6016
|
+
/**
|
|
6017
|
+
* DiscoveryCalculator automatically detects and tracks the state of any TipTap extension.
|
|
6018
|
+
* It provides a "fallback" reactive state for any mark or node not explicitly handled
|
|
6019
|
+
* by specialized calculators.
|
|
6020
|
+
*/
|
|
6021
|
+
const AteDiscoveryCalculator = (editor) => {
|
|
6022
|
+
const state = {
|
|
6023
|
+
marks: {},
|
|
6024
|
+
nodes: {},
|
|
6025
|
+
};
|
|
6026
|
+
// We skip core extensions that are already handled by specialized calculators
|
|
6027
|
+
// to avoid redundant calculations and maintain precise attribute tracking.
|
|
6028
|
+
const handled = [
|
|
6029
|
+
"bold",
|
|
6030
|
+
"italic",
|
|
6031
|
+
"underline",
|
|
6032
|
+
"strike",
|
|
6033
|
+
"code",
|
|
6034
|
+
"link",
|
|
6035
|
+
"highlight",
|
|
6036
|
+
"superscript",
|
|
6037
|
+
"subscript",
|
|
6038
|
+
"table",
|
|
6039
|
+
"image",
|
|
6040
|
+
"resizableImage",
|
|
6041
|
+
"heading",
|
|
6042
|
+
"bulletList",
|
|
6043
|
+
"orderedList",
|
|
6044
|
+
"blockquote",
|
|
6045
|
+
"textAlign",
|
|
6046
|
+
"textStyle",
|
|
6047
|
+
"color",
|
|
6048
|
+
];
|
|
6049
|
+
editor.extensionManager.extensions.forEach(extension => {
|
|
6050
|
+
const name = extension.name;
|
|
6051
|
+
const type = extension.type;
|
|
6052
|
+
// Skip internal/core extensions or already handled ones
|
|
6053
|
+
if ([
|
|
6054
|
+
"selection",
|
|
6055
|
+
"editable",
|
|
6056
|
+
"focus",
|
|
6057
|
+
"undo",
|
|
6058
|
+
"redo",
|
|
6059
|
+
"history",
|
|
6060
|
+
"placeholder",
|
|
6061
|
+
"characterCount",
|
|
6062
|
+
].includes(name)) {
|
|
6063
|
+
return;
|
|
6064
|
+
}
|
|
6065
|
+
if (handled.includes(name)) {
|
|
6066
|
+
return;
|
|
6067
|
+
}
|
|
6068
|
+
if (type === "mark") {
|
|
6069
|
+
state.marks[name] = editor.isActive(name);
|
|
6070
|
+
}
|
|
6071
|
+
else if (type === "node") {
|
|
6072
|
+
state.nodes[name] = editor.isActive(name);
|
|
6073
|
+
}
|
|
6074
|
+
});
|
|
6075
|
+
return state;
|
|
6076
|
+
};
|
|
6077
|
+
|
|
6078
|
+
const AteLinkClickBehavior = Extension.create({
|
|
6079
|
+
name: "linkClickBehavior",
|
|
6080
|
+
addProseMirrorPlugins() {
|
|
6081
|
+
return [
|
|
6082
|
+
new Plugin({
|
|
6083
|
+
key: new PluginKey("linkClickBehavior"),
|
|
6084
|
+
props: {
|
|
6085
|
+
handleClick(view, _pos, event) {
|
|
6086
|
+
// handleClick only runs in the browser, but we guard it for absolute SSR safety
|
|
6087
|
+
if (typeof window === "undefined") {
|
|
6088
|
+
return false;
|
|
6089
|
+
}
|
|
6090
|
+
const isModKey = event.ctrlKey || event.metaKey;
|
|
6091
|
+
// If editor is editable, only proceed if Ctrl/Cmd is pressed
|
|
6092
|
+
if (view.editable && !isModKey) {
|
|
6093
|
+
return false;
|
|
6094
|
+
}
|
|
6095
|
+
const attrs = getAttributes(view.state, "link");
|
|
6096
|
+
if (attrs["href"]) {
|
|
6097
|
+
window.open(attrs["href"], "_blank", "noopener,noreferrer");
|
|
6098
|
+
return true;
|
|
6099
|
+
}
|
|
6100
|
+
return false;
|
|
6101
|
+
},
|
|
6102
|
+
},
|
|
6103
|
+
}),
|
|
6104
|
+
];
|
|
6105
|
+
},
|
|
6106
|
+
});
|
|
6107
|
+
|
|
6108
|
+
// Default toolbar configuration
|
|
6109
|
+
const ATE_DEFAULT_TOOLBAR_CONFIG = {
|
|
6110
|
+
bold: true,
|
|
6111
|
+
italic: true,
|
|
6112
|
+
underline: true,
|
|
6113
|
+
strike: true,
|
|
6114
|
+
code: true,
|
|
6115
|
+
codeBlock: true,
|
|
6116
|
+
superscript: false, // Disabled by default (opt-in)
|
|
6117
|
+
subscript: false, // Disabled by default (opt-in)
|
|
6118
|
+
highlight: false, // Disabled by default (opt-in)
|
|
6119
|
+
highlightPicker: true,
|
|
6120
|
+
heading1: true,
|
|
6121
|
+
heading2: true,
|
|
6122
|
+
heading3: true,
|
|
6123
|
+
bulletList: true,
|
|
6124
|
+
orderedList: true,
|
|
6125
|
+
blockquote: true,
|
|
6126
|
+
alignLeft: false, // Disabled by default (opt-in)
|
|
6127
|
+
alignCenter: false, // Disabled by default (opt-in)
|
|
6128
|
+
alignRight: false, // Disabled by default (opt-in)
|
|
6129
|
+
alignJustify: false, // Disabled by default (opt-in)
|
|
6130
|
+
link: true,
|
|
6131
|
+
image: true,
|
|
6132
|
+
horizontalRule: false, // Disabled by default (opt-in)
|
|
6133
|
+
table: true,
|
|
6134
|
+
undo: true,
|
|
6135
|
+
redo: true,
|
|
6136
|
+
clear: false, // Disabled by default (opt-in)
|
|
6137
|
+
textColor: true,
|
|
6138
|
+
separator: true,
|
|
6139
|
+
};
|
|
6140
|
+
// Default bubble menu configuration
|
|
6141
|
+
const ATE_DEFAULT_BUBBLE_MENU_CONFIG = {
|
|
6142
|
+
bold: true,
|
|
6143
|
+
italic: true,
|
|
6144
|
+
underline: true,
|
|
6145
|
+
strike: true,
|
|
6146
|
+
code: true,
|
|
6147
|
+
superscript: false,
|
|
6148
|
+
subscript: false,
|
|
6149
|
+
highlight: false,
|
|
6150
|
+
highlightPicker: true,
|
|
6151
|
+
textColor: true,
|
|
6152
|
+
link: true,
|
|
6153
|
+
separator: true,
|
|
6154
|
+
};
|
|
6155
|
+
// Default image bubble menu configuration
|
|
6156
|
+
const ATE_DEFAULT_IMAGE_BUBBLE_MENU_CONFIG = {
|
|
6157
|
+
changeImage: true,
|
|
6158
|
+
resizeSmall: true,
|
|
6159
|
+
resizeMedium: true,
|
|
6160
|
+
resizeLarge: true,
|
|
6161
|
+
resizeOriginal: true,
|
|
6162
|
+
deleteImage: true,
|
|
6163
|
+
separator: true,
|
|
6164
|
+
};
|
|
6165
|
+
// Default table bubble menu configuration
|
|
6166
|
+
const ATE_DEFAULT_TABLE_MENU_CONFIG = {
|
|
6167
|
+
addRowBefore: true,
|
|
6168
|
+
addRowAfter: true,
|
|
6169
|
+
deleteRow: true,
|
|
6170
|
+
addColumnBefore: true,
|
|
6171
|
+
addColumnAfter: true,
|
|
6172
|
+
deleteColumn: true,
|
|
6173
|
+
toggleHeaderRow: true,
|
|
6174
|
+
toggleHeaderColumn: true,
|
|
6175
|
+
deleteTable: true,
|
|
6176
|
+
separator: true,
|
|
6177
|
+
};
|
|
6178
|
+
// Default cell bubble menu configuration
|
|
6179
|
+
const ATE_DEFAULT_CELL_MENU_CONFIG = {
|
|
6180
|
+
mergeCells: true,
|
|
6181
|
+
splitCell: true,
|
|
6182
|
+
};
|
|
6183
|
+
|
|
6184
|
+
// Slash commands configuration is handled dynamically via slashCommandsConfigComputed
|
|
6185
|
+
/**
|
|
6186
|
+
* The main rich-text editor component for Angular.
|
|
6187
|
+
*
|
|
6188
|
+
* Powered by Tiptap and built with a native Signal-based architecture, it provides
|
|
6189
|
+
* a seamless, high-performance editing experience. Supports automatic registration
|
|
6190
|
+
* of Angular components as interactive nodes ('Angular Nodes'), full Reactive Forms
|
|
6191
|
+
* integration, and extensive customization via the AteEditorConfig.
|
|
6192
|
+
*/
|
|
6193
|
+
class AngularTiptapEditorComponent {
|
|
6194
|
+
// ============================================
|
|
6195
|
+
// Toolbar / Bubble Menu Coordination
|
|
6196
|
+
// ============================================
|
|
6197
|
+
hideBubbleMenus() {
|
|
6198
|
+
this.editorCommandsService.setToolbarInteracting(true);
|
|
6199
|
+
}
|
|
6200
|
+
showBubbleMenus() {
|
|
6201
|
+
this.editorCommandsService.setToolbarInteracting(false);
|
|
6202
|
+
}
|
|
6203
|
+
constructor() {
|
|
6204
|
+
/** Configuration globale de l'éditeur */
|
|
6205
|
+
this.config = input({}, ...(ngDevMode ? [{ debugName: "config" }] : []));
|
|
6206
|
+
this.content = input("", ...(ngDevMode ? [{ debugName: "content" }] : []));
|
|
6207
|
+
this.placeholder = input("", ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
|
|
6208
|
+
this.editable = input(true, ...(ngDevMode ? [{ debugName: "editable" }] : []));
|
|
6209
|
+
this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
6210
|
+
this.minHeight = input(200, ...(ngDevMode ? [{ debugName: "minHeight" }] : []));
|
|
6211
|
+
this.height = input(undefined, ...(ngDevMode ? [{ debugName: "height" }] : []));
|
|
6212
|
+
this.maxHeight = input(undefined, ...(ngDevMode ? [{ debugName: "maxHeight" }] : []));
|
|
6213
|
+
this.fillContainer = input(false, ...(ngDevMode ? [{ debugName: "fillContainer" }] : []));
|
|
6214
|
+
this.showToolbar = input(true, ...(ngDevMode ? [{ debugName: "showToolbar" }] : []));
|
|
6215
|
+
this.showFooter = input(true, ...(ngDevMode ? [{ debugName: "showFooter" }] : []));
|
|
6216
|
+
this.showCharacterCount = input(true, ...(ngDevMode ? [{ debugName: "showCharacterCount" }] : []));
|
|
6217
|
+
this.showWordCount = input(true, ...(ngDevMode ? [{ debugName: "showWordCount" }] : []));
|
|
6218
|
+
this.maxCharacters = input(undefined, ...(ngDevMode ? [{ debugName: "maxCharacters" }] : []));
|
|
6219
|
+
this.enableOfficePaste = input(true, ...(ngDevMode ? [{ debugName: "enableOfficePaste" }] : []));
|
|
6220
|
+
this.enableSlashCommands = input(true, ...(ngDevMode ? [{ debugName: "enableSlashCommands" }] : []));
|
|
6221
|
+
this.slashCommands = input({}, ...(ngDevMode ? [{ debugName: "slashCommands" }] : []));
|
|
6222
|
+
this.customSlashCommands = input(undefined, ...(ngDevMode ? [{ debugName: "customSlashCommands" }] : []));
|
|
6223
|
+
this.locale = input(undefined, ...(ngDevMode ? [{ debugName: "locale" }] : []));
|
|
6224
|
+
this.autofocus = input(false, ...(ngDevMode ? [{ debugName: "autofocus" }] : []));
|
|
6225
|
+
this.seamless = input(false, ...(ngDevMode ? [{ debugName: "seamless" }] : []));
|
|
6226
|
+
this.floatingToolbar = input(false, ...(ngDevMode ? [{ debugName: "floatingToolbar" }] : []));
|
|
6227
|
+
this.showEditToggle = input(false, ...(ngDevMode ? [{ debugName: "showEditToggle" }] : []));
|
|
6228
|
+
this.spellcheck = input(true, ...(ngDevMode ? [{ debugName: "spellcheck" }] : []));
|
|
6229
|
+
this.tiptapExtensions = input([], ...(ngDevMode ? [{ debugName: "tiptapExtensions" }] : []));
|
|
6230
|
+
this.tiptapOptions = input({}, ...(ngDevMode ? [{ debugName: "tiptapOptions" }] : []));
|
|
6231
|
+
// Nouveaux inputs pour les bubble menus
|
|
6232
|
+
this.showBubbleMenu = input(true, ...(ngDevMode ? [{ debugName: "showBubbleMenu" }] : []));
|
|
6233
|
+
this.bubbleMenu = input(ATE_DEFAULT_BUBBLE_MENU_CONFIG, ...(ngDevMode ? [{ debugName: "bubbleMenu" }] : []));
|
|
6234
|
+
this.showImageBubbleMenu = input(true, ...(ngDevMode ? [{ debugName: "showImageBubbleMenu" }] : []));
|
|
6235
|
+
this.imageBubbleMenu = input(ATE_DEFAULT_IMAGE_BUBBLE_MENU_CONFIG, ...(ngDevMode ? [{ debugName: "imageBubbleMenu" }] : []));
|
|
6236
|
+
// Configuration de la toolbar
|
|
6237
|
+
this.toolbar = input({}, ...(ngDevMode ? [{ debugName: "toolbar" }] : []));
|
|
6238
|
+
// Configuration des menus de table
|
|
5795
6239
|
this.showTableBubbleMenu = input(true, ...(ngDevMode ? [{ debugName: "showTableBubbleMenu" }] : []));
|
|
5796
6240
|
this.tableBubbleMenu = input(ATE_DEFAULT_TABLE_MENU_CONFIG, ...(ngDevMode ? [{ debugName: "tableBubbleMenu" }] : []));
|
|
5797
6241
|
this.showCellBubbleMenu = input(true, ...(ngDevMode ? [{ debugName: "showCellBubbleMenu" }] : []));
|
|
@@ -5848,7 +6292,7 @@ class AngularTiptapEditorComponent {
|
|
|
5848
6292
|
this._isFormControlDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_isFormControlDisabled" }] : []));
|
|
5849
6293
|
this.isFormControlDisabled = this._isFormControlDisabled.asReadonly();
|
|
5850
6294
|
// Combined disabled state (Input + FormControl)
|
|
5851
|
-
this.mergedDisabled = computed(() => (this.
|
|
6295
|
+
this.mergedDisabled = computed(() => (this.effectiveConfig().disabled ?? this.disabled()) || this.isFormControlDisabled(), ...(ngDevMode ? [{ debugName: "mergedDisabled" }] : []));
|
|
5852
6296
|
// Computed for editor states
|
|
5853
6297
|
this.isEditorReady = computed(() => this.editor() !== null, ...(ngDevMode ? [{ debugName: "isEditorReady" }] : []));
|
|
5854
6298
|
// ============================================
|
|
@@ -5856,27 +6300,27 @@ class AngularTiptapEditorComponent {
|
|
|
5856
6300
|
// ============================================
|
|
5857
6301
|
// Appearance & Fundamentals
|
|
5858
6302
|
this.finalSeamless = computed(() => {
|
|
5859
|
-
const fromConfig = this.
|
|
6303
|
+
const fromConfig = this.effectiveConfig().mode;
|
|
5860
6304
|
if (fromConfig !== undefined) {
|
|
5861
6305
|
return fromConfig === "seamless";
|
|
5862
6306
|
}
|
|
5863
6307
|
return this.seamless();
|
|
5864
6308
|
}, ...(ngDevMode ? [{ debugName: "finalSeamless" }] : []));
|
|
5865
|
-
this.finalEditable = computed(() => this.
|
|
5866
|
-
this.finalPlaceholder = computed(() => this.
|
|
6309
|
+
this.finalEditable = computed(() => this.effectiveConfig().editable ?? this.editable(), ...(ngDevMode ? [{ debugName: "finalEditable" }] : []));
|
|
6310
|
+
this.finalPlaceholder = computed(() => this.effectiveConfig().placeholder ??
|
|
5867
6311
|
(this.placeholder() || this.currentTranslations().editor.placeholder), ...(ngDevMode ? [{ debugName: "finalPlaceholder" }] : []));
|
|
5868
|
-
this.finalFillContainer = computed(() => this.
|
|
5869
|
-
this.finalShowFooter = computed(() => this.
|
|
5870
|
-
this.finalShowEditToggle = computed(() => this.
|
|
5871
|
-
this.finalHeight = computed(() => this.
|
|
5872
|
-
this.finalMinHeight = computed(() => this.
|
|
5873
|
-
this.finalMaxHeight = computed(() => this.
|
|
5874
|
-
this.finalSpellcheck = computed(() => this.
|
|
5875
|
-
this.finalEnableOfficePaste = computed(() => this.
|
|
6312
|
+
this.finalFillContainer = computed(() => this.effectiveConfig().fillContainer ?? this.fillContainer(), ...(ngDevMode ? [{ debugName: "finalFillContainer" }] : []));
|
|
6313
|
+
this.finalShowFooter = computed(() => this.effectiveConfig().showFooter ?? this.showFooter(), ...(ngDevMode ? [{ debugName: "finalShowFooter" }] : []));
|
|
6314
|
+
this.finalShowEditToggle = computed(() => this.effectiveConfig().showEditToggle ?? this.showEditToggle(), ...(ngDevMode ? [{ debugName: "finalShowEditToggle" }] : []));
|
|
6315
|
+
this.finalHeight = computed(() => this.effectiveConfig().height ?? (this.height() ? `${this.height()}px` : undefined), ...(ngDevMode ? [{ debugName: "finalHeight" }] : []));
|
|
6316
|
+
this.finalMinHeight = computed(() => this.effectiveConfig().minHeight ?? (this.minHeight() ? `${this.minHeight()}px` : undefined), ...(ngDevMode ? [{ debugName: "finalMinHeight" }] : []));
|
|
6317
|
+
this.finalMaxHeight = computed(() => this.effectiveConfig().maxHeight ?? (this.maxHeight() ? `${this.maxHeight()}px` : undefined), ...(ngDevMode ? [{ debugName: "finalMaxHeight" }] : []));
|
|
6318
|
+
this.finalSpellcheck = computed(() => this.effectiveConfig().spellcheck ?? this.spellcheck(), ...(ngDevMode ? [{ debugName: "finalSpellcheck" }] : []));
|
|
6319
|
+
this.finalEnableOfficePaste = computed(() => this.effectiveConfig().enableOfficePaste ?? this.enableOfficePaste(), ...(ngDevMode ? [{ debugName: "finalEnableOfficePaste" }] : []));
|
|
5876
6320
|
// Features
|
|
5877
|
-
this.finalShowToolbar = computed(() => this.
|
|
6321
|
+
this.finalShowToolbar = computed(() => this.effectiveConfig().showToolbar ?? this.showToolbar(), ...(ngDevMode ? [{ debugName: "finalShowToolbar" }] : []));
|
|
5878
6322
|
this.finalToolbarConfig = computed(() => {
|
|
5879
|
-
const fromConfig = this.
|
|
6323
|
+
const fromConfig = this.effectiveConfig().toolbar;
|
|
5880
6324
|
const base = ATE_DEFAULT_TOOLBAR_CONFIG;
|
|
5881
6325
|
if (fromConfig) {
|
|
5882
6326
|
return { ...base, ...fromConfig };
|
|
@@ -5884,19 +6328,19 @@ class AngularTiptapEditorComponent {
|
|
|
5884
6328
|
const fromInput = this.toolbar();
|
|
5885
6329
|
return Object.keys(fromInput).length === 0 ? base : { ...base, ...fromInput };
|
|
5886
6330
|
}, ...(ngDevMode ? [{ debugName: "finalToolbarConfig" }] : []));
|
|
5887
|
-
this.finalFloatingToolbar = computed(() => this.
|
|
5888
|
-
this.finalShowBubbleMenu = computed(() => this.
|
|
6331
|
+
this.finalFloatingToolbar = computed(() => this.effectiveConfig().floatingToolbar ?? this.floatingToolbar(), ...(ngDevMode ? [{ debugName: "finalFloatingToolbar" }] : []));
|
|
6332
|
+
this.finalShowBubbleMenu = computed(() => this.effectiveConfig().showBubbleMenu ?? this.showBubbleMenu(), ...(ngDevMode ? [{ debugName: "finalShowBubbleMenu" }] : []));
|
|
5889
6333
|
this.finalBubbleMenuConfig = computed(() => {
|
|
5890
|
-
const fromConfig = this.
|
|
6334
|
+
const fromConfig = this.effectiveConfig().bubbleMenu;
|
|
5891
6335
|
const base = ATE_DEFAULT_BUBBLE_MENU_CONFIG;
|
|
5892
6336
|
if (fromConfig) {
|
|
5893
6337
|
return { ...base, ...fromConfig };
|
|
5894
6338
|
}
|
|
5895
6339
|
return Object.keys(this.bubbleMenu()).length === 0 ? base : { ...base, ...this.bubbleMenu() };
|
|
5896
6340
|
}, ...(ngDevMode ? [{ debugName: "finalBubbleMenuConfig" }] : []));
|
|
5897
|
-
this.finalShowImageBubbleMenu = computed(() => this.
|
|
6341
|
+
this.finalShowImageBubbleMenu = computed(() => this.effectiveConfig().showImageBubbleMenu ?? this.showImageBubbleMenu(), ...(ngDevMode ? [{ debugName: "finalShowImageBubbleMenu" }] : []));
|
|
5898
6342
|
this.finalImageBubbleMenuConfig = computed(() => {
|
|
5899
|
-
const fromConfig = this.
|
|
6343
|
+
const fromConfig = this.effectiveConfig().imageBubbleMenu;
|
|
5900
6344
|
const base = ATE_DEFAULT_IMAGE_BUBBLE_MENU_CONFIG;
|
|
5901
6345
|
if (fromConfig) {
|
|
5902
6346
|
return { ...base, ...fromConfig };
|
|
@@ -5905,9 +6349,9 @@ class AngularTiptapEditorComponent {
|
|
|
5905
6349
|
? base
|
|
5906
6350
|
: { ...base, ...this.imageBubbleMenu() };
|
|
5907
6351
|
}, ...(ngDevMode ? [{ debugName: "finalImageBubbleMenuConfig" }] : []));
|
|
5908
|
-
this.finalShowTableBubbleMenu = computed(() => this.
|
|
6352
|
+
this.finalShowTableBubbleMenu = computed(() => this.effectiveConfig().showTableMenu ?? this.showTableBubbleMenu(), ...(ngDevMode ? [{ debugName: "finalShowTableBubbleMenu" }] : []));
|
|
5909
6353
|
this.finalTableBubbleMenuConfig = computed(() => {
|
|
5910
|
-
const fromConfig = this.
|
|
6354
|
+
const fromConfig = this.effectiveConfig().tableBubbleMenu;
|
|
5911
6355
|
const base = ATE_DEFAULT_TABLE_MENU_CONFIG;
|
|
5912
6356
|
if (fromConfig) {
|
|
5913
6357
|
return { ...base, ...fromConfig };
|
|
@@ -5916,9 +6360,9 @@ class AngularTiptapEditorComponent {
|
|
|
5916
6360
|
? base
|
|
5917
6361
|
: { ...base, ...this.tableBubbleMenu() };
|
|
5918
6362
|
}, ...(ngDevMode ? [{ debugName: "finalTableBubbleMenuConfig" }] : []));
|
|
5919
|
-
this.finalShowCellBubbleMenu = computed(() => this.
|
|
6363
|
+
this.finalShowCellBubbleMenu = computed(() => this.effectiveConfig().showCellMenu ?? this.showCellBubbleMenu(), ...(ngDevMode ? [{ debugName: "finalShowCellBubbleMenu" }] : []));
|
|
5920
6364
|
this.finalCellBubbleMenuConfig = computed(() => {
|
|
5921
|
-
const fromConfig = this.
|
|
6365
|
+
const fromConfig = this.effectiveConfig().cellBubbleMenu;
|
|
5922
6366
|
const base = ATE_DEFAULT_CELL_MENU_CONFIG;
|
|
5923
6367
|
if (fromConfig) {
|
|
5924
6368
|
return { ...base, ...fromConfig };
|
|
@@ -5927,10 +6371,12 @@ class AngularTiptapEditorComponent {
|
|
|
5927
6371
|
? base
|
|
5928
6372
|
: { ...base, ...this.cellBubbleMenu() };
|
|
5929
6373
|
}, ...(ngDevMode ? [{ debugName: "finalCellBubbleMenuConfig" }] : []));
|
|
5930
|
-
this.finalEnableSlashCommands = computed(() => this.
|
|
6374
|
+
this.finalEnableSlashCommands = computed(() => this.effectiveConfig().enableSlashCommands ?? this.enableSlashCommands(), ...(ngDevMode ? [{ debugName: "finalEnableSlashCommands" }] : []));
|
|
5931
6375
|
this.finalSlashCommandsConfig = computed(() => {
|
|
5932
|
-
const fromConfig = this.
|
|
5933
|
-
const
|
|
6376
|
+
const fromConfig = this.effectiveConfig().slashCommands;
|
|
6377
|
+
const fromGlobalCustom = this.effectiveConfig().customSlashCommands;
|
|
6378
|
+
const fromInputCustom = this.customSlashCommands();
|
|
6379
|
+
const customConfig = fromInputCustom ?? fromGlobalCustom;
|
|
5934
6380
|
if (customConfig) {
|
|
5935
6381
|
return customConfig;
|
|
5936
6382
|
}
|
|
@@ -5943,14 +6389,19 @@ class AngularTiptapEditorComponent {
|
|
|
5943
6389
|
};
|
|
5944
6390
|
}, ...(ngDevMode ? [{ debugName: "finalSlashCommandsConfig" }] : []));
|
|
5945
6391
|
// 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.
|
|
6392
|
+
this.finalAutofocus = computed(() => this.effectiveConfig().autofocus ?? this.autofocus(), ...(ngDevMode ? [{ debugName: "finalAutofocus" }] : []));
|
|
6393
|
+
this.finalMaxCharacters = computed(() => this.effectiveConfig().maxCharacters ?? this.maxCharacters(), ...(ngDevMode ? [{ debugName: "finalMaxCharacters" }] : []));
|
|
6394
|
+
this.finalShowCharacterCount = computed(() => this.effectiveConfig().showCharacterCount ?? this.showCharacterCount(), ...(ngDevMode ? [{ debugName: "finalShowCharacterCount" }] : []));
|
|
6395
|
+
this.finalShowWordCount = computed(() => this.effectiveConfig().showWordCount ?? this.showWordCount(), ...(ngDevMode ? [{ debugName: "finalShowWordCount" }] : []));
|
|
6396
|
+
this.finalLocale = computed(() => this.effectiveConfig().locale ?? this.locale(), ...(ngDevMode ? [{ debugName: "finalLocale" }] : []));
|
|
6397
|
+
// Extensions & Options
|
|
6398
|
+
this.finalTiptapExtensions = computed(() => this.effectiveConfig().tiptapExtensions ?? this.tiptapExtensions() ?? [], ...(ngDevMode ? [{ debugName: "finalTiptapExtensions" }] : []));
|
|
6399
|
+
this.finalTiptapOptions = computed(() => this.effectiveConfig().tiptapOptions ?? this.tiptapOptions() ?? {}, ...(ngDevMode ? [{ debugName: "finalTiptapOptions" }] : []));
|
|
6400
|
+
this.finalStateCalculators = computed(() => this.effectiveConfig().stateCalculators ?? this.stateCalculators() ?? [], ...(ngDevMode ? [{ debugName: "finalStateCalculators" }] : []));
|
|
6401
|
+
this.finalAngularNodesConfig = computed(() => this.effectiveConfig().angularNodes ?? [], ...(ngDevMode ? [{ debugName: "finalAngularNodesConfig" }] : []));
|
|
5951
6402
|
// Image Upload
|
|
5952
6403
|
this.finalImageUploadConfig = computed(() => {
|
|
5953
|
-
const fromConfig = this.
|
|
6404
|
+
const fromConfig = this.effectiveConfig().imageUpload;
|
|
5954
6405
|
const fromInput = this.imageUpload();
|
|
5955
6406
|
const merged = {
|
|
5956
6407
|
maxSize: 5, // Default 5MB
|
|
@@ -5970,7 +6421,7 @@ class AngularTiptapEditorComponent {
|
|
|
5970
6421
|
maxSize: merged.maxSize * 1024 * 1024, // Convert MB to bytes for internal service
|
|
5971
6422
|
};
|
|
5972
6423
|
}, ...(ngDevMode ? [{ debugName: "finalImageUploadConfig" }] : []));
|
|
5973
|
-
this.finalImageUploadHandler = computed(() => this.
|
|
6424
|
+
this.finalImageUploadHandler = computed(() => this.effectiveConfig().imageUpload?.handler ?? this.imageUploadHandler(), ...(ngDevMode ? [{ debugName: "finalImageUploadHandler" }] : []));
|
|
5974
6425
|
// Computed for current translations (allows per-instance override via config or input)
|
|
5975
6426
|
this.currentTranslations = computed(() => {
|
|
5976
6427
|
const localeOverride = this.finalLocale();
|
|
@@ -5987,6 +6438,17 @@ class AngularTiptapEditorComponent {
|
|
|
5987
6438
|
this.editorCommandsService = inject(AteEditorCommandsService);
|
|
5988
6439
|
// Access editor state via service
|
|
5989
6440
|
this.editorState = this.editorCommandsService.editorState;
|
|
6441
|
+
this.injector = inject(Injector);
|
|
6442
|
+
this.globalConfig = inject(ATE_GLOBAL_CONFIG, { optional: true });
|
|
6443
|
+
/**
|
|
6444
|
+
* Final merged configuration.
|
|
6445
|
+
* Priority: Input [config] > Global config via provideAteEditor()
|
|
6446
|
+
*/
|
|
6447
|
+
this.effectiveConfig = computed(() => {
|
|
6448
|
+
const fromInput = this.config();
|
|
6449
|
+
const fromGlobal = this.globalConfig || {};
|
|
6450
|
+
return { ...fromGlobal, ...fromInput };
|
|
6451
|
+
}, ...(ngDevMode ? [{ debugName: "effectiveConfig" }] : []));
|
|
5990
6452
|
// Effect to update editor content (with anti-echo)
|
|
5991
6453
|
effect(() => {
|
|
5992
6454
|
const content = this.content(); // Sole reactive dependency
|
|
@@ -6053,11 +6515,12 @@ class AngularTiptapEditorComponent {
|
|
|
6053
6515
|
}
|
|
6054
6516
|
}
|
|
6055
6517
|
});
|
|
6056
|
-
// Effect to re-initialize editor when
|
|
6518
|
+
// Effect to re-initialize editor when technical configuration changes
|
|
6057
6519
|
effect(() => {
|
|
6058
|
-
// Monitor
|
|
6059
|
-
this.
|
|
6060
|
-
this.
|
|
6520
|
+
// Monitor technical dependencies
|
|
6521
|
+
this.finalTiptapExtensions();
|
|
6522
|
+
this.finalTiptapOptions();
|
|
6523
|
+
this.finalAngularNodesConfig();
|
|
6061
6524
|
untracked(() => {
|
|
6062
6525
|
// Only if already initialized (post AfterViewInit)
|
|
6063
6526
|
if (this.editorFullyInitialized()) {
|
|
@@ -6136,7 +6599,7 @@ class AngularTiptapEditorComponent {
|
|
|
6136
6599
|
AteImageCalculator,
|
|
6137
6600
|
AteStructureCalculator,
|
|
6138
6601
|
AteDiscoveryCalculator,
|
|
6139
|
-
...this.
|
|
6602
|
+
...this.finalStateCalculators(),
|
|
6140
6603
|
],
|
|
6141
6604
|
}),
|
|
6142
6605
|
];
|
|
@@ -6153,8 +6616,22 @@ class AngularTiptapEditorComponent {
|
|
|
6153
6616
|
limit: this.finalMaxCharacters(),
|
|
6154
6617
|
}));
|
|
6155
6618
|
}
|
|
6619
|
+
// Register automatic node views from config
|
|
6620
|
+
const autoNodeViews = this.finalAngularNodesConfig();
|
|
6621
|
+
autoNodeViews.forEach(reg => {
|
|
6622
|
+
const options = typeof reg === "function"
|
|
6623
|
+
? { component: reg }
|
|
6624
|
+
: reg;
|
|
6625
|
+
try {
|
|
6626
|
+
const extension = registerAngularComponent(this.injector, options);
|
|
6627
|
+
extensions.push(extension);
|
|
6628
|
+
}
|
|
6629
|
+
catch (e) {
|
|
6630
|
+
console.error("[ATE] Failed to auto-register node view:", e);
|
|
6631
|
+
}
|
|
6632
|
+
});
|
|
6156
6633
|
// Allow addition of custom extensions, but avoid duplicates by filtering by name
|
|
6157
|
-
const customExtensions = this.
|
|
6634
|
+
const customExtensions = this.finalTiptapExtensions();
|
|
6158
6635
|
if (customExtensions.length > 0) {
|
|
6159
6636
|
const existingNames = new Set(extensions
|
|
6160
6637
|
.map(ext => ext?.name)
|
|
@@ -6166,7 +6643,7 @@ class AngularTiptapEditorComponent {
|
|
|
6166
6643
|
extensions.push(...toAdd);
|
|
6167
6644
|
}
|
|
6168
6645
|
// Also allow any tiptap user options
|
|
6169
|
-
const userOptions = this.
|
|
6646
|
+
const userOptions = this.finalTiptapOptions();
|
|
6170
6647
|
const newEditor = new Editor({
|
|
6171
6648
|
...userOptions,
|
|
6172
6649
|
element: this.editorElement().nativeElement,
|
|
@@ -6265,228 +6742,84 @@ class AngularTiptapEditorComponent {
|
|
|
6265
6742
|
}
|
|
6266
6743
|
// Public methods
|
|
6267
6744
|
getHTML() {
|
|
6268
|
-
return this.editor()?.getHTML() || "";
|
|
6269
|
-
}
|
|
6270
|
-
getJSON() {
|
|
6271
|
-
return this.editor()?.getJSON();
|
|
6272
|
-
}
|
|
6273
|
-
getText() {
|
|
6274
|
-
return this.editor()?.getText() || "";
|
|
6275
|
-
}
|
|
6276
|
-
setContent(content, emitUpdate = true) {
|
|
6277
|
-
const editor = this.editor();
|
|
6278
|
-
if (editor) {
|
|
6279
|
-
this.editorCommandsService.setContent(editor, content, emitUpdate);
|
|
6280
|
-
}
|
|
6281
|
-
}
|
|
6282
|
-
focus() {
|
|
6283
|
-
const editor = this.editor();
|
|
6284
|
-
if (editor) {
|
|
6285
|
-
this.editorCommandsService.focus(editor);
|
|
6286
|
-
}
|
|
6287
|
-
}
|
|
6288
|
-
blur() {
|
|
6289
|
-
const editor = this.editor();
|
|
6290
|
-
if (editor) {
|
|
6291
|
-
this.editorCommandsService.blur(editor);
|
|
6292
|
-
}
|
|
6293
|
-
}
|
|
6294
|
-
clearContent() {
|
|
6295
|
-
const editor = this.editor();
|
|
6296
|
-
if (editor) {
|
|
6297
|
-
this.editorCommandsService.clearContent(editor);
|
|
6298
|
-
}
|
|
6299
|
-
}
|
|
6300
|
-
// Méthode publique pour obtenir l'éditeur
|
|
6301
|
-
getEditor() {
|
|
6302
|
-
return this.editor();
|
|
6303
|
-
}
|
|
6304
|
-
setupFormControlSubscription() {
|
|
6305
|
-
const control = this.ngControl?.control;
|
|
6306
|
-
if (control) {
|
|
6307
|
-
// Synchronize form control value with editor content
|
|
6308
|
-
const formValue$ = concat(defer(() => of(control.value)), control.valueChanges);
|
|
6309
|
-
formValue$
|
|
6310
|
-
.pipe(tap((value) => {
|
|
6311
|
-
const editor = this.editor();
|
|
6312
|
-
if (editor) {
|
|
6313
|
-
this.setContent(value, false);
|
|
6314
|
-
}
|
|
6315
|
-
}), takeUntilDestroyed(this._destroyRef))
|
|
6316
|
-
.subscribe();
|
|
6317
|
-
// Synchronize form control status with editor disabled state
|
|
6318
|
-
const formStatus$ = concat(defer(() => of(control.status)), control.statusChanges);
|
|
6319
|
-
formStatus$
|
|
6320
|
-
.pipe(tap((status) => {
|
|
6321
|
-
this._isFormControlDisabled.set(status === "DISABLED");
|
|
6322
|
-
}), takeUntilDestroyed(this._destroyRef))
|
|
6323
|
-
.subscribe();
|
|
6324
|
-
}
|
|
6325
|
-
}
|
|
6326
|
-
onEditorClick(event) {
|
|
6327
|
-
const editor = this.editor();
|
|
6328
|
-
if (!editor || !this.finalEditable()) {
|
|
6329
|
-
return;
|
|
6330
|
-
}
|
|
6331
|
-
// Verify if interaction is on the container element and not on the content
|
|
6332
|
-
const target = event.target;
|
|
6333
|
-
const editorElement = this.editorElement()?.nativeElement;
|
|
6334
|
-
if (target === editorElement || target.classList.contains("ate-content")) {
|
|
6335
|
-
// Interaction in the empty space, position the cursor at the end
|
|
6336
|
-
setTimeout(() => {
|
|
6337
|
-
const { doc } = editor.state;
|
|
6338
|
-
const endPos = doc.content.size;
|
|
6339
|
-
editor.commands.setTextSelection(endPos);
|
|
6340
|
-
editor.commands.focus();
|
|
6341
|
-
}, 0);
|
|
6342
|
-
}
|
|
6343
|
-
}
|
|
6344
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AngularTiptapEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
6345
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AngularTiptapEditorComponent, isStandalone: true, selector: "angular-tiptap-editor", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, content: { classPropertyName: "content", publicName: "content", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, minHeight: { classPropertyName: "minHeight", publicName: "minHeight", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, fillContainer: { classPropertyName: "fillContainer", publicName: "fillContainer", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, showFooter: { classPropertyName: "showFooter", publicName: "showFooter", isSignal: true, isRequired: false, transformFunction: null }, showCharacterCount: { classPropertyName: "showCharacterCount", publicName: "showCharacterCount", isSignal: true, isRequired: false, transformFunction: null }, showWordCount: { classPropertyName: "showWordCount", publicName: "showWordCount", isSignal: true, isRequired: false, transformFunction: null }, maxCharacters: { classPropertyName: "maxCharacters", publicName: "maxCharacters", isSignal: true, isRequired: false, transformFunction: null }, enableOfficePaste: { classPropertyName: "enableOfficePaste", publicName: "enableOfficePaste", isSignal: true, isRequired: false, transformFunction: null }, enableSlashCommands: { classPropertyName: "enableSlashCommands", publicName: "enableSlashCommands", isSignal: true, isRequired: false, transformFunction: null }, slashCommands: { classPropertyName: "slashCommands", publicName: "slashCommands", isSignal: true, isRequired: false, transformFunction: null }, customSlashCommands: { classPropertyName: "customSlashCommands", publicName: "customSlashCommands", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, autofocus: { classPropertyName: "autofocus", publicName: "autofocus", isSignal: true, isRequired: false, transformFunction: null }, seamless: { classPropertyName: "seamless", publicName: "seamless", isSignal: true, isRequired: false, transformFunction: null }, floatingToolbar: { classPropertyName: "floatingToolbar", publicName: "floatingToolbar", isSignal: true, isRequired: false, transformFunction: null }, showEditToggle: { classPropertyName: "showEditToggle", publicName: "showEditToggle", isSignal: true, isRequired: false, transformFunction: null }, spellcheck: { classPropertyName: "spellcheck", publicName: "spellcheck", isSignal: true, isRequired: false, transformFunction: null }, tiptapExtensions: { classPropertyName: "tiptapExtensions", publicName: "tiptapExtensions", isSignal: true, isRequired: false, transformFunction: null }, tiptapOptions: { classPropertyName: "tiptapOptions", publicName: "tiptapOptions", isSignal: true, isRequired: false, transformFunction: null }, showBubbleMenu: { classPropertyName: "showBubbleMenu", publicName: "showBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, bubbleMenu: { classPropertyName: "bubbleMenu", publicName: "bubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, showImageBubbleMenu: { classPropertyName: "showImageBubbleMenu", publicName: "showImageBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, imageBubbleMenu: { classPropertyName: "imageBubbleMenu", publicName: "imageBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, toolbar: { classPropertyName: "toolbar", publicName: "toolbar", isSignal: true, isRequired: false, transformFunction: null }, showTableBubbleMenu: { classPropertyName: "showTableBubbleMenu", publicName: "showTableBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, tableBubbleMenu: { classPropertyName: "tableBubbleMenu", publicName: "tableBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, showCellBubbleMenu: { classPropertyName: "showCellBubbleMenu", publicName: "showCellBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, cellBubbleMenu: { classPropertyName: "cellBubbleMenu", publicName: "cellBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, stateCalculators: { classPropertyName: "stateCalculators", publicName: "stateCalculators", isSignal: true, isRequired: false, transformFunction: null }, imageUpload: { classPropertyName: "imageUpload", publicName: "imageUpload", isSignal: true, isRequired: false, transformFunction: null }, imageUploadHandler: { classPropertyName: "imageUploadHandler", publicName: "imageUploadHandler", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { contentChange: "contentChange", editorCreated: "editorCreated", editorUpdate: "editorUpdate", editorFocus: "editorFocus", editorBlur: "editorBlur", editableChange: "editableChange" }, host: { properties: { "class.fill-container": "finalFillContainer()", "class.floating-toolbar": "finalFloatingToolbar()", "class.is-readonly": "!finalEditable() && !mergedDisabled()", "class.is-disabled": "mergedDisabled()", "style.--ate-border-width": "finalSeamless() || mergedDisabled() ? '0' : null", "style.--ate-background": "finalSeamless() ? 'transparent' : (mergedDisabled() ? 'var(--ate-surface-tertiary)' : null)", "style.--ate-toolbar-border-color": "finalSeamless() ? 'transparent' : null", "style.--ate-counter-background": "finalSeamless() ? 'transparent' : null", "style.--ate-counter-border-color": "finalSeamless() ? 'transparent' : null", "class.dark": "config().theme === 'dark'", "attr.data-theme": "config().theme" } }, providers: [AteEditorCommandsService, AteImageService, AteColorPickerService, AteLinkService], viewQueries: [{ propertyName: "editorElement", first: true, predicate: ["editorElement"], descendants: true, isSignal: true }], hostDirectives: [{ directive: AteNoopValueAccessorDirective }], ngImport: i0, template: `
|
|
6346
|
-
<div class="ate-editor">
|
|
6347
|
-
<!-- Toolbar -->
|
|
6348
|
-
@if (finalEditable() && !mergedDisabled() && finalShowToolbar() && editor()) {
|
|
6349
|
-
<ate-toolbar
|
|
6350
|
-
[editor]="editor()!"
|
|
6351
|
-
[config]="finalToolbarConfig()"
|
|
6352
|
-
[imageUpload]="finalImageUploadConfig()"
|
|
6353
|
-
[floating]="finalFloatingToolbar()"
|
|
6354
|
-
(mouseenter)="hideBubbleMenus()"
|
|
6355
|
-
(mouseleave)="showBubbleMenus()" />
|
|
6356
|
-
}
|
|
6357
|
-
|
|
6358
|
-
@if (finalShowEditToggle() && !mergedDisabled()) {
|
|
6359
|
-
<ate-edit-toggle
|
|
6360
|
-
[editable]="finalEditable()"
|
|
6361
|
-
[translations]="currentTranslations()"
|
|
6362
|
-
(editToggle)="toggleEditMode($event)" />
|
|
6363
|
-
}
|
|
6364
|
-
|
|
6365
|
-
<!-- Editor Content -->
|
|
6366
|
-
<div
|
|
6367
|
-
#editorElement
|
|
6368
|
-
class="ate-content"
|
|
6369
|
-
[class.drag-over]="isDragOver()"
|
|
6370
|
-
(dragover)="onDragOver($event)"
|
|
6371
|
-
(drop)="onDrop($event)"
|
|
6372
|
-
(click)="onEditorClick($event)"
|
|
6373
|
-
(keydown.enter)="onEditorClick($event)"
|
|
6374
|
-
(keydown.space)="onEditorClick($event)"
|
|
6375
|
-
tabindex="0"
|
|
6376
|
-
role="application"
|
|
6377
|
-
[attr.aria-label]="currentTranslations().editor.placeholder"></div>
|
|
6378
|
-
|
|
6379
|
-
<!-- Text Bubble Menu -->
|
|
6380
|
-
@if (finalEditable() && finalShowBubbleMenu() && editor()) {
|
|
6381
|
-
<ate-bubble-menu
|
|
6382
|
-
[editor]="editor()!"
|
|
6383
|
-
[config]="finalBubbleMenuConfig()"
|
|
6384
|
-
[style.display]="editorFullyInitialized() ? 'block' : 'none'"></ate-bubble-menu>
|
|
6385
|
-
}
|
|
6386
|
-
|
|
6387
|
-
<!-- Image Bubble Menu -->
|
|
6388
|
-
@if (finalEditable() && finalShowImageBubbleMenu() && editor()) {
|
|
6389
|
-
<ate-image-bubble-menu
|
|
6390
|
-
[editor]="editor()!"
|
|
6391
|
-
[config]="finalImageBubbleMenuConfig()"
|
|
6392
|
-
[imageUpload]="finalImageUploadConfig()"
|
|
6393
|
-
[style.display]="editorFullyInitialized() ? 'block' : 'none'"></ate-image-bubble-menu>
|
|
6394
|
-
}
|
|
6395
|
-
|
|
6396
|
-
<!-- Link Bubble Menu -->
|
|
6397
|
-
@if (finalEditable() && editor()) {
|
|
6398
|
-
<ate-link-bubble-menu
|
|
6399
|
-
[editor]="editor()!"
|
|
6400
|
-
[style.display]="editorFullyInitialized() ? 'block' : 'none'"></ate-link-bubble-menu>
|
|
6401
|
-
}
|
|
6402
|
-
|
|
6403
|
-
<!-- Color Bubble Menu -->
|
|
6404
|
-
@if (finalEditable() && editor()) {
|
|
6405
|
-
<ate-color-bubble-menu
|
|
6406
|
-
[editor]="editor()!"
|
|
6407
|
-
[style.display]="editorFullyInitialized() ? 'block' : 'none'"></ate-color-bubble-menu>
|
|
6408
|
-
}
|
|
6409
|
-
|
|
6410
|
-
<!-- Slash Commands -->
|
|
6411
|
-
@if (finalEditable() && finalEnableSlashCommands() && editor()) {
|
|
6412
|
-
<ate-slash-commands
|
|
6413
|
-
[editor]="editor()!"
|
|
6414
|
-
[config]="finalSlashCommandsConfig()"
|
|
6415
|
-
[style.display]="editorFullyInitialized() ? 'block' : 'none'"></ate-slash-commands>
|
|
6416
|
-
}
|
|
6417
|
-
|
|
6418
|
-
<!-- Table Menu -->
|
|
6419
|
-
@if (finalEditable() && finalShowTableBubbleMenu() && editor()) {
|
|
6420
|
-
<ate-table-bubble-menu
|
|
6421
|
-
[editor]="editor()!"
|
|
6422
|
-
[config]="finalTableBubbleMenuConfig()"
|
|
6423
|
-
[style.display]="editorFullyInitialized() ? 'block' : 'none'"></ate-table-bubble-menu>
|
|
6424
|
-
}
|
|
6425
|
-
|
|
6426
|
-
<!-- Cell Menu -->
|
|
6427
|
-
@if (finalEditable() && finalShowCellBubbleMenu() && editor()) {
|
|
6428
|
-
<ate-cell-bubble-menu
|
|
6429
|
-
[editor]="editor()!"
|
|
6430
|
-
[config]="finalCellBubbleMenuConfig()"
|
|
6431
|
-
[style.display]="editorFullyInitialized() ? 'block' : 'none'"></ate-cell-bubble-menu>
|
|
6432
|
-
}
|
|
6433
|
-
|
|
6434
|
-
<!-- Counters -->
|
|
6435
|
-
@if (
|
|
6436
|
-
finalEditable() &&
|
|
6437
|
-
!mergedDisabled() &&
|
|
6438
|
-
finalShowFooter() &&
|
|
6439
|
-
(finalShowCharacterCount() || finalShowWordCount())
|
|
6440
|
-
) {
|
|
6441
|
-
<div
|
|
6442
|
-
class="character-count"
|
|
6443
|
-
[class.limit-reached]="finalMaxCharacters() && characterCount() >= finalMaxCharacters()!">
|
|
6444
|
-
@if (finalShowCharacterCount()) {
|
|
6445
|
-
{{ characterCount() }}
|
|
6446
|
-
{{ currentTranslations().editor.character }}{{ characterCount() > 1 ? "s" : "" }}
|
|
6447
|
-
@if (finalMaxCharacters()) {
|
|
6448
|
-
/ {{ finalMaxCharacters() }}
|
|
6449
|
-
}
|
|
6450
|
-
}
|
|
6451
|
-
|
|
6452
|
-
@if (finalShowCharacterCount() && finalShowWordCount()) {
|
|
6453
|
-
,
|
|
6454
|
-
}
|
|
6455
|
-
|
|
6456
|
-
@if (finalShowWordCount()) {
|
|
6457
|
-
{{ wordCount() }}
|
|
6458
|
-
{{ currentTranslations().editor.word }}{{ wordCount() > 1 ? "s" : "" }}
|
|
6459
|
-
}
|
|
6460
|
-
</div>
|
|
6461
|
-
}
|
|
6462
|
-
</div>
|
|
6463
|
-
`, isInline: true, 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"], dependencies: [{ kind: "component", type: AteToolbarComponent, selector: "ate-toolbar", inputs: ["editor", "config", "imageUpload", "floating"] }, { kind: "component", type: AteBubbleMenuComponent, selector: "ate-bubble-menu", inputs: ["config"] }, { kind: "component", type: AteImageBubbleMenuComponent, selector: "ate-image-bubble-menu", inputs: ["config", "imageUpload"] }, { kind: "component", type: AteTableBubbleMenuComponent, selector: "ate-table-bubble-menu", inputs: ["config"] }, { kind: "component", type: AteCellBubbleMenuComponent, selector: "ate-cell-bubble-menu", inputs: ["config"] }, { kind: "component", type: AteSlashCommandsComponent, selector: "ate-slash-commands", inputs: ["editor", "config"] }, { kind: "component", type: AteLinkBubbleMenuComponent, selector: "ate-link-bubble-menu" }, { kind: "component", type: AteColorBubbleMenuComponent, selector: "ate-color-bubble-menu" }, { kind: "component", type: AteEditToggleComponent, selector: "ate-edit-toggle", inputs: ["editable", "translations"], outputs: ["editToggle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
6464
|
-
}
|
|
6465
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AngularTiptapEditorComponent, decorators: [{
|
|
6466
|
-
type: Component,
|
|
6467
|
-
args: [{ selector: "angular-tiptap-editor", standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, hostDirectives: [AteNoopValueAccessorDirective], host: {
|
|
6468
|
-
"[class.fill-container]": "finalFillContainer()",
|
|
6469
|
-
"[class.floating-toolbar]": "finalFloatingToolbar()",
|
|
6470
|
-
"[class.is-readonly]": "!finalEditable() && !mergedDisabled()",
|
|
6471
|
-
"[class.is-disabled]": "mergedDisabled()",
|
|
6472
|
-
"[style.--ate-border-width]": "finalSeamless() || mergedDisabled() ? '0' : null",
|
|
6473
|
-
"[style.--ate-background]": "finalSeamless() ? 'transparent' : (mergedDisabled() ? 'var(--ate-surface-tertiary)' : null)",
|
|
6474
|
-
"[style.--ate-toolbar-border-color]": "finalSeamless() ? 'transparent' : null",
|
|
6475
|
-
"[style.--ate-counter-background]": "finalSeamless() ? 'transparent' : null",
|
|
6476
|
-
"[style.--ate-counter-border-color]": "finalSeamless() ? 'transparent' : null",
|
|
6477
|
-
"[class.dark]": "config().theme === 'dark'",
|
|
6478
|
-
"[attr.data-theme]": "config().theme",
|
|
6479
|
-
}, imports: [
|
|
6480
|
-
AteToolbarComponent,
|
|
6481
|
-
AteBubbleMenuComponent,
|
|
6482
|
-
AteImageBubbleMenuComponent,
|
|
6483
|
-
AteTableBubbleMenuComponent,
|
|
6484
|
-
AteCellBubbleMenuComponent,
|
|
6485
|
-
AteSlashCommandsComponent,
|
|
6486
|
-
AteLinkBubbleMenuComponent,
|
|
6487
|
-
AteColorBubbleMenuComponent,
|
|
6488
|
-
AteEditToggleComponent,
|
|
6489
|
-
], providers: [AteEditorCommandsService, AteImageService, AteColorPickerService, AteLinkService], template: `
|
|
6745
|
+
return this.editor()?.getHTML() || "";
|
|
6746
|
+
}
|
|
6747
|
+
getJSON() {
|
|
6748
|
+
return this.editor()?.getJSON();
|
|
6749
|
+
}
|
|
6750
|
+
getText() {
|
|
6751
|
+
return this.editor()?.getText() || "";
|
|
6752
|
+
}
|
|
6753
|
+
setContent(content, emitUpdate = true) {
|
|
6754
|
+
const editor = this.editor();
|
|
6755
|
+
if (editor) {
|
|
6756
|
+
this.editorCommandsService.setContent(editor, content, emitUpdate);
|
|
6757
|
+
}
|
|
6758
|
+
}
|
|
6759
|
+
focus() {
|
|
6760
|
+
const editor = this.editor();
|
|
6761
|
+
if (editor) {
|
|
6762
|
+
this.editorCommandsService.focus(editor);
|
|
6763
|
+
}
|
|
6764
|
+
}
|
|
6765
|
+
blur() {
|
|
6766
|
+
const editor = this.editor();
|
|
6767
|
+
if (editor) {
|
|
6768
|
+
this.editorCommandsService.blur(editor);
|
|
6769
|
+
}
|
|
6770
|
+
}
|
|
6771
|
+
clearContent() {
|
|
6772
|
+
const editor = this.editor();
|
|
6773
|
+
if (editor) {
|
|
6774
|
+
this.editorCommandsService.clearContent(editor);
|
|
6775
|
+
}
|
|
6776
|
+
}
|
|
6777
|
+
// Méthode publique pour obtenir l'éditeur
|
|
6778
|
+
getEditor() {
|
|
6779
|
+
return this.editor();
|
|
6780
|
+
}
|
|
6781
|
+
setupFormControlSubscription() {
|
|
6782
|
+
const control = this.ngControl?.control;
|
|
6783
|
+
if (control) {
|
|
6784
|
+
// Synchronize form control value with editor content
|
|
6785
|
+
const formValue$ = concat(defer(() => of(control.value)), control.valueChanges);
|
|
6786
|
+
formValue$
|
|
6787
|
+
.pipe(tap((value) => {
|
|
6788
|
+
const editor = this.editor();
|
|
6789
|
+
if (editor) {
|
|
6790
|
+
this.setContent(value, false);
|
|
6791
|
+
}
|
|
6792
|
+
}), takeUntilDestroyed(this._destroyRef))
|
|
6793
|
+
.subscribe();
|
|
6794
|
+
// Synchronize form control status with editor disabled state
|
|
6795
|
+
const formStatus$ = concat(defer(() => of(control.status)), control.statusChanges);
|
|
6796
|
+
formStatus$
|
|
6797
|
+
.pipe(tap((status) => {
|
|
6798
|
+
this._isFormControlDisabled.set(status === "DISABLED");
|
|
6799
|
+
}), takeUntilDestroyed(this._destroyRef))
|
|
6800
|
+
.subscribe();
|
|
6801
|
+
}
|
|
6802
|
+
}
|
|
6803
|
+
onEditorClick(event) {
|
|
6804
|
+
const editor = this.editor();
|
|
6805
|
+
if (!editor || !this.finalEditable()) {
|
|
6806
|
+
return;
|
|
6807
|
+
}
|
|
6808
|
+
// Verify if interaction is on the container element and not on the content
|
|
6809
|
+
const target = event.target;
|
|
6810
|
+
const editorElement = this.editorElement()?.nativeElement;
|
|
6811
|
+
if (target === editorElement || target.classList.contains("ate-content")) {
|
|
6812
|
+
// Interaction in the empty space, position the cursor at the end
|
|
6813
|
+
setTimeout(() => {
|
|
6814
|
+
const { doc } = editor.state;
|
|
6815
|
+
const endPos = doc.content.size;
|
|
6816
|
+
editor.commands.setTextSelection(endPos);
|
|
6817
|
+
editor.commands.focus();
|
|
6818
|
+
}, 0);
|
|
6819
|
+
}
|
|
6820
|
+
}
|
|
6821
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AngularTiptapEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
6822
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AngularTiptapEditorComponent, isStandalone: true, selector: "angular-tiptap-editor", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, content: { classPropertyName: "content", publicName: "content", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, minHeight: { classPropertyName: "minHeight", publicName: "minHeight", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, fillContainer: { classPropertyName: "fillContainer", publicName: "fillContainer", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, showFooter: { classPropertyName: "showFooter", publicName: "showFooter", isSignal: true, isRequired: false, transformFunction: null }, showCharacterCount: { classPropertyName: "showCharacterCount", publicName: "showCharacterCount", isSignal: true, isRequired: false, transformFunction: null }, showWordCount: { classPropertyName: "showWordCount", publicName: "showWordCount", isSignal: true, isRequired: false, transformFunction: null }, maxCharacters: { classPropertyName: "maxCharacters", publicName: "maxCharacters", isSignal: true, isRequired: false, transformFunction: null }, enableOfficePaste: { classPropertyName: "enableOfficePaste", publicName: "enableOfficePaste", isSignal: true, isRequired: false, transformFunction: null }, enableSlashCommands: { classPropertyName: "enableSlashCommands", publicName: "enableSlashCommands", isSignal: true, isRequired: false, transformFunction: null }, slashCommands: { classPropertyName: "slashCommands", publicName: "slashCommands", isSignal: true, isRequired: false, transformFunction: null }, customSlashCommands: { classPropertyName: "customSlashCommands", publicName: "customSlashCommands", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, autofocus: { classPropertyName: "autofocus", publicName: "autofocus", isSignal: true, isRequired: false, transformFunction: null }, seamless: { classPropertyName: "seamless", publicName: "seamless", isSignal: true, isRequired: false, transformFunction: null }, floatingToolbar: { classPropertyName: "floatingToolbar", publicName: "floatingToolbar", isSignal: true, isRequired: false, transformFunction: null }, showEditToggle: { classPropertyName: "showEditToggle", publicName: "showEditToggle", isSignal: true, isRequired: false, transformFunction: null }, spellcheck: { classPropertyName: "spellcheck", publicName: "spellcheck", isSignal: true, isRequired: false, transformFunction: null }, tiptapExtensions: { classPropertyName: "tiptapExtensions", publicName: "tiptapExtensions", isSignal: true, isRequired: false, transformFunction: null }, tiptapOptions: { classPropertyName: "tiptapOptions", publicName: "tiptapOptions", isSignal: true, isRequired: false, transformFunction: null }, showBubbleMenu: { classPropertyName: "showBubbleMenu", publicName: "showBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, bubbleMenu: { classPropertyName: "bubbleMenu", publicName: "bubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, showImageBubbleMenu: { classPropertyName: "showImageBubbleMenu", publicName: "showImageBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, imageBubbleMenu: { classPropertyName: "imageBubbleMenu", publicName: "imageBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, toolbar: { classPropertyName: "toolbar", publicName: "toolbar", isSignal: true, isRequired: false, transformFunction: null }, showTableBubbleMenu: { classPropertyName: "showTableBubbleMenu", publicName: "showTableBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, tableBubbleMenu: { classPropertyName: "tableBubbleMenu", publicName: "tableBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, showCellBubbleMenu: { classPropertyName: "showCellBubbleMenu", publicName: "showCellBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, cellBubbleMenu: { classPropertyName: "cellBubbleMenu", publicName: "cellBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, stateCalculators: { classPropertyName: "stateCalculators", publicName: "stateCalculators", isSignal: true, isRequired: false, transformFunction: null }, imageUpload: { classPropertyName: "imageUpload", publicName: "imageUpload", isSignal: true, isRequired: false, transformFunction: null }, imageUploadHandler: { classPropertyName: "imageUploadHandler", publicName: "imageUploadHandler", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { contentChange: "contentChange", editorCreated: "editorCreated", editorUpdate: "editorUpdate", editorFocus: "editorFocus", editorBlur: "editorBlur", editableChange: "editableChange" }, host: { properties: { "class.fill-container": "finalFillContainer()", "class.floating-toolbar": "finalFloatingToolbar()", "class.is-readonly": "!finalEditable() && !mergedDisabled()", "class.is-disabled": "mergedDisabled()", "style.--ate-border-width": "finalSeamless() || mergedDisabled() ? '0' : null", "style.--ate-background": "finalSeamless() ? 'transparent' : (mergedDisabled() ? 'var(--ate-surface-tertiary)' : null)", "style.--ate-toolbar-border-color": "finalSeamless() ? 'transparent' : null", "style.--ate-counter-background": "finalSeamless() ? 'transparent' : null", "style.--ate-counter-border-color": "finalSeamless() ? 'transparent' : null", "class.dark": "config().theme === 'dark'", "attr.data-theme": "config().theme" } }, providers: [AteEditorCommandsService, AteImageService, AteColorPickerService, AteLinkService], viewQueries: [{ propertyName: "editorElement", first: true, predicate: ["editorElement"], descendants: true, isSignal: true }], hostDirectives: [{ directive: AteNoopValueAccessorDirective }], ngImport: i0, template: `
|
|
6490
6823
|
<div class="ate-editor">
|
|
6491
6824
|
<!-- Toolbar -->
|
|
6492
6825
|
@if (finalEditable() && !mergedDisabled() && finalShowToolbar() && editor()) {
|
|
@@ -6600,498 +6933,276 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
6600
6933
|
@if (finalShowWordCount()) {
|
|
6601
6934
|
{{ wordCount() }}
|
|
6602
6935
|
{{ 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
|
-
* Clés des boutons de la barre d'outils native
|
|
6612
|
-
*/
|
|
6613
|
-
const ATE_TOOLBAR_KEYS = [
|
|
6614
|
-
"bold",
|
|
6615
|
-
"italic",
|
|
6616
|
-
"underline",
|
|
6617
|
-
"strike",
|
|
6618
|
-
"code",
|
|
6619
|
-
"codeBlock",
|
|
6620
|
-
"superscript",
|
|
6621
|
-
"subscript",
|
|
6622
|
-
"highlight",
|
|
6623
|
-
"highlightPicker",
|
|
6624
|
-
"heading1",
|
|
6625
|
-
"heading2",
|
|
6626
|
-
"heading3",
|
|
6627
|
-
"bulletList",
|
|
6628
|
-
"orderedList",
|
|
6629
|
-
"blockquote",
|
|
6630
|
-
"alignLeft",
|
|
6631
|
-
"alignCenter",
|
|
6632
|
-
"alignRight",
|
|
6633
|
-
"alignJustify",
|
|
6634
|
-
"link",
|
|
6635
|
-
"image",
|
|
6636
|
-
"horizontalRule",
|
|
6637
|
-
"table",
|
|
6638
|
-
"undo",
|
|
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 }); }
|
|
6936
|
+
}
|
|
6937
|
+
</div>
|
|
6938
|
+
}
|
|
6939
|
+
</div>
|
|
6940
|
+
`, isInline: true, 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"], dependencies: [{ kind: "component", type: AteToolbarComponent, selector: "ate-toolbar", inputs: ["editor", "config", "imageUpload", "floating"] }, { kind: "component", type: AteBubbleMenuComponent, selector: "ate-bubble-menu", inputs: ["config"] }, { kind: "component", type: AteImageBubbleMenuComponent, selector: "ate-image-bubble-menu", inputs: ["config", "imageUpload"] }, { kind: "component", type: AteTableBubbleMenuComponent, selector: "ate-table-bubble-menu", inputs: ["config"] }, { kind: "component", type: AteCellBubbleMenuComponent, selector: "ate-cell-bubble-menu", inputs: ["config"] }, { kind: "component", type: AteSlashCommandsComponent, selector: "ate-slash-commands", inputs: ["editor", "config"] }, { kind: "component", type: AteLinkBubbleMenuComponent, selector: "ate-link-bubble-menu" }, { kind: "component", type: AteColorBubbleMenuComponent, selector: "ate-color-bubble-menu" }, { kind: "component", type: AteEditToggleComponent, selector: "ate-edit-toggle", inputs: ["editable", "translations"], outputs: ["editToggle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
6783
6941
|
}
|
|
6784
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type:
|
|
6785
|
-
type:
|
|
6786
|
-
|
|
6942
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AngularTiptapEditorComponent, decorators: [{
|
|
6943
|
+
type: Component,
|
|
6944
|
+
args: [{ selector: "angular-tiptap-editor", standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, hostDirectives: [AteNoopValueAccessorDirective], host: {
|
|
6945
|
+
"[class.fill-container]": "finalFillContainer()",
|
|
6946
|
+
"[class.floating-toolbar]": "finalFloatingToolbar()",
|
|
6947
|
+
"[class.is-readonly]": "!finalEditable() && !mergedDisabled()",
|
|
6948
|
+
"[class.is-disabled]": "mergedDisabled()",
|
|
6949
|
+
"[style.--ate-border-width]": "finalSeamless() || mergedDisabled() ? '0' : null",
|
|
6950
|
+
"[style.--ate-background]": "finalSeamless() ? 'transparent' : (mergedDisabled() ? 'var(--ate-surface-tertiary)' : null)",
|
|
6951
|
+
"[style.--ate-toolbar-border-color]": "finalSeamless() ? 'transparent' : null",
|
|
6952
|
+
"[style.--ate-counter-background]": "finalSeamless() ? 'transparent' : null",
|
|
6953
|
+
"[style.--ate-counter-border-color]": "finalSeamless() ? 'transparent' : null",
|
|
6954
|
+
"[class.dark]": "config().theme === 'dark'",
|
|
6955
|
+
"[attr.data-theme]": "config().theme",
|
|
6956
|
+
}, imports: [
|
|
6957
|
+
AteToolbarComponent,
|
|
6958
|
+
AteBubbleMenuComponent,
|
|
6959
|
+
AteImageBubbleMenuComponent,
|
|
6960
|
+
AteTableBubbleMenuComponent,
|
|
6961
|
+
AteCellBubbleMenuComponent,
|
|
6962
|
+
AteSlashCommandsComponent,
|
|
6963
|
+
AteLinkBubbleMenuComponent,
|
|
6964
|
+
AteColorBubbleMenuComponent,
|
|
6965
|
+
AteEditToggleComponent,
|
|
6966
|
+
], providers: [AteEditorCommandsService, AteImageService, AteColorPickerService, AteLinkService], template: `
|
|
6967
|
+
<div class="ate-editor">
|
|
6968
|
+
<!-- Toolbar -->
|
|
6969
|
+
@if (finalEditable() && !mergedDisabled() && finalShowToolbar() && editor()) {
|
|
6970
|
+
<ate-toolbar
|
|
6971
|
+
[editor]="editor()!"
|
|
6972
|
+
[config]="finalToolbarConfig()"
|
|
6973
|
+
[imageUpload]="finalImageUploadConfig()"
|
|
6974
|
+
[floating]="finalFloatingToolbar()"
|
|
6975
|
+
(mouseenter)="hideBubbleMenus()"
|
|
6976
|
+
(mouseleave)="showBubbleMenus()" />
|
|
6977
|
+
}
|
|
6787
6978
|
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
instance._updateNodeView(updatedNode, updatedDecorations);
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
|
|
6921
|
-
|
|
6922
|
-
|
|
6923
|
-
|
|
6924
|
-
|
|
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
|
-
};
|
|
6979
|
+
@if (finalShowEditToggle() && !mergedDisabled()) {
|
|
6980
|
+
<ate-edit-toggle
|
|
6981
|
+
[editable]="finalEditable()"
|
|
6982
|
+
[translations]="currentTranslations()"
|
|
6983
|
+
(editToggle)="toggleEditMode($event)" />
|
|
6984
|
+
}
|
|
6985
|
+
|
|
6986
|
+
<!-- Editor Content -->
|
|
6987
|
+
<div
|
|
6988
|
+
#editorElement
|
|
6989
|
+
class="ate-content"
|
|
6990
|
+
[class.drag-over]="isDragOver()"
|
|
6991
|
+
(dragover)="onDragOver($event)"
|
|
6992
|
+
(drop)="onDrop($event)"
|
|
6993
|
+
(click)="onEditorClick($event)"
|
|
6994
|
+
(keydown.enter)="onEditorClick($event)"
|
|
6995
|
+
(keydown.space)="onEditorClick($event)"
|
|
6996
|
+
tabindex="0"
|
|
6997
|
+
role="application"
|
|
6998
|
+
[attr.aria-label]="currentTranslations().editor.placeholder"></div>
|
|
6999
|
+
|
|
7000
|
+
<!-- Text Bubble Menu -->
|
|
7001
|
+
@if (finalEditable() && finalShowBubbleMenu() && editor()) {
|
|
7002
|
+
<ate-bubble-menu
|
|
7003
|
+
[editor]="editor()!"
|
|
7004
|
+
[config]="finalBubbleMenuConfig()"
|
|
7005
|
+
[style.display]="editorFullyInitialized() ? 'block' : 'none'"></ate-bubble-menu>
|
|
7006
|
+
}
|
|
7007
|
+
|
|
7008
|
+
<!-- Image Bubble Menu -->
|
|
7009
|
+
@if (finalEditable() && finalShowImageBubbleMenu() && editor()) {
|
|
7010
|
+
<ate-image-bubble-menu
|
|
7011
|
+
[editor]="editor()!"
|
|
7012
|
+
[config]="finalImageBubbleMenuConfig()"
|
|
7013
|
+
[imageUpload]="finalImageUploadConfig()"
|
|
7014
|
+
[style.display]="editorFullyInitialized() ? 'block' : 'none'"></ate-image-bubble-menu>
|
|
7015
|
+
}
|
|
7016
|
+
|
|
7017
|
+
<!-- Link Bubble Menu -->
|
|
7018
|
+
@if (finalEditable() && editor()) {
|
|
7019
|
+
<ate-link-bubble-menu
|
|
7020
|
+
[editor]="editor()!"
|
|
7021
|
+
[style.display]="editorFullyInitialized() ? 'block' : 'none'"></ate-link-bubble-menu>
|
|
7022
|
+
}
|
|
7023
|
+
|
|
7024
|
+
<!-- Color Bubble Menu -->
|
|
7025
|
+
@if (finalEditable() && editor()) {
|
|
7026
|
+
<ate-color-bubble-menu
|
|
7027
|
+
[editor]="editor()!"
|
|
7028
|
+
[style.display]="editorFullyInitialized() ? 'block' : 'none'"></ate-color-bubble-menu>
|
|
7029
|
+
}
|
|
7030
|
+
|
|
7031
|
+
<!-- Slash Commands -->
|
|
7032
|
+
@if (finalEditable() && finalEnableSlashCommands() && editor()) {
|
|
7033
|
+
<ate-slash-commands
|
|
7034
|
+
[editor]="editor()!"
|
|
7035
|
+
[config]="finalSlashCommandsConfig()"
|
|
7036
|
+
[style.display]="editorFullyInitialized() ? 'block' : 'none'"></ate-slash-commands>
|
|
7037
|
+
}
|
|
7038
|
+
|
|
7039
|
+
<!-- Table Menu -->
|
|
7040
|
+
@if (finalEditable() && finalShowTableBubbleMenu() && editor()) {
|
|
7041
|
+
<ate-table-bubble-menu
|
|
7042
|
+
[editor]="editor()!"
|
|
7043
|
+
[config]="finalTableBubbleMenuConfig()"
|
|
7044
|
+
[style.display]="editorFullyInitialized() ? 'block' : 'none'"></ate-table-bubble-menu>
|
|
7045
|
+
}
|
|
7046
|
+
|
|
7047
|
+
<!-- Cell Menu -->
|
|
7048
|
+
@if (finalEditable() && finalShowCellBubbleMenu() && editor()) {
|
|
7049
|
+
<ate-cell-bubble-menu
|
|
7050
|
+
[editor]="editor()!"
|
|
7051
|
+
[config]="finalCellBubbleMenuConfig()"
|
|
7052
|
+
[style.display]="editorFullyInitialized() ? 'block' : 'none'"></ate-cell-bubble-menu>
|
|
7053
|
+
}
|
|
7054
|
+
|
|
7055
|
+
<!-- Counters -->
|
|
7056
|
+
@if (
|
|
7057
|
+
finalEditable() &&
|
|
7058
|
+
!mergedDisabled() &&
|
|
7059
|
+
finalShowFooter() &&
|
|
7060
|
+
(finalShowCharacterCount() || finalShowWordCount())
|
|
7061
|
+
) {
|
|
7062
|
+
<div
|
|
7063
|
+
class="character-count"
|
|
7064
|
+
[class.limit-reached]="finalMaxCharacters() && characterCount() >= finalMaxCharacters()!">
|
|
7065
|
+
@if (finalShowCharacterCount()) {
|
|
7066
|
+
{{ characterCount() }}
|
|
7067
|
+
{{ currentTranslations().editor.character }}{{ characterCount() > 1 ? "s" : "" }}
|
|
7068
|
+
@if (finalMaxCharacters()) {
|
|
7069
|
+
/ {{ finalMaxCharacters() }}
|
|
7070
|
+
}
|
|
7071
|
+
}
|
|
7072
|
+
|
|
7073
|
+
@if (finalShowCharacterCount() && finalShowWordCount()) {
|
|
7074
|
+
,
|
|
7075
|
+
}
|
|
7076
|
+
|
|
7077
|
+
@if (finalShowWordCount()) {
|
|
7078
|
+
{{ wordCount() }}
|
|
7079
|
+
{{ currentTranslations().editor.word }}{{ wordCount() > 1 ? "s" : "" }}
|
|
7080
|
+
}
|
|
7081
|
+
</div>
|
|
7082
|
+
}
|
|
7083
|
+
</div>
|
|
7084
|
+
`, 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"] }]
|
|
7085
|
+
}], 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 }] }] } });
|
|
7086
|
+
|
|
7087
|
+
/**
|
|
7088
|
+
* Provides the necessary configuration and initialization for the Angular TipTap Editor.
|
|
7089
|
+
* This should be called in your app.config.ts (for standalone) or main.ts.
|
|
7090
|
+
*
|
|
7091
|
+
* This provider is essential for 'Angular Nodes' to work correctly, as it captures the
|
|
7092
|
+
* root Injector required to instantiate custom Angular components within the editor.
|
|
7093
|
+
* @example
|
|
7094
|
+
* ```ts
|
|
7095
|
+
* bootstrapApplication(AppComponent, {
|
|
7096
|
+
* providers: [
|
|
7097
|
+
* provideAteEditor({
|
|
7098
|
+
* theme: 'dark',
|
|
7099
|
+
* mode: 'seamless'
|
|
7100
|
+
* })
|
|
7101
|
+
* ]
|
|
7102
|
+
* });
|
|
7103
|
+
* ```
|
|
7104
|
+
*/
|
|
7105
|
+
function provideAteEditor(config) {
|
|
7106
|
+
return makeEnvironmentProviders([
|
|
7107
|
+
{
|
|
7108
|
+
provide: ATE_GLOBAL_CONFIG,
|
|
7109
|
+
useValue: config || {},
|
|
7110
|
+
},
|
|
7111
|
+
provideEnvironmentInitializer(() => {
|
|
7112
|
+
const injector = inject(Injector);
|
|
7113
|
+
setGlobalInjector(injector);
|
|
7114
|
+
}),
|
|
7115
|
+
]);
|
|
6937
7116
|
}
|
|
6938
7117
|
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
"
|
|
7118
|
+
/**
|
|
7119
|
+
* Clés des boutons de la barre d'outils native
|
|
7120
|
+
*/
|
|
7121
|
+
const ATE_TOOLBAR_KEYS = [
|
|
7122
|
+
"bold",
|
|
7123
|
+
"italic",
|
|
7124
|
+
"underline",
|
|
7125
|
+
"strike",
|
|
7126
|
+
"code",
|
|
7127
|
+
"codeBlock",
|
|
7128
|
+
"superscript",
|
|
7129
|
+
"subscript",
|
|
7130
|
+
"highlight",
|
|
7131
|
+
"highlightPicker",
|
|
7132
|
+
"heading1",
|
|
7133
|
+
"heading2",
|
|
7134
|
+
"heading3",
|
|
6944
7135
|
"bulletList",
|
|
6945
7136
|
"orderedList",
|
|
6946
|
-
"listItem",
|
|
6947
7137
|
"blockquote",
|
|
6948
|
-
"
|
|
6949
|
-
"
|
|
7138
|
+
"alignLeft",
|
|
7139
|
+
"alignCenter",
|
|
7140
|
+
"alignRight",
|
|
7141
|
+
"alignJustify",
|
|
7142
|
+
"link",
|
|
7143
|
+
"image",
|
|
6950
7144
|
"horizontalRule",
|
|
7145
|
+
"table",
|
|
7146
|
+
"undo",
|
|
7147
|
+
"redo",
|
|
7148
|
+
"clear",
|
|
7149
|
+
"textColor",
|
|
7150
|
+
"separator",
|
|
6951
7151
|
];
|
|
7152
|
+
|
|
6952
7153
|
/**
|
|
6953
|
-
*
|
|
7154
|
+
* Clés des options du menu bulle de texte
|
|
6954
7155
|
*/
|
|
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
|
-
}
|
|
7156
|
+
const ATE_BUBBLE_MENU_KEYS = [
|
|
7157
|
+
"bold",
|
|
7158
|
+
"italic",
|
|
7159
|
+
"underline",
|
|
7160
|
+
"strike",
|
|
7161
|
+
"code",
|
|
7162
|
+
"superscript",
|
|
7163
|
+
"subscript",
|
|
7164
|
+
"highlight",
|
|
7165
|
+
"highlightPicker",
|
|
7166
|
+
"textColor",
|
|
7167
|
+
"link",
|
|
7168
|
+
"separator",
|
|
7169
|
+
];
|
|
6972
7170
|
/**
|
|
6973
|
-
*
|
|
7171
|
+
* Clés des options du menu bulle d'image
|
|
6974
7172
|
*/
|
|
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
|
-
}
|
|
7173
|
+
const ATE_IMAGE_BUBBLE_MENU_KEYS = [
|
|
7174
|
+
"changeImage",
|
|
7175
|
+
"resizeSmall",
|
|
7176
|
+
"resizeMedium",
|
|
7177
|
+
"resizeLarge",
|
|
7178
|
+
"resizeOriginal",
|
|
7179
|
+
"deleteImage",
|
|
7180
|
+
"separator",
|
|
7181
|
+
];
|
|
7004
7182
|
/**
|
|
7005
|
-
*
|
|
7006
|
-
* Supports both "TipTap-Aware" and "Standard" modes.
|
|
7007
|
-
*
|
|
7008
|
-
* @internal
|
|
7183
|
+
* Clés des options du menu de table
|
|
7009
7184
|
*/
|
|
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
|
-
|
|
7185
|
+
const ATE_TABLE_BUBBLE_MENU_KEYS = [
|
|
7186
|
+
"addRowBefore",
|
|
7187
|
+
"addRowAfter",
|
|
7188
|
+
"deleteRow",
|
|
7189
|
+
"addColumnBefore",
|
|
7190
|
+
"addColumnAfter",
|
|
7191
|
+
"deleteColumn",
|
|
7192
|
+
"deleteTable",
|
|
7193
|
+
"toggleHeaderRow",
|
|
7194
|
+
"toggleHeaderColumn",
|
|
7195
|
+
"separator",
|
|
7196
|
+
];
|
|
7061
7197
|
/**
|
|
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
|
-
* ```
|
|
7198
|
+
* Clés des options du menu de cellule
|
|
7086
7199
|
*/
|
|
7087
|
-
|
|
7088
|
-
return createAngularComponentExtension(injector, options);
|
|
7089
|
-
}
|
|
7200
|
+
const ATE_CELL_BUBBLE_MENU_KEYS = ["mergeCells", "splitCell"];
|
|
7090
7201
|
|
|
7091
7202
|
/*
|
|
7092
7203
|
* Public API Surface of tiptap-editor
|
|
7093
7204
|
*/
|
|
7094
|
-
// Main component
|
|
7205
|
+
// Main component & Provider
|
|
7095
7206
|
/** @deprecated Renamed to `ATE_INITIAL_EDITOR_STATE`. This alias will be removed in v3.0.0. */
|
|
7096
7207
|
const INITIAL_EDITOR_STATE = ATE_INITIAL_EDITOR_STATE;
|
|
7097
7208
|
/** @deprecated Renamed to `ATE_SLASH_COMMAND_KEYS`. This alias will be removed in v3.0.0. */
|
|
@@ -7125,5 +7236,5 @@ const DEFAULT_CELL_MENU_CONFIG = ATE_DEFAULT_CELL_MENU_CONFIG;
|
|
|
7125
7236
|
* Generated bundle index. Do not edit.
|
|
7126
7237
|
*/
|
|
7127
7238
|
|
|
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, AteColorPickerService as ColorPickerService, DEFAULT_BUBBLE_MENU_CONFIG, DEFAULT_CELL_MENU_CONFIG, DEFAULT_IMAGE_BUBBLE_MENU_CONFIG, DEFAULT_SLASH_COMMANDS_CONFIG, DEFAULT_TABLE_MENU_CONFIG, DEFAULT_TOOLBAR_CONFIG, DiscoveryCalculator, AteEditorCommandsService as EditorCommandsService, INITIAL_EDITOR_STATE, ImageCalculator, AteImageService as ImageService, AteLinkService as LinkService, MarksCalculator, SLASH_COMMAND_KEYS, SelectionCalculator, StructureCalculator, TableCalculator, AteI18nService as TiptapI18nService, createAngularComponentExtension, createDefaultSlashCommands, filterSlashCommands, registerAngularComponent };
|
|
7239
|
+
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_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, AteColorPickerService as ColorPickerService, DEFAULT_BUBBLE_MENU_CONFIG, DEFAULT_CELL_MENU_CONFIG, DEFAULT_IMAGE_BUBBLE_MENU_CONFIG, DEFAULT_SLASH_COMMANDS_CONFIG, DEFAULT_TABLE_MENU_CONFIG, DEFAULT_TOOLBAR_CONFIG, DiscoveryCalculator, AteEditorCommandsService as EditorCommandsService, INITIAL_EDITOR_STATE, ImageCalculator, AteImageService as ImageService, AteLinkService as LinkService, MarksCalculator, SLASH_COMMAND_KEYS, SelectionCalculator, StructureCalculator, TableCalculator, AteI18nService as TiptapI18nService, createAngularComponentExtension, createDefaultSlashCommands, filterSlashCommands, provideAteEditor, registerAngularComponent };
|
|
7129
7240
|
//# sourceMappingURL=flogeez-angular-tiptap-editor.mjs.map
|