@flogeez/angular-tiptap-editor 2.2.3 → 2.3.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
CHANGED
|
@@ -5,6 +5,16 @@ All notable changes to `@flogeez/angular-tiptap-editor` will be documented in th
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html), with the exception that the major version is specifically aligned with the major version of [Tiptap](https://tiptap.dev).
|
|
7
7
|
|
|
8
|
+
## [2.3.0] - 2026-01-28
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Advanced Angular Component Engine**: A powerful, unified engine to embed any Angular component directly into the editor as a custom TipTap node.
|
|
13
|
+
- **Universal Integration**: Seamlessly register both standard library components and TipTap-aware interactive components.
|
|
14
|
+
- **Modern Reactive Support**: Full out-of-the-box compatibility with Angular 18+ Signal-based `input()` and `output()`.
|
|
15
|
+
- **Editable Content Zones**: Turn any part of your Angular component into a nested, editable rich-text area.
|
|
16
|
+
- **Robustness**: Build-in protection against naming collisions and reserved TipTap nodes.
|
|
17
|
+
|
|
8
18
|
## [2.2.3] - 2026-01-27
|
|
9
19
|
|
|
10
20
|
### Added
|
|
@@ -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, untracked } from '@angular/core';
|
|
2
|
+
import { input, output, ChangeDetectionStrategy, Component, signal, computed, Injectable, inject, viewChild, effect, Directive, DestroyRef, untracked, ApplicationRef, EnvironmentInjector, createComponent } 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 } from 'rxjs';
|
|
23
|
+
import { isObservable, firstValueFrom, concat, defer, of, tap, Subscription } 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';
|
|
@@ -6053,6 +6053,23 @@ class AngularTiptapEditorComponent {
|
|
|
6053
6053
|
}
|
|
6054
6054
|
}
|
|
6055
6055
|
});
|
|
6056
|
+
// Effect to re-initialize editor when extensions or options change
|
|
6057
|
+
effect(() => {
|
|
6058
|
+
// Monitor extensions and options
|
|
6059
|
+
this.tiptapExtensions();
|
|
6060
|
+
this.tiptapOptions();
|
|
6061
|
+
untracked(() => {
|
|
6062
|
+
// Only if already initialized (post AfterViewInit)
|
|
6063
|
+
if (this.editorFullyInitialized()) {
|
|
6064
|
+
const currentEditor = this.editor();
|
|
6065
|
+
if (currentEditor) {
|
|
6066
|
+
currentEditor.destroy();
|
|
6067
|
+
this._editorFullyInitialized.set(false);
|
|
6068
|
+
this.initEditor();
|
|
6069
|
+
}
|
|
6070
|
+
}
|
|
6071
|
+
});
|
|
6072
|
+
});
|
|
6056
6073
|
}
|
|
6057
6074
|
ngAfterViewInit() {
|
|
6058
6075
|
// La vue est déjà complètement initialisée dans ngAfterViewInit
|
|
@@ -6674,6 +6691,403 @@ const ATE_TABLE_BUBBLE_MENU_KEYS = [
|
|
|
6674
6691
|
*/
|
|
6675
6692
|
const ATE_CELL_BUBBLE_MENU_KEYS = ["mergeCells", "splitCell"];
|
|
6676
6693
|
|
|
6694
|
+
/**
|
|
6695
|
+
* Base abstract class for Angular components used as TipTap NodeViews.
|
|
6696
|
+
*
|
|
6697
|
+
* Extend this class in your custom components to get access to the TipTap node properties.
|
|
6698
|
+
*
|
|
6699
|
+
* @example
|
|
6700
|
+
* ```typescript
|
|
6701
|
+
* @Component({
|
|
6702
|
+
* selector: 'app-counter',
|
|
6703
|
+
* template: `
|
|
6704
|
+
* <div>
|
|
6705
|
+
* <button (click)="increment()">Count: {{ node().attrs['count'] }}</button>
|
|
6706
|
+
* </div>
|
|
6707
|
+
* `
|
|
6708
|
+
* })
|
|
6709
|
+
* export class CounterComponent extends AteAngularNodeView {
|
|
6710
|
+
* increment() {
|
|
6711
|
+
* const count = this.node().attrs['count'] || 0;
|
|
6712
|
+
* this.updateAttributes({ count: count + 1 });
|
|
6713
|
+
* }
|
|
6714
|
+
* }
|
|
6715
|
+
* ```
|
|
6716
|
+
*/
|
|
6717
|
+
class AteAngularNodeView {
|
|
6718
|
+
/**
|
|
6719
|
+
* Internal method to initialize the component with NodeView props.
|
|
6720
|
+
* This is called by the AngularNodeViewRenderer.
|
|
6721
|
+
*
|
|
6722
|
+
* @internal
|
|
6723
|
+
*/
|
|
6724
|
+
_initNodeView(props) {
|
|
6725
|
+
// Create signals from the props
|
|
6726
|
+
const editorSignal = signal(props.editor, ...(ngDevMode ? [{ debugName: "editorSignal" }] : []));
|
|
6727
|
+
const nodeSignal = signal(props.node, ...(ngDevMode ? [{ debugName: "nodeSignal" }] : []));
|
|
6728
|
+
const decorationsSignal = signal(props.decorations, ...(ngDevMode ? [{ debugName: "decorationsSignal" }] : []));
|
|
6729
|
+
const selectedSignal = signal(props.selected, ...(ngDevMode ? [{ debugName: "selectedSignal" }] : []));
|
|
6730
|
+
const extensionSignal = signal(props.extension, ...(ngDevMode ? [{ debugName: "extensionSignal" }] : []));
|
|
6731
|
+
// Assign to the component
|
|
6732
|
+
this.editor = editorSignal.asReadonly();
|
|
6733
|
+
this.node = nodeSignal.asReadonly();
|
|
6734
|
+
this.decorations = decorationsSignal.asReadonly();
|
|
6735
|
+
this.selected = selectedSignal.asReadonly();
|
|
6736
|
+
this.extension = extensionSignal.asReadonly();
|
|
6737
|
+
this.getPos = props.getPos;
|
|
6738
|
+
this.updateAttributes = props.updateAttributes;
|
|
6739
|
+
this.deleteNode = props.deleteNode;
|
|
6740
|
+
// Store writable signals for updates
|
|
6741
|
+
this._writableSignals = {
|
|
6742
|
+
node: nodeSignal,
|
|
6743
|
+
decorations: decorationsSignal,
|
|
6744
|
+
selected: selectedSignal,
|
|
6745
|
+
};
|
|
6746
|
+
}
|
|
6747
|
+
/**
|
|
6748
|
+
* Internal method to update the component when the node changes.
|
|
6749
|
+
* This is called by the AngularNodeViewRenderer.
|
|
6750
|
+
*
|
|
6751
|
+
* @internal
|
|
6752
|
+
*/
|
|
6753
|
+
_updateNodeView(node, decorations) {
|
|
6754
|
+
if (this._writableSignals) {
|
|
6755
|
+
this._writableSignals.node.set(node);
|
|
6756
|
+
this._writableSignals.decorations.set(decorations);
|
|
6757
|
+
}
|
|
6758
|
+
}
|
|
6759
|
+
/**
|
|
6760
|
+
* Internal method to update the selection state.
|
|
6761
|
+
* This is called by the AngularNodeViewRenderer.
|
|
6762
|
+
*
|
|
6763
|
+
* @internal
|
|
6764
|
+
*/
|
|
6765
|
+
_selectNodeView() {
|
|
6766
|
+
if (this._writableSignals) {
|
|
6767
|
+
this._writableSignals.selected.set(true);
|
|
6768
|
+
}
|
|
6769
|
+
}
|
|
6770
|
+
/**
|
|
6771
|
+
* Internal method to update the deselection state.
|
|
6772
|
+
* This is called by the AngularNodeViewRenderer.
|
|
6773
|
+
*
|
|
6774
|
+
* @internal
|
|
6775
|
+
*/
|
|
6776
|
+
_deselectNodeView() {
|
|
6777
|
+
if (this._writableSignals) {
|
|
6778
|
+
this._writableSignals.selected.set(false);
|
|
6779
|
+
}
|
|
6780
|
+
}
|
|
6781
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AteAngularNodeView, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
6782
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.16", type: AteAngularNodeView, isStandalone: true, ngImport: i0 }); }
|
|
6783
|
+
}
|
|
6784
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AteAngularNodeView, decorators: [{
|
|
6785
|
+
type: Directive
|
|
6786
|
+
}] });
|
|
6787
|
+
|
|
6788
|
+
/**
|
|
6789
|
+
* Universal Renderer for Angular Components as TipTap NodeViews.
|
|
6790
|
+
*
|
|
6791
|
+
* Supports:
|
|
6792
|
+
* - TipTap-Aware components (extending AteAngularNodeView)
|
|
6793
|
+
* - Standard library components (automatic @Input() sync)
|
|
6794
|
+
* - Signal-based inputs and outputs (Angular 18+)
|
|
6795
|
+
* - Content projection (editableContent)
|
|
6796
|
+
* - Unified lifecycle and event management
|
|
6797
|
+
*/
|
|
6798
|
+
function AteNodeViewRenderer(component, options) {
|
|
6799
|
+
return props => {
|
|
6800
|
+
const { node, view: _view, getPos, decorations, editor, extension } = props;
|
|
6801
|
+
const { injector, inputs = {}, wrapperTag = "div", wrapperClass, editableContent = false, contentSelector, onOutput, ignoreMutation = true, } = options;
|
|
6802
|
+
const dom = document.createElement(wrapperTag);
|
|
6803
|
+
if (wrapperClass) {
|
|
6804
|
+
dom.className = wrapperClass;
|
|
6805
|
+
}
|
|
6806
|
+
const applicationRef = injector.get(ApplicationRef);
|
|
6807
|
+
const environmentInjector = injector.get(EnvironmentInjector);
|
|
6808
|
+
// 1. Setup Content Projection (ng-content)
|
|
6809
|
+
let initialNodes = [];
|
|
6810
|
+
let contentDOM = null;
|
|
6811
|
+
if (editableContent && !contentSelector) {
|
|
6812
|
+
contentDOM = document.createElement("div");
|
|
6813
|
+
contentDOM.setAttribute("data-node-view-content", "");
|
|
6814
|
+
initialNodes = [[contentDOM]];
|
|
6815
|
+
}
|
|
6816
|
+
// 2. Create the Angular Component
|
|
6817
|
+
const componentRef = createComponent(component, {
|
|
6818
|
+
environmentInjector,
|
|
6819
|
+
elementInjector: injector,
|
|
6820
|
+
projectableNodes: initialNodes,
|
|
6821
|
+
});
|
|
6822
|
+
const instance = componentRef.instance;
|
|
6823
|
+
const subscriptions = [];
|
|
6824
|
+
// 3. Initialize TipTap-Aware instances
|
|
6825
|
+
if (instance instanceof AteAngularNodeView) {
|
|
6826
|
+
instance._initNodeView({
|
|
6827
|
+
editor,
|
|
6828
|
+
node,
|
|
6829
|
+
decorations,
|
|
6830
|
+
selected: false,
|
|
6831
|
+
extension,
|
|
6832
|
+
getPos,
|
|
6833
|
+
updateAttributes: attrs => editor.commands.updateAttributes(node.type.name, attrs),
|
|
6834
|
+
deleteNode: () => {
|
|
6835
|
+
const pos = getPos();
|
|
6836
|
+
if (pos !== undefined) {
|
|
6837
|
+
editor.commands.deleteRange({ from: pos, to: pos + node.nodeSize });
|
|
6838
|
+
}
|
|
6839
|
+
},
|
|
6840
|
+
});
|
|
6841
|
+
}
|
|
6842
|
+
// 4. Synchronize Inputs (Handles standard @Input and Signal-based inputs)
|
|
6843
|
+
const syncInputs = (nodeToSync) => {
|
|
6844
|
+
// Combine base inputs from options with dynamic node attributes
|
|
6845
|
+
const mergedInputs = { ...inputs, ...nodeToSync.attrs };
|
|
6846
|
+
Object.entries(mergedInputs).forEach(([key, value]) => {
|
|
6847
|
+
if (key !== "id" && value !== null && value !== undefined) {
|
|
6848
|
+
try {
|
|
6849
|
+
componentRef.setInput(key, value);
|
|
6850
|
+
}
|
|
6851
|
+
catch {
|
|
6852
|
+
// Silently ignore inputs that don't exist on the component
|
|
6853
|
+
}
|
|
6854
|
+
}
|
|
6855
|
+
});
|
|
6856
|
+
};
|
|
6857
|
+
syncInputs(node);
|
|
6858
|
+
// 5. Setup Outputs (Handles EventEmitter and OutputRef)
|
|
6859
|
+
if (onOutput) {
|
|
6860
|
+
Object.entries(instance).forEach(([key, potentialOutput]) => {
|
|
6861
|
+
if (potentialOutput &&
|
|
6862
|
+
typeof potentialOutput
|
|
6863
|
+
.subscribe === "function") {
|
|
6864
|
+
const sub = potentialOutput.subscribe((value) => {
|
|
6865
|
+
onOutput(key, value);
|
|
6866
|
+
});
|
|
6867
|
+
if (sub instanceof Subscription) {
|
|
6868
|
+
subscriptions.push(sub);
|
|
6869
|
+
}
|
|
6870
|
+
}
|
|
6871
|
+
});
|
|
6872
|
+
}
|
|
6873
|
+
// 6. Attach to DOM and ApplicationRef
|
|
6874
|
+
applicationRef.attachView(componentRef.hostView);
|
|
6875
|
+
const componentElement = componentRef.location.nativeElement;
|
|
6876
|
+
// Target specific element for content if selector provided
|
|
6877
|
+
if (editableContent && contentSelector) {
|
|
6878
|
+
contentDOM = componentElement.querySelector(contentSelector);
|
|
6879
|
+
}
|
|
6880
|
+
dom.appendChild(componentElement);
|
|
6881
|
+
// Initial detection to ensure the component is rendered
|
|
6882
|
+
componentRef.changeDetectorRef.detectChanges();
|
|
6883
|
+
// 7. Return the TipTap NodeView Interface
|
|
6884
|
+
return {
|
|
6885
|
+
dom,
|
|
6886
|
+
contentDOM,
|
|
6887
|
+
update: (updatedNode, updatedDecorations) => {
|
|
6888
|
+
if (updatedNode.type !== node.type) {
|
|
6889
|
+
return false;
|
|
6890
|
+
}
|
|
6891
|
+
// Update Aware component signals
|
|
6892
|
+
if (instance instanceof AteAngularNodeView) {
|
|
6893
|
+
instance._updateNodeView(updatedNode, updatedDecorations);
|
|
6894
|
+
}
|
|
6895
|
+
// Update inputs
|
|
6896
|
+
syncInputs(updatedNode);
|
|
6897
|
+
// Notify and Detect changes
|
|
6898
|
+
componentRef.changeDetectorRef.detectChanges();
|
|
6899
|
+
if (options.onUpdate) {
|
|
6900
|
+
options.onUpdate(updatedNode);
|
|
6901
|
+
}
|
|
6902
|
+
return true;
|
|
6903
|
+
},
|
|
6904
|
+
selectNode: () => {
|
|
6905
|
+
if (instance instanceof AteAngularNodeView) {
|
|
6906
|
+
instance._selectNodeView();
|
|
6907
|
+
}
|
|
6908
|
+
dom.classList.add("ProseMirror-selectednode");
|
|
6909
|
+
},
|
|
6910
|
+
deselectNode: () => {
|
|
6911
|
+
if (instance instanceof AteAngularNodeView) {
|
|
6912
|
+
instance._deselectNodeView();
|
|
6913
|
+
}
|
|
6914
|
+
dom.classList.remove("ProseMirror-selectednode");
|
|
6915
|
+
},
|
|
6916
|
+
destroy: () => {
|
|
6917
|
+
if (options.onDestroy) {
|
|
6918
|
+
options.onDestroy();
|
|
6919
|
+
}
|
|
6920
|
+
subscriptions.forEach(s => s.unsubscribe());
|
|
6921
|
+
applicationRef.detachView(componentRef.hostView);
|
|
6922
|
+
componentRef.destroy();
|
|
6923
|
+
},
|
|
6924
|
+
stopEvent: event => {
|
|
6925
|
+
const target = event.target;
|
|
6926
|
+
const isEditable = target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable;
|
|
6927
|
+
return isEditable;
|
|
6928
|
+
},
|
|
6929
|
+
ignoreMutation: mutation => {
|
|
6930
|
+
if (typeof ignoreMutation === "function") {
|
|
6931
|
+
return ignoreMutation(mutation);
|
|
6932
|
+
}
|
|
6933
|
+
return ignoreMutation;
|
|
6934
|
+
},
|
|
6935
|
+
};
|
|
6936
|
+
};
|
|
6937
|
+
}
|
|
6938
|
+
|
|
6939
|
+
const RESERVED_NAMES = [
|
|
6940
|
+
"doc",
|
|
6941
|
+
"paragraph",
|
|
6942
|
+
"text",
|
|
6943
|
+
"hardBreak",
|
|
6944
|
+
"bulletList",
|
|
6945
|
+
"orderedList",
|
|
6946
|
+
"listItem",
|
|
6947
|
+
"blockquote",
|
|
6948
|
+
"codeBlock",
|
|
6949
|
+
"heading",
|
|
6950
|
+
"horizontalRule",
|
|
6951
|
+
];
|
|
6952
|
+
/**
|
|
6953
|
+
* Derives the TipTap node name and HTML tag from a component class.
|
|
6954
|
+
*/
|
|
6955
|
+
function deriveMetadata(component, customName) {
|
|
6956
|
+
let nodeName = customName;
|
|
6957
|
+
if (!nodeName) {
|
|
6958
|
+
nodeName = component.name
|
|
6959
|
+
.replace(/Component$/, "")
|
|
6960
|
+
.replace(/Node$/, "")
|
|
6961
|
+
.replace(/^([A-Z])/, m => m.toLowerCase());
|
|
6962
|
+
console.warn(`[ATE] Auto-deriving node name '${nodeName}' for component ${component.name}. ` +
|
|
6963
|
+
`Provide an explicit 'name' in options to avoid potential naming collisions.`);
|
|
6964
|
+
}
|
|
6965
|
+
if (RESERVED_NAMES.includes(nodeName)) {
|
|
6966
|
+
throw new Error(`[ATE] The name '${nodeName}' is a reserved TipTap node name. ` +
|
|
6967
|
+
`Please provide a unique name for your custom component.`);
|
|
6968
|
+
}
|
|
6969
|
+
const tag = nodeName.toLowerCase().replace(/([A-Z])/g, "-$1");
|
|
6970
|
+
return { nodeName, tag };
|
|
6971
|
+
}
|
|
6972
|
+
/**
|
|
6973
|
+
* Creates TipTap attributes from component inputs (Standard Mode).
|
|
6974
|
+
*/
|
|
6975
|
+
function createStandardAttributes(defaultInputs = {}) {
|
|
6976
|
+
const tiptapAttributes = {};
|
|
6977
|
+
Object.entries(defaultInputs).forEach(([key, defaultValue]) => {
|
|
6978
|
+
tiptapAttributes[key] = {
|
|
6979
|
+
default: defaultValue,
|
|
6980
|
+
parseHTML: (element) => {
|
|
6981
|
+
const attr = element.getAttribute(`data-${key}`);
|
|
6982
|
+
if (attr === null) {
|
|
6983
|
+
return defaultValue;
|
|
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
|
+
}
|
|
7004
|
+
/**
|
|
7005
|
+
* Factory to transform an Angular component into a TipTap Node extension.
|
|
7006
|
+
* Supports both "TipTap-Aware" and "Standard" modes.
|
|
7007
|
+
*
|
|
7008
|
+
* @internal
|
|
7009
|
+
*/
|
|
7010
|
+
function createAngularComponentExtension(injector, options) {
|
|
7011
|
+
const { component, name: customName, attributes, defaultInputs, contentSelector, contentMode = "block", editableContent = false, group = "block", draggable = true, ignoreMutation = true, onOutput, HTMLAttributes = {}, parseHTML: customParseHTML, renderHTML: customRenderHTML, } = options;
|
|
7012
|
+
const { nodeName, tag } = deriveMetadata(component, customName);
|
|
7013
|
+
const isTipTapAware = Object.prototype.isPrototypeOf.call(AteAngularNodeView, component);
|
|
7014
|
+
const atom = !editableContent;
|
|
7015
|
+
// 1. Prepare Attributes
|
|
7016
|
+
const tiptapAttributes = isTipTapAware
|
|
7017
|
+
? attributes || {}
|
|
7018
|
+
: createStandardAttributes(defaultInputs);
|
|
7019
|
+
// 2. Create Node Extension
|
|
7020
|
+
return Node$1.create({
|
|
7021
|
+
name: nodeName,
|
|
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
|
+
|
|
7061
|
+
/**
|
|
7062
|
+
* Registers ANY Angular component as a TipTap node extension.
|
|
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
|
+
* ```
|
|
7086
|
+
*/
|
|
7087
|
+
function registerAngularComponent(injector, options) {
|
|
7088
|
+
return createAngularComponentExtension(injector, options);
|
|
7089
|
+
}
|
|
7090
|
+
|
|
6677
7091
|
/*
|
|
6678
7092
|
* Public API Surface of tiptap-editor
|
|
6679
7093
|
*/
|
|
@@ -6711,5 +7125,5 @@ const DEFAULT_CELL_MENU_CONFIG = ATE_DEFAULT_CELL_MENU_CONFIG;
|
|
|
6711
7125
|
* Generated bundle index. Do not edit.
|
|
6712
7126
|
*/
|
|
6713
7127
|
|
|
6714
|
-
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, AteColorPickerService, AteDiscoveryCalculator, AteEditorCommandsService, AteI18nService, AteImageCalculator, AteImageService, AteLinkService, AteMarksCalculator, 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, createDefaultSlashCommands, filterSlashCommands };
|
|
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 };
|
|
6715
7129
|
//# sourceMappingURL=flogeez-angular-tiptap-editor.mjs.map
|