@flogeez/angular-tiptap-editor 2.2.2 → 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,22 @@ 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
|
+
|
|
18
|
+
## [2.2.3] - 2026-01-27
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- **Link Navigation**: Added support for opening links via `Ctrl + Click` (Windows/Linux) or `Cmd + Click` (macOS), allowing users to follow links directly from the editor even in edit mode.
|
|
23
|
+
|
|
8
24
|
## [2.2.2] - 2026-01-26
|
|
9
25
|
|
|
10
26
|
### Refactored
|
|
@@ -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';
|
|
@@ -5646,13 +5646,14 @@ const AteLinkClickBehavior = Extension.create({
|
|
|
5646
5646
|
new Plugin({
|
|
5647
5647
|
key: new PluginKey("linkClickBehavior"),
|
|
5648
5648
|
props: {
|
|
5649
|
-
handleClick(view, _pos,
|
|
5649
|
+
handleClick(view, _pos, event) {
|
|
5650
5650
|
// handleClick only runs in the browser, but we guard it for absolute SSR safety
|
|
5651
5651
|
if (typeof window === "undefined") {
|
|
5652
5652
|
return false;
|
|
5653
5653
|
}
|
|
5654
|
-
|
|
5655
|
-
|
|
5654
|
+
const isModKey = event.ctrlKey || event.metaKey;
|
|
5655
|
+
// If editor is editable, only proceed if Ctrl/Cmd is pressed
|
|
5656
|
+
if (view.editable && !isModKey) {
|
|
5656
5657
|
return false;
|
|
5657
5658
|
}
|
|
5658
5659
|
const attrs = getAttributes(view.state, "link");
|
|
@@ -6052,6 +6053,23 @@ class AngularTiptapEditorComponent {
|
|
|
6052
6053
|
}
|
|
6053
6054
|
}
|
|
6054
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
|
+
});
|
|
6055
6073
|
}
|
|
6056
6074
|
ngAfterViewInit() {
|
|
6057
6075
|
// La vue est déjà complètement initialisée dans ngAfterViewInit
|
|
@@ -6673,6 +6691,403 @@ const ATE_TABLE_BUBBLE_MENU_KEYS = [
|
|
|
6673
6691
|
*/
|
|
6674
6692
|
const ATE_CELL_BUBBLE_MENU_KEYS = ["mergeCells", "splitCell"];
|
|
6675
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
|
+
|
|
6676
7091
|
/*
|
|
6677
7092
|
* Public API Surface of tiptap-editor
|
|
6678
7093
|
*/
|
|
@@ -6710,5 +7125,5 @@ const DEFAULT_CELL_MENU_CONFIG = ATE_DEFAULT_CELL_MENU_CONFIG;
|
|
|
6710
7125
|
* Generated bundle index. Do not edit.
|
|
6711
7126
|
*/
|
|
6712
7127
|
|
|
6713
|
-
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 };
|
|
6714
7129
|
//# sourceMappingURL=flogeez-angular-tiptap-editor.mjs.map
|