ninegrid2 6.1315.0 → 6.1317.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/dist/bundle.cjs.js +1362 -122
- package/dist/bundle.esm.js +1362 -122
- package/dist/nx/nxEditor.js +76 -61
- package/package.json +7 -1
- package/src/nx/nxEditor.js +76 -61
package/dist/bundle.cjs.js
CHANGED
|
@@ -47627,6 +47627,581 @@ function wrappingInputRule(config) {
|
|
|
47627
47627
|
undoable: config.undoable
|
|
47628
47628
|
});
|
|
47629
47629
|
}
|
|
47630
|
+
|
|
47631
|
+
// src/lib/ResizableNodeView.ts
|
|
47632
|
+
var isTouchEvent = (e) => {
|
|
47633
|
+
return "touches" in e;
|
|
47634
|
+
};
|
|
47635
|
+
var ResizableNodeView = class {
|
|
47636
|
+
/**
|
|
47637
|
+
* Creates a new ResizableNodeView instance.
|
|
47638
|
+
*
|
|
47639
|
+
* The constructor sets up the resize handles, applies initial sizing from
|
|
47640
|
+
* node attributes, and configures all resize behavior options.
|
|
47641
|
+
*
|
|
47642
|
+
* @param options - Configuration options for the resizable node view
|
|
47643
|
+
*/
|
|
47644
|
+
constructor(options) {
|
|
47645
|
+
/** Active resize handle directions */
|
|
47646
|
+
this.directions = ["bottom-left", "bottom-right", "top-left", "top-right"];
|
|
47647
|
+
/** Minimum allowed dimensions */
|
|
47648
|
+
this.minSize = {
|
|
47649
|
+
height: 8,
|
|
47650
|
+
width: 8
|
|
47651
|
+
};
|
|
47652
|
+
/** Whether to always preserve aspect ratio */
|
|
47653
|
+
this.preserveAspectRatio = false;
|
|
47654
|
+
/** CSS class names for elements */
|
|
47655
|
+
this.classNames = {
|
|
47656
|
+
container: "",
|
|
47657
|
+
wrapper: "",
|
|
47658
|
+
handle: "",
|
|
47659
|
+
resizing: ""
|
|
47660
|
+
};
|
|
47661
|
+
/** Initial width of the element (for aspect ratio calculation) */
|
|
47662
|
+
this.initialWidth = 0;
|
|
47663
|
+
/** Initial height of the element (for aspect ratio calculation) */
|
|
47664
|
+
this.initialHeight = 0;
|
|
47665
|
+
/** Calculated aspect ratio (width / height) */
|
|
47666
|
+
this.aspectRatio = 1;
|
|
47667
|
+
/** Whether a resize operation is currently active */
|
|
47668
|
+
this.isResizing = false;
|
|
47669
|
+
/** The handle currently being dragged */
|
|
47670
|
+
this.activeHandle = null;
|
|
47671
|
+
/** Starting mouse X position when resize began */
|
|
47672
|
+
this.startX = 0;
|
|
47673
|
+
/** Starting mouse Y position when resize began */
|
|
47674
|
+
this.startY = 0;
|
|
47675
|
+
/** Element width when resize began */
|
|
47676
|
+
this.startWidth = 0;
|
|
47677
|
+
/** Element height when resize began */
|
|
47678
|
+
this.startHeight = 0;
|
|
47679
|
+
/** Whether Shift key is currently pressed (for temporary aspect ratio lock) */
|
|
47680
|
+
this.isShiftKeyPressed = false;
|
|
47681
|
+
/** Last known editable state of the editor */
|
|
47682
|
+
this.lastEditableState = void 0;
|
|
47683
|
+
/** Map of handle elements by direction */
|
|
47684
|
+
this.handleMap = /* @__PURE__ */ new Map();
|
|
47685
|
+
/**
|
|
47686
|
+
* Handles mouse movement during an active resize.
|
|
47687
|
+
*
|
|
47688
|
+
* Calculates the delta from the starting position, computes new dimensions
|
|
47689
|
+
* based on the active handle direction, applies constraints and aspect ratio,
|
|
47690
|
+
* then updates the element's style and calls the onResize callback.
|
|
47691
|
+
*
|
|
47692
|
+
* @param event - The mouse move event
|
|
47693
|
+
*/
|
|
47694
|
+
this.handleMouseMove = (event) => {
|
|
47695
|
+
if (!this.isResizing || !this.activeHandle) {
|
|
47696
|
+
return;
|
|
47697
|
+
}
|
|
47698
|
+
const deltaX = event.clientX - this.startX;
|
|
47699
|
+
const deltaY = event.clientY - this.startY;
|
|
47700
|
+
this.handleResize(deltaX, deltaY);
|
|
47701
|
+
};
|
|
47702
|
+
this.handleTouchMove = (event) => {
|
|
47703
|
+
if (!this.isResizing || !this.activeHandle) {
|
|
47704
|
+
return;
|
|
47705
|
+
}
|
|
47706
|
+
const touch = event.touches[0];
|
|
47707
|
+
if (!touch) {
|
|
47708
|
+
return;
|
|
47709
|
+
}
|
|
47710
|
+
const deltaX = touch.clientX - this.startX;
|
|
47711
|
+
const deltaY = touch.clientY - this.startY;
|
|
47712
|
+
this.handleResize(deltaX, deltaY);
|
|
47713
|
+
};
|
|
47714
|
+
/**
|
|
47715
|
+
* Completes the resize operation when the mouse button is released.
|
|
47716
|
+
*
|
|
47717
|
+
* Captures final dimensions, calls the onCommit callback to persist changes,
|
|
47718
|
+
* removes the resizing state and class, and cleans up document-level listeners.
|
|
47719
|
+
*/
|
|
47720
|
+
this.handleMouseUp = () => {
|
|
47721
|
+
if (!this.isResizing) {
|
|
47722
|
+
return;
|
|
47723
|
+
}
|
|
47724
|
+
const finalWidth = this.element.offsetWidth;
|
|
47725
|
+
const finalHeight = this.element.offsetHeight;
|
|
47726
|
+
this.onCommit(finalWidth, finalHeight);
|
|
47727
|
+
this.isResizing = false;
|
|
47728
|
+
this.activeHandle = null;
|
|
47729
|
+
this.container.dataset.resizeState = "false";
|
|
47730
|
+
if (this.classNames.resizing) {
|
|
47731
|
+
this.container.classList.remove(this.classNames.resizing);
|
|
47732
|
+
}
|
|
47733
|
+
document.removeEventListener("mousemove", this.handleMouseMove);
|
|
47734
|
+
document.removeEventListener("mouseup", this.handleMouseUp);
|
|
47735
|
+
document.removeEventListener("keydown", this.handleKeyDown);
|
|
47736
|
+
document.removeEventListener("keyup", this.handleKeyUp);
|
|
47737
|
+
};
|
|
47738
|
+
/**
|
|
47739
|
+
* Tracks Shift key state to enable temporary aspect ratio locking.
|
|
47740
|
+
*
|
|
47741
|
+
* When Shift is pressed during resize, aspect ratio is preserved even if
|
|
47742
|
+
* preserveAspectRatio is false.
|
|
47743
|
+
*
|
|
47744
|
+
* @param event - The keyboard event
|
|
47745
|
+
*/
|
|
47746
|
+
this.handleKeyDown = (event) => {
|
|
47747
|
+
if (event.key === "Shift") {
|
|
47748
|
+
this.isShiftKeyPressed = true;
|
|
47749
|
+
}
|
|
47750
|
+
};
|
|
47751
|
+
/**
|
|
47752
|
+
* Tracks Shift key release to disable temporary aspect ratio locking.
|
|
47753
|
+
*
|
|
47754
|
+
* @param event - The keyboard event
|
|
47755
|
+
*/
|
|
47756
|
+
this.handleKeyUp = (event) => {
|
|
47757
|
+
if (event.key === "Shift") {
|
|
47758
|
+
this.isShiftKeyPressed = false;
|
|
47759
|
+
}
|
|
47760
|
+
};
|
|
47761
|
+
var _a, _b, _c, _d, _e, _f;
|
|
47762
|
+
this.node = options.node;
|
|
47763
|
+
this.editor = options.editor;
|
|
47764
|
+
this.element = options.element;
|
|
47765
|
+
this.contentElement = options.contentElement;
|
|
47766
|
+
this.getPos = options.getPos;
|
|
47767
|
+
this.onResize = options.onResize;
|
|
47768
|
+
this.onCommit = options.onCommit;
|
|
47769
|
+
this.onUpdate = options.onUpdate;
|
|
47770
|
+
if ((_a = options.options) == null ? void 0 : _a.min) {
|
|
47771
|
+
this.minSize = {
|
|
47772
|
+
...this.minSize,
|
|
47773
|
+
...options.options.min
|
|
47774
|
+
};
|
|
47775
|
+
}
|
|
47776
|
+
if ((_b = options.options) == null ? void 0 : _b.max) {
|
|
47777
|
+
this.maxSize = options.options.max;
|
|
47778
|
+
}
|
|
47779
|
+
if ((_c = options == null ? void 0 : options.options) == null ? void 0 : _c.directions) {
|
|
47780
|
+
this.directions = options.options.directions;
|
|
47781
|
+
}
|
|
47782
|
+
if ((_d = options.options) == null ? void 0 : _d.preserveAspectRatio) {
|
|
47783
|
+
this.preserveAspectRatio = options.options.preserveAspectRatio;
|
|
47784
|
+
}
|
|
47785
|
+
if ((_e = options.options) == null ? void 0 : _e.className) {
|
|
47786
|
+
this.classNames = {
|
|
47787
|
+
container: options.options.className.container || "",
|
|
47788
|
+
wrapper: options.options.className.wrapper || "",
|
|
47789
|
+
handle: options.options.className.handle || "",
|
|
47790
|
+
resizing: options.options.className.resizing || ""
|
|
47791
|
+
};
|
|
47792
|
+
}
|
|
47793
|
+
if ((_f = options.options) == null ? void 0 : _f.createCustomHandle) {
|
|
47794
|
+
this.createCustomHandle = options.options.createCustomHandle;
|
|
47795
|
+
}
|
|
47796
|
+
this.wrapper = this.createWrapper();
|
|
47797
|
+
this.container = this.createContainer();
|
|
47798
|
+
this.applyInitialSize();
|
|
47799
|
+
this.attachHandles();
|
|
47800
|
+
this.editor.on("update", this.handleEditorUpdate.bind(this));
|
|
47801
|
+
}
|
|
47802
|
+
/**
|
|
47803
|
+
* Returns the top-level DOM node that should be placed in the editor.
|
|
47804
|
+
*
|
|
47805
|
+
* This is required by the ProseMirror NodeView interface. The container
|
|
47806
|
+
* includes the wrapper, handles, and the actual content element.
|
|
47807
|
+
*
|
|
47808
|
+
* @returns The container element to be inserted into the editor
|
|
47809
|
+
*/
|
|
47810
|
+
get dom() {
|
|
47811
|
+
return this.container;
|
|
47812
|
+
}
|
|
47813
|
+
get contentDOM() {
|
|
47814
|
+
var _a;
|
|
47815
|
+
return (_a = this.contentElement) != null ? _a : null;
|
|
47816
|
+
}
|
|
47817
|
+
handleEditorUpdate() {
|
|
47818
|
+
const isEditable = this.editor.isEditable;
|
|
47819
|
+
if (isEditable === this.lastEditableState) {
|
|
47820
|
+
return;
|
|
47821
|
+
}
|
|
47822
|
+
this.lastEditableState = isEditable;
|
|
47823
|
+
if (!isEditable) {
|
|
47824
|
+
this.removeHandles();
|
|
47825
|
+
} else if (isEditable && this.handleMap.size === 0) {
|
|
47826
|
+
this.attachHandles();
|
|
47827
|
+
}
|
|
47828
|
+
}
|
|
47829
|
+
/**
|
|
47830
|
+
* Called when the node's content or attributes change.
|
|
47831
|
+
*
|
|
47832
|
+
* Updates the internal node reference. If a custom `onUpdate` callback
|
|
47833
|
+
* was provided, it will be called to handle additional update logic.
|
|
47834
|
+
*
|
|
47835
|
+
* @param node - The new/updated node
|
|
47836
|
+
* @param decorations - Node decorations
|
|
47837
|
+
* @param innerDecorations - Inner decorations
|
|
47838
|
+
* @returns `false` if the node type has changed (requires full rebuild), otherwise the result of `onUpdate` or `true`
|
|
47839
|
+
*/
|
|
47840
|
+
update(node, decorations, innerDecorations) {
|
|
47841
|
+
if (node.type !== this.node.type) {
|
|
47842
|
+
return false;
|
|
47843
|
+
}
|
|
47844
|
+
this.node = node;
|
|
47845
|
+
if (this.onUpdate) {
|
|
47846
|
+
return this.onUpdate(node, decorations, innerDecorations);
|
|
47847
|
+
}
|
|
47848
|
+
return true;
|
|
47849
|
+
}
|
|
47850
|
+
/**
|
|
47851
|
+
* Cleanup method called when the node view is being removed.
|
|
47852
|
+
*
|
|
47853
|
+
* Removes all event listeners to prevent memory leaks. This is required
|
|
47854
|
+
* by the ProseMirror NodeView interface. If a resize is active when
|
|
47855
|
+
* destroy is called, it will be properly cancelled.
|
|
47856
|
+
*/
|
|
47857
|
+
destroy() {
|
|
47858
|
+
if (this.isResizing) {
|
|
47859
|
+
this.container.dataset.resizeState = "false";
|
|
47860
|
+
if (this.classNames.resizing) {
|
|
47861
|
+
this.container.classList.remove(this.classNames.resizing);
|
|
47862
|
+
}
|
|
47863
|
+
document.removeEventListener("mousemove", this.handleMouseMove);
|
|
47864
|
+
document.removeEventListener("mouseup", this.handleMouseUp);
|
|
47865
|
+
document.removeEventListener("keydown", this.handleKeyDown);
|
|
47866
|
+
document.removeEventListener("keyup", this.handleKeyUp);
|
|
47867
|
+
this.isResizing = false;
|
|
47868
|
+
this.activeHandle = null;
|
|
47869
|
+
}
|
|
47870
|
+
this.editor.off("update", this.handleEditorUpdate.bind(this));
|
|
47871
|
+
this.container.remove();
|
|
47872
|
+
}
|
|
47873
|
+
/**
|
|
47874
|
+
* Creates the outer container element.
|
|
47875
|
+
*
|
|
47876
|
+
* The container is the top-level element returned by the NodeView and
|
|
47877
|
+
* wraps the entire resizable node. It's set up with flexbox to handle
|
|
47878
|
+
* alignment and includes data attributes for styling and identification.
|
|
47879
|
+
*
|
|
47880
|
+
* @returns The container element
|
|
47881
|
+
*/
|
|
47882
|
+
createContainer() {
|
|
47883
|
+
const element = document.createElement("div");
|
|
47884
|
+
element.dataset.resizeContainer = "";
|
|
47885
|
+
element.dataset.node = this.node.type.name;
|
|
47886
|
+
element.style.display = "flex";
|
|
47887
|
+
if (this.classNames.container) {
|
|
47888
|
+
element.className = this.classNames.container;
|
|
47889
|
+
}
|
|
47890
|
+
element.appendChild(this.wrapper);
|
|
47891
|
+
return element;
|
|
47892
|
+
}
|
|
47893
|
+
/**
|
|
47894
|
+
* Creates the wrapper element that contains the content and handles.
|
|
47895
|
+
*
|
|
47896
|
+
* The wrapper uses relative positioning so that resize handles can be
|
|
47897
|
+
* positioned absolutely within it. This is the direct parent of the
|
|
47898
|
+
* content element being made resizable.
|
|
47899
|
+
*
|
|
47900
|
+
* @returns The wrapper element
|
|
47901
|
+
*/
|
|
47902
|
+
createWrapper() {
|
|
47903
|
+
const element = document.createElement("div");
|
|
47904
|
+
element.style.position = "relative";
|
|
47905
|
+
element.style.display = "block";
|
|
47906
|
+
element.dataset.resizeWrapper = "";
|
|
47907
|
+
if (this.classNames.wrapper) {
|
|
47908
|
+
element.className = this.classNames.wrapper;
|
|
47909
|
+
}
|
|
47910
|
+
element.appendChild(this.element);
|
|
47911
|
+
return element;
|
|
47912
|
+
}
|
|
47913
|
+
/**
|
|
47914
|
+
* Creates a resize handle element for a specific direction.
|
|
47915
|
+
*
|
|
47916
|
+
* Each handle is absolutely positioned and includes a data attribute
|
|
47917
|
+
* identifying its direction for styling purposes.
|
|
47918
|
+
*
|
|
47919
|
+
* @param direction - The resize direction for this handle
|
|
47920
|
+
* @returns The handle element
|
|
47921
|
+
*/
|
|
47922
|
+
createHandle(direction) {
|
|
47923
|
+
const handle = document.createElement("div");
|
|
47924
|
+
handle.dataset.resizeHandle = direction;
|
|
47925
|
+
handle.style.position = "absolute";
|
|
47926
|
+
if (this.classNames.handle) {
|
|
47927
|
+
handle.className = this.classNames.handle;
|
|
47928
|
+
}
|
|
47929
|
+
return handle;
|
|
47930
|
+
}
|
|
47931
|
+
/**
|
|
47932
|
+
* Positions a handle element according to its direction.
|
|
47933
|
+
*
|
|
47934
|
+
* Corner handles (e.g., 'top-left') are positioned at the intersection
|
|
47935
|
+
* of two edges. Edge handles (e.g., 'top') span the full width or height.
|
|
47936
|
+
*
|
|
47937
|
+
* @param handle - The handle element to position
|
|
47938
|
+
* @param direction - The direction determining the position
|
|
47939
|
+
*/
|
|
47940
|
+
positionHandle(handle, direction) {
|
|
47941
|
+
const isTop = direction.includes("top");
|
|
47942
|
+
const isBottom = direction.includes("bottom");
|
|
47943
|
+
const isLeft = direction.includes("left");
|
|
47944
|
+
const isRight = direction.includes("right");
|
|
47945
|
+
if (isTop) {
|
|
47946
|
+
handle.style.top = "0";
|
|
47947
|
+
}
|
|
47948
|
+
if (isBottom) {
|
|
47949
|
+
handle.style.bottom = "0";
|
|
47950
|
+
}
|
|
47951
|
+
if (isLeft) {
|
|
47952
|
+
handle.style.left = "0";
|
|
47953
|
+
}
|
|
47954
|
+
if (isRight) {
|
|
47955
|
+
handle.style.right = "0";
|
|
47956
|
+
}
|
|
47957
|
+
if (direction === "top" || direction === "bottom") {
|
|
47958
|
+
handle.style.left = "0";
|
|
47959
|
+
handle.style.right = "0";
|
|
47960
|
+
}
|
|
47961
|
+
if (direction === "left" || direction === "right") {
|
|
47962
|
+
handle.style.top = "0";
|
|
47963
|
+
handle.style.bottom = "0";
|
|
47964
|
+
}
|
|
47965
|
+
}
|
|
47966
|
+
/**
|
|
47967
|
+
* Creates and attaches all resize handles to the wrapper.
|
|
47968
|
+
*
|
|
47969
|
+
* Iterates through the configured directions, creates a handle for each,
|
|
47970
|
+
* positions it, attaches the mousedown listener, and appends it to the DOM.
|
|
47971
|
+
*/
|
|
47972
|
+
attachHandles() {
|
|
47973
|
+
this.directions.forEach((direction) => {
|
|
47974
|
+
let handle;
|
|
47975
|
+
if (this.createCustomHandle) {
|
|
47976
|
+
handle = this.createCustomHandle(direction);
|
|
47977
|
+
} else {
|
|
47978
|
+
handle = this.createHandle(direction);
|
|
47979
|
+
}
|
|
47980
|
+
if (!(handle instanceof HTMLElement)) {
|
|
47981
|
+
console.warn(
|
|
47982
|
+
`[ResizableNodeView] createCustomHandle("${direction}") did not return an HTMLElement. Falling back to default handle.`
|
|
47983
|
+
);
|
|
47984
|
+
handle = this.createHandle(direction);
|
|
47985
|
+
}
|
|
47986
|
+
if (!this.createCustomHandle) {
|
|
47987
|
+
this.positionHandle(handle, direction);
|
|
47988
|
+
}
|
|
47989
|
+
handle.addEventListener("mousedown", (event) => this.handleResizeStart(event, direction));
|
|
47990
|
+
handle.addEventListener("touchstart", (event) => this.handleResizeStart(event, direction));
|
|
47991
|
+
this.handleMap.set(direction, handle);
|
|
47992
|
+
this.wrapper.appendChild(handle);
|
|
47993
|
+
});
|
|
47994
|
+
}
|
|
47995
|
+
/**
|
|
47996
|
+
* Removes all resize handles from the wrapper.
|
|
47997
|
+
*
|
|
47998
|
+
* Cleans up the handle map and removes each handle element from the DOM.
|
|
47999
|
+
*/
|
|
48000
|
+
removeHandles() {
|
|
48001
|
+
this.handleMap.forEach((el) => el.remove());
|
|
48002
|
+
this.handleMap.clear();
|
|
48003
|
+
}
|
|
48004
|
+
/**
|
|
48005
|
+
* Applies initial sizing from node attributes to the element.
|
|
48006
|
+
*
|
|
48007
|
+
* If width/height attributes exist on the node, they're applied to the element.
|
|
48008
|
+
* Otherwise, the element's natural/current dimensions are measured. The aspect
|
|
48009
|
+
* ratio is calculated for later use in aspect-ratio-preserving resizes.
|
|
48010
|
+
*/
|
|
48011
|
+
applyInitialSize() {
|
|
48012
|
+
const width = this.node.attrs.width;
|
|
48013
|
+
const height = this.node.attrs.height;
|
|
48014
|
+
if (width) {
|
|
48015
|
+
this.element.style.width = `${width}px`;
|
|
48016
|
+
this.initialWidth = width;
|
|
48017
|
+
} else {
|
|
48018
|
+
this.initialWidth = this.element.offsetWidth;
|
|
48019
|
+
}
|
|
48020
|
+
if (height) {
|
|
48021
|
+
this.element.style.height = `${height}px`;
|
|
48022
|
+
this.initialHeight = height;
|
|
48023
|
+
} else {
|
|
48024
|
+
this.initialHeight = this.element.offsetHeight;
|
|
48025
|
+
}
|
|
48026
|
+
if (this.initialWidth > 0 && this.initialHeight > 0) {
|
|
48027
|
+
this.aspectRatio = this.initialWidth / this.initialHeight;
|
|
48028
|
+
}
|
|
48029
|
+
}
|
|
48030
|
+
/**
|
|
48031
|
+
* Initiates a resize operation when a handle is clicked.
|
|
48032
|
+
*
|
|
48033
|
+
* Captures the starting mouse position and element dimensions, sets up
|
|
48034
|
+
* the resize state, adds the resizing class and state attribute, and
|
|
48035
|
+
* attaches document-level listeners for mouse movement and keyboard input.
|
|
48036
|
+
*
|
|
48037
|
+
* @param event - The mouse down event
|
|
48038
|
+
* @param direction - The direction of the handle being dragged
|
|
48039
|
+
*/
|
|
48040
|
+
handleResizeStart(event, direction) {
|
|
48041
|
+
event.preventDefault();
|
|
48042
|
+
event.stopPropagation();
|
|
48043
|
+
this.isResizing = true;
|
|
48044
|
+
this.activeHandle = direction;
|
|
48045
|
+
if (isTouchEvent(event)) {
|
|
48046
|
+
this.startX = event.touches[0].clientX;
|
|
48047
|
+
this.startY = event.touches[0].clientY;
|
|
48048
|
+
} else {
|
|
48049
|
+
this.startX = event.clientX;
|
|
48050
|
+
this.startY = event.clientY;
|
|
48051
|
+
}
|
|
48052
|
+
this.startWidth = this.element.offsetWidth;
|
|
48053
|
+
this.startHeight = this.element.offsetHeight;
|
|
48054
|
+
if (this.startWidth > 0 && this.startHeight > 0) {
|
|
48055
|
+
this.aspectRatio = this.startWidth / this.startHeight;
|
|
48056
|
+
}
|
|
48057
|
+
this.getPos();
|
|
48058
|
+
this.container.dataset.resizeState = "true";
|
|
48059
|
+
if (this.classNames.resizing) {
|
|
48060
|
+
this.container.classList.add(this.classNames.resizing);
|
|
48061
|
+
}
|
|
48062
|
+
document.addEventListener("mousemove", this.handleMouseMove);
|
|
48063
|
+
document.addEventListener("touchmove", this.handleTouchMove);
|
|
48064
|
+
document.addEventListener("mouseup", this.handleMouseUp);
|
|
48065
|
+
document.addEventListener("keydown", this.handleKeyDown);
|
|
48066
|
+
document.addEventListener("keyup", this.handleKeyUp);
|
|
48067
|
+
}
|
|
48068
|
+
handleResize(deltaX, deltaY) {
|
|
48069
|
+
if (!this.activeHandle) {
|
|
48070
|
+
return;
|
|
48071
|
+
}
|
|
48072
|
+
const shouldPreserveAspectRatio = this.preserveAspectRatio || this.isShiftKeyPressed;
|
|
48073
|
+
const { width, height } = this.calculateNewDimensions(this.activeHandle, deltaX, deltaY);
|
|
48074
|
+
const constrained = this.applyConstraints(width, height, shouldPreserveAspectRatio);
|
|
48075
|
+
this.element.style.width = `${constrained.width}px`;
|
|
48076
|
+
this.element.style.height = `${constrained.height}px`;
|
|
48077
|
+
if (this.onResize) {
|
|
48078
|
+
this.onResize(constrained.width, constrained.height);
|
|
48079
|
+
}
|
|
48080
|
+
}
|
|
48081
|
+
/**
|
|
48082
|
+
* Calculates new dimensions based on mouse delta and resize direction.
|
|
48083
|
+
*
|
|
48084
|
+
* Takes the starting dimensions and applies the mouse movement delta
|
|
48085
|
+
* according to the handle direction. For corner handles, both dimensions
|
|
48086
|
+
* are affected. For edge handles, only one dimension changes. If aspect
|
|
48087
|
+
* ratio should be preserved, delegates to applyAspectRatio.
|
|
48088
|
+
*
|
|
48089
|
+
* @param direction - The active resize handle direction
|
|
48090
|
+
* @param deltaX - Horizontal mouse movement since resize start
|
|
48091
|
+
* @param deltaY - Vertical mouse movement since resize start
|
|
48092
|
+
* @returns The calculated width and height
|
|
48093
|
+
*/
|
|
48094
|
+
calculateNewDimensions(direction, deltaX, deltaY) {
|
|
48095
|
+
let newWidth = this.startWidth;
|
|
48096
|
+
let newHeight = this.startHeight;
|
|
48097
|
+
const isRight = direction.includes("right");
|
|
48098
|
+
const isLeft = direction.includes("left");
|
|
48099
|
+
const isBottom = direction.includes("bottom");
|
|
48100
|
+
const isTop = direction.includes("top");
|
|
48101
|
+
if (isRight) {
|
|
48102
|
+
newWidth = this.startWidth + deltaX;
|
|
48103
|
+
} else if (isLeft) {
|
|
48104
|
+
newWidth = this.startWidth - deltaX;
|
|
48105
|
+
}
|
|
48106
|
+
if (isBottom) {
|
|
48107
|
+
newHeight = this.startHeight + deltaY;
|
|
48108
|
+
} else if (isTop) {
|
|
48109
|
+
newHeight = this.startHeight - deltaY;
|
|
48110
|
+
}
|
|
48111
|
+
if (direction === "right" || direction === "left") {
|
|
48112
|
+
newWidth = this.startWidth + (isRight ? deltaX : -deltaX);
|
|
48113
|
+
}
|
|
48114
|
+
if (direction === "top" || direction === "bottom") {
|
|
48115
|
+
newHeight = this.startHeight + (isBottom ? deltaY : -deltaY);
|
|
48116
|
+
}
|
|
48117
|
+
const shouldPreserveAspectRatio = this.preserveAspectRatio || this.isShiftKeyPressed;
|
|
48118
|
+
if (shouldPreserveAspectRatio) {
|
|
48119
|
+
return this.applyAspectRatio(newWidth, newHeight, direction);
|
|
48120
|
+
}
|
|
48121
|
+
return { width: newWidth, height: newHeight };
|
|
48122
|
+
}
|
|
48123
|
+
/**
|
|
48124
|
+
* Applies min/max constraints to dimensions.
|
|
48125
|
+
*
|
|
48126
|
+
* When aspect ratio is NOT preserved, constraints are applied independently
|
|
48127
|
+
* to width and height. When aspect ratio IS preserved, constraints are
|
|
48128
|
+
* applied while maintaining the aspect ratio—if one dimension hits a limit,
|
|
48129
|
+
* the other is recalculated proportionally.
|
|
48130
|
+
*
|
|
48131
|
+
* This ensures that aspect ratio is never broken when constrained.
|
|
48132
|
+
*
|
|
48133
|
+
* @param width - The unconstrained width
|
|
48134
|
+
* @param height - The unconstrained height
|
|
48135
|
+
* @param preserveAspectRatio - Whether to maintain aspect ratio while constraining
|
|
48136
|
+
* @returns The constrained dimensions
|
|
48137
|
+
*/
|
|
48138
|
+
applyConstraints(width, height, preserveAspectRatio) {
|
|
48139
|
+
var _a, _b, _c, _d;
|
|
48140
|
+
if (!preserveAspectRatio) {
|
|
48141
|
+
let constrainedWidth2 = Math.max(this.minSize.width, width);
|
|
48142
|
+
let constrainedHeight2 = Math.max(this.minSize.height, height);
|
|
48143
|
+
if ((_a = this.maxSize) == null ? void 0 : _a.width) {
|
|
48144
|
+
constrainedWidth2 = Math.min(this.maxSize.width, constrainedWidth2);
|
|
48145
|
+
}
|
|
48146
|
+
if ((_b = this.maxSize) == null ? void 0 : _b.height) {
|
|
48147
|
+
constrainedHeight2 = Math.min(this.maxSize.height, constrainedHeight2);
|
|
48148
|
+
}
|
|
48149
|
+
return { width: constrainedWidth2, height: constrainedHeight2 };
|
|
48150
|
+
}
|
|
48151
|
+
let constrainedWidth = width;
|
|
48152
|
+
let constrainedHeight = height;
|
|
48153
|
+
if (constrainedWidth < this.minSize.width) {
|
|
48154
|
+
constrainedWidth = this.minSize.width;
|
|
48155
|
+
constrainedHeight = constrainedWidth / this.aspectRatio;
|
|
48156
|
+
}
|
|
48157
|
+
if (constrainedHeight < this.minSize.height) {
|
|
48158
|
+
constrainedHeight = this.minSize.height;
|
|
48159
|
+
constrainedWidth = constrainedHeight * this.aspectRatio;
|
|
48160
|
+
}
|
|
48161
|
+
if (((_c = this.maxSize) == null ? void 0 : _c.width) && constrainedWidth > this.maxSize.width) {
|
|
48162
|
+
constrainedWidth = this.maxSize.width;
|
|
48163
|
+
constrainedHeight = constrainedWidth / this.aspectRatio;
|
|
48164
|
+
}
|
|
48165
|
+
if (((_d = this.maxSize) == null ? void 0 : _d.height) && constrainedHeight > this.maxSize.height) {
|
|
48166
|
+
constrainedHeight = this.maxSize.height;
|
|
48167
|
+
constrainedWidth = constrainedHeight * this.aspectRatio;
|
|
48168
|
+
}
|
|
48169
|
+
return { width: constrainedWidth, height: constrainedHeight };
|
|
48170
|
+
}
|
|
48171
|
+
/**
|
|
48172
|
+
* Adjusts dimensions to maintain the original aspect ratio.
|
|
48173
|
+
*
|
|
48174
|
+
* For horizontal handles (left/right), uses width as the primary dimension
|
|
48175
|
+
* and calculates height from it. For vertical handles (top/bottom), uses
|
|
48176
|
+
* height as primary and calculates width. For corner handles, uses width
|
|
48177
|
+
* as the primary dimension.
|
|
48178
|
+
*
|
|
48179
|
+
* @param width - The new width
|
|
48180
|
+
* @param height - The new height
|
|
48181
|
+
* @param direction - The active resize direction
|
|
48182
|
+
* @returns Dimensions adjusted to preserve aspect ratio
|
|
48183
|
+
*/
|
|
48184
|
+
applyAspectRatio(width, height, direction) {
|
|
48185
|
+
const isHorizontal = direction === "left" || direction === "right";
|
|
48186
|
+
const isVertical = direction === "top" || direction === "bottom";
|
|
48187
|
+
if (isHorizontal) {
|
|
48188
|
+
return {
|
|
48189
|
+
width,
|
|
48190
|
+
height: width / this.aspectRatio
|
|
48191
|
+
};
|
|
48192
|
+
}
|
|
48193
|
+
if (isVertical) {
|
|
48194
|
+
return {
|
|
48195
|
+
width: height * this.aspectRatio,
|
|
48196
|
+
height
|
|
48197
|
+
};
|
|
48198
|
+
}
|
|
48199
|
+
return {
|
|
48200
|
+
width,
|
|
48201
|
+
height: width / this.aspectRatio
|
|
48202
|
+
};
|
|
48203
|
+
}
|
|
48204
|
+
};
|
|
47630
48205
|
function canInsertNode(state, nodeType) {
|
|
47631
48206
|
const { selection } = state;
|
|
47632
48207
|
const { $from } = selection;
|
|
@@ -48252,7 +48827,7 @@ var h = (tag, attributes) => {
|
|
|
48252
48827
|
};
|
|
48253
48828
|
|
|
48254
48829
|
// src/blockquote.tsx
|
|
48255
|
-
var inputRegex$
|
|
48830
|
+
var inputRegex$5 = /^\s*>\s$/;
|
|
48256
48831
|
var Blockquote = Node3.create({
|
|
48257
48832
|
name: "blockquote",
|
|
48258
48833
|
addOptions() {
|
|
@@ -48314,7 +48889,7 @@ ${prefix}
|
|
|
48314
48889
|
addInputRules() {
|
|
48315
48890
|
return [
|
|
48316
48891
|
wrappingInputRule({
|
|
48317
|
-
find: inputRegex$
|
|
48892
|
+
find: inputRegex$5,
|
|
48318
48893
|
type: this.type
|
|
48319
48894
|
})
|
|
48320
48895
|
];
|
|
@@ -48408,8 +48983,8 @@ var Bold = Mark.create({
|
|
|
48408
48983
|
});
|
|
48409
48984
|
|
|
48410
48985
|
// src/code.ts
|
|
48411
|
-
var inputRegex$
|
|
48412
|
-
var pasteRegex$
|
|
48986
|
+
var inputRegex$4 = /(^|[^`])`([^`]+)`(?!`)$/;
|
|
48987
|
+
var pasteRegex$2 = /(^|[^`])`([^`]+)`(?!`)/g;
|
|
48413
48988
|
var Code = Mark.create({
|
|
48414
48989
|
name: "code",
|
|
48415
48990
|
addOptions() {
|
|
@@ -48457,7 +49032,7 @@ var Code = Mark.create({
|
|
|
48457
49032
|
addInputRules() {
|
|
48458
49033
|
return [
|
|
48459
49034
|
markInputRule({
|
|
48460
|
-
find: inputRegex$
|
|
49035
|
+
find: inputRegex$4,
|
|
48461
49036
|
type: this.type
|
|
48462
49037
|
})
|
|
48463
49038
|
];
|
|
@@ -48465,7 +49040,7 @@ var Code = Mark.create({
|
|
|
48465
49040
|
addPasteRules() {
|
|
48466
49041
|
return [
|
|
48467
49042
|
markPasteRule({
|
|
48468
|
-
find: pasteRegex$
|
|
49043
|
+
find: pasteRegex$2,
|
|
48469
49044
|
type: this.type
|
|
48470
49045
|
})
|
|
48471
49046
|
];
|
|
@@ -51960,7 +52535,7 @@ var OrderedList = Node3.create({
|
|
|
51960
52535
|
return [inputRule];
|
|
51961
52536
|
}
|
|
51962
52537
|
});
|
|
51963
|
-
var inputRegex$
|
|
52538
|
+
var inputRegex$3 = /^\s*(\[([( |x])?\])\s$/;
|
|
51964
52539
|
var TaskItem = Node3.create({
|
|
51965
52540
|
name: "taskItem",
|
|
51966
52541
|
addOptions() {
|
|
@@ -52147,7 +52722,7 @@ var TaskItem = Node3.create({
|
|
|
52147
52722
|
addInputRules() {
|
|
52148
52723
|
return [
|
|
52149
52724
|
wrappingInputRule({
|
|
52150
|
-
find: inputRegex$
|
|
52725
|
+
find: inputRegex$3,
|
|
52151
52726
|
type: this.type,
|
|
52152
52727
|
getAttributes: (match) => ({
|
|
52153
52728
|
checked: match[match.length - 1] === "x"
|
|
@@ -52366,8 +52941,8 @@ var Paragraph = Node3.create({
|
|
|
52366
52941
|
});
|
|
52367
52942
|
|
|
52368
52943
|
// src/strike.ts
|
|
52369
|
-
var inputRegex = /(?:^|\s)(~~(?!\s+~~)((?:[^~]+))~~(?!\s+~~))$/;
|
|
52370
|
-
var pasteRegex = /(?:^|\s)(~~(?!\s+~~)((?:[^~]+))~~(?!\s+~~))/g;
|
|
52944
|
+
var inputRegex$2 = /(?:^|\s)(~~(?!\s+~~)((?:[^~]+))~~(?!\s+~~))$/;
|
|
52945
|
+
var pasteRegex$1 = /(?:^|\s)(~~(?!\s+~~)((?:[^~]+))~~(?!\s+~~))/g;
|
|
52371
52946
|
var Strike = Mark.create({
|
|
52372
52947
|
name: "strike",
|
|
52373
52948
|
addOptions() {
|
|
@@ -52424,7 +52999,7 @@ var Strike = Mark.create({
|
|
|
52424
52999
|
addInputRules() {
|
|
52425
53000
|
return [
|
|
52426
53001
|
markInputRule({
|
|
52427
|
-
find: inputRegex,
|
|
53002
|
+
find: inputRegex$2,
|
|
52428
53003
|
type: this.type
|
|
52429
53004
|
})
|
|
52430
53005
|
];
|
|
@@ -52432,7 +53007,7 @@ var Strike = Mark.create({
|
|
|
52432
53007
|
addPasteRules() {
|
|
52433
53008
|
return [
|
|
52434
53009
|
markPasteRule({
|
|
52435
|
-
find: pasteRegex,
|
|
53010
|
+
find: pasteRegex$1,
|
|
52436
53011
|
type: this.type
|
|
52437
53012
|
})
|
|
52438
53013
|
];
|
|
@@ -53947,116 +54522,781 @@ var StarterKit = Extension.create({
|
|
|
53947
54522
|
// src/index.ts
|
|
53948
54523
|
var index_default = StarterKit;
|
|
53949
54524
|
|
|
53950
|
-
//
|
|
53951
|
-
|
|
53952
|
-
|
|
53953
|
-
|
|
53954
|
-
|
|
53955
|
-
|
|
53956
|
-
|
|
53957
|
-
|
|
53958
|
-
|
|
53959
|
-
|
|
53960
|
-
|
|
53961
|
-
|
|
53962
|
-
|
|
53963
|
-
|
|
53964
|
-
|
|
53965
|
-
|
|
53966
|
-
|
|
53967
|
-
|
|
53968
|
-
|
|
53969
|
-
|
|
53970
|
-
|
|
53971
|
-
|
|
53972
|
-
|
|
53973
|
-
|
|
53974
|
-
|
|
53975
|
-
|
|
53976
|
-
|
|
53977
|
-
|
|
53978
|
-
|
|
53979
|
-
|
|
53980
|
-
|
|
53981
|
-
|
|
53982
|
-
|
|
53983
|
-
|
|
53984
|
-
|
|
53985
|
-
|
|
53986
|
-
|
|
53987
|
-
|
|
53988
|
-
|
|
53989
|
-
|
|
53990
|
-
|
|
53991
|
-
|
|
53992
|
-
|
|
53993
|
-
|
|
53994
|
-
|
|
53995
|
-
|
|
53996
|
-
|
|
53997
|
-
|
|
53998
|
-
|
|
53999
|
-
|
|
54000
|
-
|
|
54001
|
-
|
|
54002
|
-
|
|
54003
|
-
|
|
54004
|
-
|
|
54005
|
-
|
|
54006
|
-
|
|
54007
|
-
|
|
54008
|
-
|
|
54009
|
-
|
|
54010
|
-
|
|
54011
|
-
|
|
54012
|
-
|
|
54013
|
-
|
|
54014
|
-
|
|
54015
|
-
|
|
54016
|
-
|
|
54017
|
-
|
|
54018
|
-
|
|
54019
|
-
|
|
54020
|
-
|
|
54021
|
-
|
|
54022
|
-
|
|
54023
|
-
|
|
54024
|
-
|
|
54025
|
-
|
|
54026
|
-
|
|
54027
|
-
|
|
54028
|
-
|
|
54029
|
-
|
|
54030
|
-
|
|
54031
|
-
|
|
54032
|
-
|
|
54033
|
-
|
|
54034
|
-
|
|
54035
|
-
|
|
54036
|
-
|
|
54037
|
-
|
|
54038
|
-
|
|
54039
|
-
|
|
54040
|
-
|
|
54041
|
-
|
|
54042
|
-
|
|
54043
|
-
|
|
54044
|
-
|
|
54045
|
-
|
|
54046
|
-
|
|
54047
|
-
|
|
54048
|
-
|
|
54049
|
-
|
|
54050
|
-
|
|
54051
|
-
|
|
54052
|
-
|
|
54053
|
-
|
|
54054
|
-
|
|
54055
|
-
|
|
54056
|
-
|
|
54057
|
-
|
|
54058
|
-
|
|
54059
|
-
|
|
54525
|
+
// src/text-style/index.ts
|
|
54526
|
+
var MAX_FIND_CHILD_SPAN_DEPTH = 20;
|
|
54527
|
+
var findChildSpans = (element, depth = 0) => {
|
|
54528
|
+
const childSpans = [];
|
|
54529
|
+
if (!element.children.length || depth > MAX_FIND_CHILD_SPAN_DEPTH) {
|
|
54530
|
+
return childSpans;
|
|
54531
|
+
}
|
|
54532
|
+
Array.from(element.children).forEach((child) => {
|
|
54533
|
+
if (child.tagName === "SPAN") {
|
|
54534
|
+
childSpans.push(child);
|
|
54535
|
+
} else if (child.children.length) {
|
|
54536
|
+
childSpans.push(...findChildSpans(child, depth + 1));
|
|
54537
|
+
}
|
|
54538
|
+
});
|
|
54539
|
+
return childSpans;
|
|
54540
|
+
};
|
|
54541
|
+
var mergeNestedSpanStyles = (element) => {
|
|
54542
|
+
if (!element.children.length) {
|
|
54543
|
+
return;
|
|
54544
|
+
}
|
|
54545
|
+
const childSpans = findChildSpans(element);
|
|
54546
|
+
if (!childSpans) {
|
|
54547
|
+
return;
|
|
54548
|
+
}
|
|
54549
|
+
childSpans.forEach((childSpan) => {
|
|
54550
|
+
var _a, _b;
|
|
54551
|
+
const childStyle = childSpan.getAttribute("style");
|
|
54552
|
+
const closestParentSpanStyleOfChild = (_b = (_a = childSpan.parentElement) == null ? void 0 : _a.closest("span")) == null ? void 0 : _b.getAttribute("style");
|
|
54553
|
+
childSpan.setAttribute("style", `${closestParentSpanStyleOfChild};${childStyle}`);
|
|
54554
|
+
});
|
|
54555
|
+
};
|
|
54556
|
+
var TextStyle = Mark.create({
|
|
54557
|
+
name: "textStyle",
|
|
54558
|
+
priority: 101,
|
|
54559
|
+
addOptions() {
|
|
54560
|
+
return {
|
|
54561
|
+
HTMLAttributes: {},
|
|
54562
|
+
mergeNestedSpanStyles: true
|
|
54563
|
+
};
|
|
54564
|
+
},
|
|
54565
|
+
parseHTML() {
|
|
54566
|
+
return [
|
|
54567
|
+
{
|
|
54568
|
+
tag: "span",
|
|
54569
|
+
consuming: false,
|
|
54570
|
+
getAttrs: (element) => {
|
|
54571
|
+
const hasStyles = element.hasAttribute("style");
|
|
54572
|
+
if (!hasStyles) {
|
|
54573
|
+
return false;
|
|
54574
|
+
}
|
|
54575
|
+
if (this.options.mergeNestedSpanStyles) {
|
|
54576
|
+
mergeNestedSpanStyles(element);
|
|
54577
|
+
}
|
|
54578
|
+
return {};
|
|
54579
|
+
}
|
|
54580
|
+
}
|
|
54581
|
+
];
|
|
54582
|
+
},
|
|
54583
|
+
renderHTML({ HTMLAttributes }) {
|
|
54584
|
+
return ["span", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
|
|
54585
|
+
},
|
|
54586
|
+
addCommands() {
|
|
54587
|
+
return {
|
|
54588
|
+
toggleTextStyle: (attributes) => ({ commands }) => {
|
|
54589
|
+
return commands.toggleMark(this.name, attributes);
|
|
54590
|
+
},
|
|
54591
|
+
removeEmptyTextStyle: () => ({ tr }) => {
|
|
54592
|
+
const { selection } = tr;
|
|
54593
|
+
tr.doc.nodesBetween(selection.from, selection.to, (node, pos) => {
|
|
54594
|
+
if (node.isTextblock) {
|
|
54595
|
+
return true;
|
|
54596
|
+
}
|
|
54597
|
+
if (!node.marks.filter((mark) => mark.type === this.type).some((mark) => Object.values(mark.attrs).some((value) => !!value))) {
|
|
54598
|
+
tr.removeMark(pos, pos + node.nodeSize, this.type);
|
|
54599
|
+
}
|
|
54600
|
+
});
|
|
54601
|
+
return true;
|
|
54602
|
+
}
|
|
54603
|
+
};
|
|
54604
|
+
}
|
|
54605
|
+
});
|
|
54606
|
+
var BackgroundColor = Extension.create({
|
|
54607
|
+
name: "backgroundColor",
|
|
54608
|
+
addOptions() {
|
|
54609
|
+
return {
|
|
54610
|
+
types: ["textStyle"]
|
|
54611
|
+
};
|
|
54612
|
+
},
|
|
54613
|
+
addGlobalAttributes() {
|
|
54614
|
+
return [
|
|
54615
|
+
{
|
|
54616
|
+
types: this.options.types,
|
|
54617
|
+
attributes: {
|
|
54618
|
+
backgroundColor: {
|
|
54619
|
+
default: null,
|
|
54620
|
+
parseHTML: (element) => {
|
|
54621
|
+
var _a;
|
|
54622
|
+
const styleAttr = element.getAttribute("style");
|
|
54623
|
+
if (styleAttr) {
|
|
54624
|
+
const decls = styleAttr.split(";").map((s) => s.trim()).filter(Boolean);
|
|
54625
|
+
for (let i = decls.length - 1; i >= 0; i -= 1) {
|
|
54626
|
+
const parts = decls[i].split(":");
|
|
54627
|
+
if (parts.length >= 2) {
|
|
54628
|
+
const prop = parts[0].trim().toLowerCase();
|
|
54629
|
+
const val = parts.slice(1).join(":").trim();
|
|
54630
|
+
if (prop === "background-color") {
|
|
54631
|
+
return val.replace(/['"]+/g, "");
|
|
54632
|
+
}
|
|
54633
|
+
}
|
|
54634
|
+
}
|
|
54635
|
+
}
|
|
54636
|
+
return (_a = element.style.backgroundColor) == null ? void 0 : _a.replace(/['"]+/g, "");
|
|
54637
|
+
},
|
|
54638
|
+
renderHTML: (attributes) => {
|
|
54639
|
+
if (!attributes.backgroundColor) {
|
|
54640
|
+
return {};
|
|
54641
|
+
}
|
|
54642
|
+
return {
|
|
54643
|
+
style: `background-color: ${attributes.backgroundColor}`
|
|
54644
|
+
};
|
|
54645
|
+
}
|
|
54646
|
+
}
|
|
54647
|
+
}
|
|
54648
|
+
}
|
|
54649
|
+
];
|
|
54650
|
+
},
|
|
54651
|
+
addCommands() {
|
|
54652
|
+
return {
|
|
54653
|
+
setBackgroundColor: (backgroundColor) => ({ chain }) => {
|
|
54654
|
+
return chain().setMark("textStyle", { backgroundColor }).run();
|
|
54655
|
+
},
|
|
54656
|
+
unsetBackgroundColor: () => ({ chain }) => {
|
|
54657
|
+
return chain().setMark("textStyle", { backgroundColor: null }).removeEmptyTextStyle().run();
|
|
54658
|
+
}
|
|
54659
|
+
};
|
|
54660
|
+
}
|
|
54661
|
+
});
|
|
54662
|
+
var Color = Extension.create({
|
|
54663
|
+
name: "color",
|
|
54664
|
+
addOptions() {
|
|
54665
|
+
return {
|
|
54666
|
+
types: ["textStyle"]
|
|
54667
|
+
};
|
|
54668
|
+
},
|
|
54669
|
+
addGlobalAttributes() {
|
|
54670
|
+
return [
|
|
54671
|
+
{
|
|
54672
|
+
types: this.options.types,
|
|
54673
|
+
attributes: {
|
|
54674
|
+
color: {
|
|
54675
|
+
default: null,
|
|
54676
|
+
parseHTML: (element) => {
|
|
54677
|
+
var _a;
|
|
54678
|
+
const styleAttr = element.getAttribute("style");
|
|
54679
|
+
if (styleAttr) {
|
|
54680
|
+
const decls = styleAttr.split(";").map((s) => s.trim()).filter(Boolean);
|
|
54681
|
+
for (let i = decls.length - 1; i >= 0; i -= 1) {
|
|
54682
|
+
const parts = decls[i].split(":");
|
|
54683
|
+
if (parts.length >= 2) {
|
|
54684
|
+
const prop = parts[0].trim().toLowerCase();
|
|
54685
|
+
const val = parts.slice(1).join(":").trim();
|
|
54686
|
+
if (prop === "color") {
|
|
54687
|
+
return val.replace(/['"]+/g, "");
|
|
54688
|
+
}
|
|
54689
|
+
}
|
|
54690
|
+
}
|
|
54691
|
+
}
|
|
54692
|
+
return (_a = element.style.color) == null ? void 0 : _a.replace(/['"]+/g, "");
|
|
54693
|
+
},
|
|
54694
|
+
renderHTML: (attributes) => {
|
|
54695
|
+
if (!attributes.color) {
|
|
54696
|
+
return {};
|
|
54697
|
+
}
|
|
54698
|
+
return {
|
|
54699
|
+
style: `color: ${attributes.color}`
|
|
54700
|
+
};
|
|
54701
|
+
}
|
|
54702
|
+
}
|
|
54703
|
+
}
|
|
54704
|
+
}
|
|
54705
|
+
];
|
|
54706
|
+
},
|
|
54707
|
+
addCommands() {
|
|
54708
|
+
return {
|
|
54709
|
+
setColor: (color) => ({ chain }) => {
|
|
54710
|
+
return chain().setMark("textStyle", { color }).run();
|
|
54711
|
+
},
|
|
54712
|
+
unsetColor: () => ({ chain }) => {
|
|
54713
|
+
return chain().setMark("textStyle", { color: null }).removeEmptyTextStyle().run();
|
|
54714
|
+
}
|
|
54715
|
+
};
|
|
54716
|
+
}
|
|
54717
|
+
});
|
|
54718
|
+
var FontFamily = Extension.create({
|
|
54719
|
+
name: "fontFamily",
|
|
54720
|
+
addOptions() {
|
|
54721
|
+
return {
|
|
54722
|
+
types: ["textStyle"]
|
|
54723
|
+
};
|
|
54724
|
+
},
|
|
54725
|
+
addGlobalAttributes() {
|
|
54726
|
+
return [
|
|
54727
|
+
{
|
|
54728
|
+
types: this.options.types,
|
|
54729
|
+
attributes: {
|
|
54730
|
+
fontFamily: {
|
|
54731
|
+
default: null,
|
|
54732
|
+
parseHTML: (element) => element.style.fontFamily,
|
|
54733
|
+
renderHTML: (attributes) => {
|
|
54734
|
+
if (!attributes.fontFamily) {
|
|
54735
|
+
return {};
|
|
54736
|
+
}
|
|
54737
|
+
return {
|
|
54738
|
+
style: `font-family: ${attributes.fontFamily}`
|
|
54739
|
+
};
|
|
54740
|
+
}
|
|
54741
|
+
}
|
|
54742
|
+
}
|
|
54743
|
+
}
|
|
54744
|
+
];
|
|
54745
|
+
},
|
|
54746
|
+
addCommands() {
|
|
54747
|
+
return {
|
|
54748
|
+
setFontFamily: (fontFamily) => ({ chain }) => {
|
|
54749
|
+
return chain().setMark("textStyle", { fontFamily }).run();
|
|
54750
|
+
},
|
|
54751
|
+
unsetFontFamily: () => ({ chain }) => {
|
|
54752
|
+
return chain().setMark("textStyle", { fontFamily: null }).removeEmptyTextStyle().run();
|
|
54753
|
+
}
|
|
54754
|
+
};
|
|
54755
|
+
}
|
|
54756
|
+
});
|
|
54757
|
+
var FontSize = Extension.create({
|
|
54758
|
+
name: "fontSize",
|
|
54759
|
+
addOptions() {
|
|
54760
|
+
return {
|
|
54761
|
+
types: ["textStyle"]
|
|
54762
|
+
};
|
|
54763
|
+
},
|
|
54764
|
+
addGlobalAttributes() {
|
|
54765
|
+
return [
|
|
54766
|
+
{
|
|
54767
|
+
types: this.options.types,
|
|
54768
|
+
attributes: {
|
|
54769
|
+
fontSize: {
|
|
54770
|
+
default: null,
|
|
54771
|
+
parseHTML: (element) => element.style.fontSize,
|
|
54772
|
+
renderHTML: (attributes) => {
|
|
54773
|
+
if (!attributes.fontSize) {
|
|
54774
|
+
return {};
|
|
54775
|
+
}
|
|
54776
|
+
return {
|
|
54777
|
+
style: `font-size: ${attributes.fontSize}`
|
|
54778
|
+
};
|
|
54779
|
+
}
|
|
54780
|
+
}
|
|
54781
|
+
}
|
|
54782
|
+
}
|
|
54783
|
+
];
|
|
54784
|
+
},
|
|
54785
|
+
addCommands() {
|
|
54786
|
+
return {
|
|
54787
|
+
setFontSize: (fontSize) => ({ chain }) => {
|
|
54788
|
+
return chain().setMark("textStyle", { fontSize }).run();
|
|
54789
|
+
},
|
|
54790
|
+
unsetFontSize: () => ({ chain }) => {
|
|
54791
|
+
return chain().setMark("textStyle", { fontSize: null }).removeEmptyTextStyle().run();
|
|
54792
|
+
}
|
|
54793
|
+
};
|
|
54794
|
+
}
|
|
54795
|
+
});
|
|
54796
|
+
var LineHeight = Extension.create({
|
|
54797
|
+
name: "lineHeight",
|
|
54798
|
+
addOptions() {
|
|
54799
|
+
return {
|
|
54800
|
+
types: ["textStyle"]
|
|
54801
|
+
};
|
|
54802
|
+
},
|
|
54803
|
+
addGlobalAttributes() {
|
|
54804
|
+
return [
|
|
54805
|
+
{
|
|
54806
|
+
types: this.options.types,
|
|
54807
|
+
attributes: {
|
|
54808
|
+
lineHeight: {
|
|
54809
|
+
default: null,
|
|
54810
|
+
parseHTML: (element) => element.style.lineHeight,
|
|
54811
|
+
renderHTML: (attributes) => {
|
|
54812
|
+
if (!attributes.lineHeight) {
|
|
54813
|
+
return {};
|
|
54814
|
+
}
|
|
54815
|
+
return {
|
|
54816
|
+
style: `line-height: ${attributes.lineHeight}`
|
|
54817
|
+
};
|
|
54818
|
+
}
|
|
54819
|
+
}
|
|
54820
|
+
}
|
|
54821
|
+
}
|
|
54822
|
+
];
|
|
54823
|
+
},
|
|
54824
|
+
addCommands() {
|
|
54825
|
+
return {
|
|
54826
|
+
setLineHeight: (lineHeight) => ({ chain }) => {
|
|
54827
|
+
return chain().setMark("textStyle", { lineHeight }).run();
|
|
54828
|
+
},
|
|
54829
|
+
unsetLineHeight: () => ({ chain }) => {
|
|
54830
|
+
return chain().setMark("textStyle", { lineHeight: null }).removeEmptyTextStyle().run();
|
|
54831
|
+
}
|
|
54832
|
+
};
|
|
54833
|
+
}
|
|
54834
|
+
});
|
|
54835
|
+
Extension.create({
|
|
54836
|
+
name: "textStyleKit",
|
|
54837
|
+
addExtensions() {
|
|
54838
|
+
const extensions = [];
|
|
54839
|
+
if (this.options.backgroundColor !== false) {
|
|
54840
|
+
extensions.push(BackgroundColor.configure(this.options.backgroundColor));
|
|
54841
|
+
}
|
|
54842
|
+
if (this.options.color !== false) {
|
|
54843
|
+
extensions.push(Color.configure(this.options.color));
|
|
54844
|
+
}
|
|
54845
|
+
if (this.options.fontFamily !== false) {
|
|
54846
|
+
extensions.push(FontFamily.configure(this.options.fontFamily));
|
|
54847
|
+
}
|
|
54848
|
+
if (this.options.fontSize !== false) {
|
|
54849
|
+
extensions.push(FontSize.configure(this.options.fontSize));
|
|
54850
|
+
}
|
|
54851
|
+
if (this.options.lineHeight !== false) {
|
|
54852
|
+
extensions.push(LineHeight.configure(this.options.lineHeight));
|
|
54853
|
+
}
|
|
54854
|
+
if (this.options.textStyle !== false) {
|
|
54855
|
+
extensions.push(TextStyle.configure(this.options.textStyle));
|
|
54856
|
+
}
|
|
54857
|
+
return extensions;
|
|
54858
|
+
}
|
|
54859
|
+
});
|
|
54860
|
+
|
|
54861
|
+
// src/highlight.ts
|
|
54862
|
+
var inputRegex$1 = /(?:^|\s)(==(?!\s+==)((?:[^=]+))==(?!\s+==))$/;
|
|
54863
|
+
var pasteRegex = /(?:^|\s)(==(?!\s+==)((?:[^=]+))==(?!\s+==))/g;
|
|
54864
|
+
var Highlight = Mark.create({
|
|
54865
|
+
name: "highlight",
|
|
54866
|
+
addOptions() {
|
|
54867
|
+
return {
|
|
54868
|
+
multicolor: false,
|
|
54869
|
+
HTMLAttributes: {}
|
|
54870
|
+
};
|
|
54871
|
+
},
|
|
54872
|
+
addAttributes() {
|
|
54873
|
+
if (!this.options.multicolor) {
|
|
54874
|
+
return {};
|
|
54875
|
+
}
|
|
54876
|
+
return {
|
|
54877
|
+
color: {
|
|
54878
|
+
default: null,
|
|
54879
|
+
parseHTML: (element) => element.getAttribute("data-color") || element.style.backgroundColor,
|
|
54880
|
+
renderHTML: (attributes) => {
|
|
54881
|
+
if (!attributes.color) {
|
|
54882
|
+
return {};
|
|
54883
|
+
}
|
|
54884
|
+
return {
|
|
54885
|
+
"data-color": attributes.color,
|
|
54886
|
+
style: `background-color: ${attributes.color}; color: inherit`
|
|
54887
|
+
};
|
|
54888
|
+
}
|
|
54889
|
+
}
|
|
54890
|
+
};
|
|
54891
|
+
},
|
|
54892
|
+
parseHTML() {
|
|
54893
|
+
return [
|
|
54894
|
+
{
|
|
54895
|
+
tag: "mark"
|
|
54896
|
+
}
|
|
54897
|
+
];
|
|
54898
|
+
},
|
|
54899
|
+
renderHTML({ HTMLAttributes }) {
|
|
54900
|
+
return ["mark", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
|
|
54901
|
+
},
|
|
54902
|
+
renderMarkdown: (node, h) => {
|
|
54903
|
+
return `==${h.renderChildren(node)}==`;
|
|
54904
|
+
},
|
|
54905
|
+
parseMarkdown: (token, h) => {
|
|
54906
|
+
return h.applyMark("highlight", h.parseInline(token.tokens || []));
|
|
54907
|
+
},
|
|
54908
|
+
markdownTokenizer: {
|
|
54909
|
+
name: "highlight",
|
|
54910
|
+
level: "inline",
|
|
54911
|
+
start: (src) => src.indexOf("=="),
|
|
54912
|
+
tokenize(src, _, h) {
|
|
54913
|
+
const rule = /^(==)([^=]+)(==)/;
|
|
54914
|
+
const match = rule.exec(src);
|
|
54915
|
+
if (match) {
|
|
54916
|
+
const innerContent = match[2].trim();
|
|
54917
|
+
const children = h.inlineTokens(innerContent);
|
|
54918
|
+
return {
|
|
54919
|
+
type: "highlight",
|
|
54920
|
+
raw: match[0],
|
|
54921
|
+
text: innerContent,
|
|
54922
|
+
tokens: children
|
|
54923
|
+
};
|
|
54924
|
+
}
|
|
54925
|
+
}
|
|
54926
|
+
},
|
|
54927
|
+
addCommands() {
|
|
54928
|
+
return {
|
|
54929
|
+
setHighlight: (attributes) => ({ commands }) => {
|
|
54930
|
+
return commands.setMark(this.name, attributes);
|
|
54931
|
+
},
|
|
54932
|
+
toggleHighlight: (attributes) => ({ commands }) => {
|
|
54933
|
+
return commands.toggleMark(this.name, attributes);
|
|
54934
|
+
},
|
|
54935
|
+
unsetHighlight: () => ({ commands }) => {
|
|
54936
|
+
return commands.unsetMark(this.name);
|
|
54937
|
+
}
|
|
54938
|
+
};
|
|
54939
|
+
},
|
|
54940
|
+
addKeyboardShortcuts() {
|
|
54941
|
+
return {
|
|
54942
|
+
"Mod-Shift-h": () => this.editor.commands.toggleHighlight()
|
|
54943
|
+
};
|
|
54944
|
+
},
|
|
54945
|
+
addInputRules() {
|
|
54946
|
+
return [
|
|
54947
|
+
markInputRule({
|
|
54948
|
+
find: inputRegex$1,
|
|
54949
|
+
type: this.type
|
|
54950
|
+
})
|
|
54951
|
+
];
|
|
54952
|
+
},
|
|
54953
|
+
addPasteRules() {
|
|
54954
|
+
return [
|
|
54955
|
+
markPasteRule({
|
|
54956
|
+
find: pasteRegex,
|
|
54957
|
+
type: this.type
|
|
54958
|
+
})
|
|
54959
|
+
];
|
|
54960
|
+
}
|
|
54961
|
+
});
|
|
54962
|
+
|
|
54963
|
+
// src/text-align.ts
|
|
54964
|
+
var TextAlign = Extension.create({
|
|
54965
|
+
name: "textAlign",
|
|
54966
|
+
addOptions() {
|
|
54967
|
+
return {
|
|
54968
|
+
types: [],
|
|
54969
|
+
alignments: ["left", "center", "right", "justify"],
|
|
54970
|
+
defaultAlignment: null
|
|
54971
|
+
};
|
|
54972
|
+
},
|
|
54973
|
+
addGlobalAttributes() {
|
|
54974
|
+
return [
|
|
54975
|
+
{
|
|
54976
|
+
types: this.options.types,
|
|
54977
|
+
attributes: {
|
|
54978
|
+
textAlign: {
|
|
54979
|
+
default: this.options.defaultAlignment,
|
|
54980
|
+
parseHTML: (element) => {
|
|
54981
|
+
const alignment = element.style.textAlign;
|
|
54982
|
+
return this.options.alignments.includes(alignment) ? alignment : this.options.defaultAlignment;
|
|
54983
|
+
},
|
|
54984
|
+
renderHTML: (attributes) => {
|
|
54985
|
+
if (!attributes.textAlign) {
|
|
54986
|
+
return {};
|
|
54987
|
+
}
|
|
54988
|
+
return { style: `text-align: ${attributes.textAlign}` };
|
|
54989
|
+
}
|
|
54990
|
+
}
|
|
54991
|
+
}
|
|
54992
|
+
}
|
|
54993
|
+
];
|
|
54994
|
+
},
|
|
54995
|
+
addCommands() {
|
|
54996
|
+
return {
|
|
54997
|
+
setTextAlign: (alignment) => ({ commands }) => {
|
|
54998
|
+
if (!this.options.alignments.includes(alignment)) {
|
|
54999
|
+
return false;
|
|
55000
|
+
}
|
|
55001
|
+
return this.options.types.map((type) => commands.updateAttributes(type, { textAlign: alignment })).some((response) => response);
|
|
55002
|
+
},
|
|
55003
|
+
unsetTextAlign: () => ({ commands }) => {
|
|
55004
|
+
return this.options.types.map((type) => commands.resetAttributes(type, "textAlign")).some((response) => response);
|
|
55005
|
+
},
|
|
55006
|
+
toggleTextAlign: (alignment) => ({ editor, commands }) => {
|
|
55007
|
+
if (!this.options.alignments.includes(alignment)) {
|
|
55008
|
+
return false;
|
|
55009
|
+
}
|
|
55010
|
+
if (editor.isActive({ textAlign: alignment })) {
|
|
55011
|
+
return commands.unsetTextAlign();
|
|
55012
|
+
}
|
|
55013
|
+
return commands.setTextAlign(alignment);
|
|
55014
|
+
}
|
|
55015
|
+
};
|
|
55016
|
+
},
|
|
55017
|
+
addKeyboardShortcuts() {
|
|
55018
|
+
return {
|
|
55019
|
+
"Mod-Shift-l": () => this.editor.commands.setTextAlign("left"),
|
|
55020
|
+
"Mod-Shift-e": () => this.editor.commands.setTextAlign("center"),
|
|
55021
|
+
"Mod-Shift-r": () => this.editor.commands.setTextAlign("right"),
|
|
55022
|
+
"Mod-Shift-j": () => this.editor.commands.setTextAlign("justify")
|
|
55023
|
+
};
|
|
55024
|
+
}
|
|
55025
|
+
});
|
|
55026
|
+
|
|
55027
|
+
// src/image.ts
|
|
55028
|
+
var inputRegex = /(?:^|\s)(!\[(.+|:?)]\((\S+)(?:(?:\s+)["'](\S+)["'])?\))$/;
|
|
55029
|
+
var Image = Node3.create({
|
|
55030
|
+
name: "image",
|
|
55031
|
+
addOptions() {
|
|
55032
|
+
return {
|
|
55033
|
+
inline: false,
|
|
55034
|
+
allowBase64: false,
|
|
55035
|
+
HTMLAttributes: {},
|
|
55036
|
+
resize: false
|
|
55037
|
+
};
|
|
55038
|
+
},
|
|
55039
|
+
inline() {
|
|
55040
|
+
return this.options.inline;
|
|
55041
|
+
},
|
|
55042
|
+
group() {
|
|
55043
|
+
return this.options.inline ? "inline" : "block";
|
|
55044
|
+
},
|
|
55045
|
+
draggable: true,
|
|
55046
|
+
addAttributes() {
|
|
55047
|
+
return {
|
|
55048
|
+
src: {
|
|
55049
|
+
default: null
|
|
55050
|
+
},
|
|
55051
|
+
alt: {
|
|
55052
|
+
default: null
|
|
55053
|
+
},
|
|
55054
|
+
title: {
|
|
55055
|
+
default: null
|
|
55056
|
+
},
|
|
55057
|
+
width: {
|
|
55058
|
+
default: null
|
|
55059
|
+
},
|
|
55060
|
+
height: {
|
|
55061
|
+
default: null
|
|
55062
|
+
}
|
|
55063
|
+
};
|
|
55064
|
+
},
|
|
55065
|
+
parseHTML() {
|
|
55066
|
+
return [
|
|
55067
|
+
{
|
|
55068
|
+
tag: this.options.allowBase64 ? "img[src]" : 'img[src]:not([src^="data:"])'
|
|
55069
|
+
}
|
|
55070
|
+
];
|
|
55071
|
+
},
|
|
55072
|
+
renderHTML({ HTMLAttributes }) {
|
|
55073
|
+
return ["img", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)];
|
|
55074
|
+
},
|
|
55075
|
+
parseMarkdown: (token, helpers) => {
|
|
55076
|
+
return helpers.createNode("image", {
|
|
55077
|
+
src: token.href,
|
|
55078
|
+
title: token.title,
|
|
55079
|
+
alt: token.text
|
|
55080
|
+
});
|
|
55081
|
+
},
|
|
55082
|
+
renderMarkdown: (node) => {
|
|
55083
|
+
var _a, _b, _c, _d, _e, _f;
|
|
55084
|
+
const src = (_b = (_a = node.attrs) == null ? void 0 : _a.src) != null ? _b : "";
|
|
55085
|
+
const alt = (_d = (_c = node.attrs) == null ? void 0 : _c.alt) != null ? _d : "";
|
|
55086
|
+
const title = (_f = (_e = node.attrs) == null ? void 0 : _e.title) != null ? _f : "";
|
|
55087
|
+
return title ? `` : ``;
|
|
55088
|
+
},
|
|
55089
|
+
addNodeView() {
|
|
55090
|
+
if (!this.options.resize || !this.options.resize.enabled || typeof document === "undefined") {
|
|
55091
|
+
return null;
|
|
55092
|
+
}
|
|
55093
|
+
const { directions, minWidth, minHeight, alwaysPreserveAspectRatio } = this.options.resize;
|
|
55094
|
+
return ({ node, getPos, HTMLAttributes, editor }) => {
|
|
55095
|
+
const el = document.createElement("img");
|
|
55096
|
+
Object.entries(HTMLAttributes).forEach(([key, value]) => {
|
|
55097
|
+
if (value != null) {
|
|
55098
|
+
switch (key) {
|
|
55099
|
+
case "width":
|
|
55100
|
+
case "height":
|
|
55101
|
+
break;
|
|
55102
|
+
default:
|
|
55103
|
+
el.setAttribute(key, value);
|
|
55104
|
+
break;
|
|
55105
|
+
}
|
|
55106
|
+
}
|
|
55107
|
+
});
|
|
55108
|
+
el.src = HTMLAttributes.src;
|
|
55109
|
+
const nodeView = new ResizableNodeView({
|
|
55110
|
+
element: el,
|
|
55111
|
+
editor,
|
|
55112
|
+
node,
|
|
55113
|
+
getPos,
|
|
55114
|
+
onResize: (width, height) => {
|
|
55115
|
+
el.style.width = `${width}px`;
|
|
55116
|
+
el.style.height = `${height}px`;
|
|
55117
|
+
},
|
|
55118
|
+
onCommit: (width, height) => {
|
|
55119
|
+
const pos = getPos();
|
|
55120
|
+
if (pos === void 0) {
|
|
55121
|
+
return;
|
|
55122
|
+
}
|
|
55123
|
+
this.editor.chain().setNodeSelection(pos).updateAttributes(this.name, {
|
|
55124
|
+
width,
|
|
55125
|
+
height
|
|
55126
|
+
}).run();
|
|
55127
|
+
},
|
|
55128
|
+
onUpdate: (updatedNode, _decorations, _innerDecorations) => {
|
|
55129
|
+
if (updatedNode.type !== node.type) {
|
|
55130
|
+
return false;
|
|
55131
|
+
}
|
|
55132
|
+
return true;
|
|
55133
|
+
},
|
|
55134
|
+
options: {
|
|
55135
|
+
directions,
|
|
55136
|
+
min: {
|
|
55137
|
+
width: minWidth,
|
|
55138
|
+
height: minHeight
|
|
55139
|
+
},
|
|
55140
|
+
preserveAspectRatio: alwaysPreserveAspectRatio === true
|
|
55141
|
+
}
|
|
55142
|
+
});
|
|
55143
|
+
const dom = nodeView.dom;
|
|
55144
|
+
dom.style.visibility = "hidden";
|
|
55145
|
+
dom.style.pointerEvents = "none";
|
|
55146
|
+
el.onload = () => {
|
|
55147
|
+
dom.style.visibility = "";
|
|
55148
|
+
dom.style.pointerEvents = "";
|
|
55149
|
+
};
|
|
55150
|
+
return nodeView;
|
|
55151
|
+
};
|
|
55152
|
+
},
|
|
55153
|
+
addCommands() {
|
|
55154
|
+
return {
|
|
55155
|
+
setImage: (options) => ({ commands }) => {
|
|
55156
|
+
return commands.insertContent({
|
|
55157
|
+
type: this.name,
|
|
55158
|
+
attrs: options
|
|
55159
|
+
});
|
|
55160
|
+
}
|
|
55161
|
+
};
|
|
55162
|
+
},
|
|
55163
|
+
addInputRules() {
|
|
55164
|
+
return [
|
|
55165
|
+
nodeInputRule({
|
|
55166
|
+
find: inputRegex,
|
|
55167
|
+
type: this.type,
|
|
55168
|
+
getAttributes: (match) => {
|
|
55169
|
+
const [, , alt, src, title] = match;
|
|
55170
|
+
return { src, alt, title };
|
|
55171
|
+
}
|
|
55172
|
+
})
|
|
55173
|
+
];
|
|
55174
|
+
}
|
|
55175
|
+
});
|
|
55176
|
+
|
|
55177
|
+
class nxEditor extends nxDiv {
|
|
55178
|
+
#editor = null;
|
|
55179
|
+
#container = null;
|
|
55180
|
+
|
|
55181
|
+
constructor() {
|
|
55182
|
+
super();
|
|
55183
|
+
//this.attachShadow({ mode: 'open' });
|
|
55184
|
+
}
|
|
55185
|
+
|
|
55186
|
+
connectedCallback() {
|
|
55187
|
+
if (super.connectedCallback()) {
|
|
55188
|
+
this.#render();
|
|
55189
|
+
this.#initEditor();
|
|
55190
|
+
}
|
|
55191
|
+
}
|
|
55192
|
+
|
|
55193
|
+
#render() {
|
|
55194
|
+
this.shadowRoot.innerHTML = `
|
|
55195
|
+
<style>
|
|
55196
|
+
@import "https://cdn.jsdelivr.net/npm/ninegrid@${ninegrid.version}/dist/css/nxEditor.css";
|
|
55197
|
+
${ninegrid.getCustomPath(this,"nxEditor.css")}
|
|
55198
|
+
</style>
|
|
55199
|
+
|
|
55200
|
+
<div class="menu-bar">
|
|
55201
|
+
<button type="button" data-cmd="bold"><b>B</b></button>
|
|
55202
|
+
<button type="button" data-cmd="italic"><i>I</i></button>
|
|
55203
|
+
<button type="button" data-cmd="underline"><u>U</u></button>
|
|
55204
|
+
<button type="button" data-cmd="strike">S</button>
|
|
55205
|
+
|
|
55206
|
+
<input type="color" id="color-picker" title="Text Color">
|
|
55207
|
+
<button type="button" data-cmd="highlight">Highlight</button>
|
|
55208
|
+
|
|
55209
|
+
<button type="button" data-align="left">Left</button>
|
|
55210
|
+
<button type="button" data-align="center">Center</button>
|
|
55211
|
+
<button type="button" data-align="right">Right</button>
|
|
55212
|
+
|
|
55213
|
+
<button type="button" data-cmd="bulletList">Bullet</button>
|
|
55214
|
+
<button type="button" data-cmd="orderedList">Ordered</button>
|
|
55215
|
+
|
|
55216
|
+
<button type="button" id="img-btn">Image</button>
|
|
55217
|
+
|
|
55218
|
+
<button type="button" data-cmd="undo">Undo</button>
|
|
55219
|
+
<button type="button" data-cmd="redo">Redo</button>
|
|
55220
|
+
</div>
|
|
55221
|
+
<div id="editor-container"></div>
|
|
55222
|
+
`;
|
|
55223
|
+
this.#container = this.shadowRoot.querySelector('#editor-container');
|
|
55224
|
+
}
|
|
55225
|
+
|
|
55226
|
+
#initEditor() {
|
|
55227
|
+
this.#editor = new Editor({
|
|
55228
|
+
element: this.#container,
|
|
55229
|
+
extensions: [
|
|
55230
|
+
index_default,
|
|
55231
|
+
Underline,
|
|
55232
|
+
TextStyle,
|
|
55233
|
+
Color,
|
|
55234
|
+
Highlight.configure({ multicolor: true }),
|
|
55235
|
+
TextAlign.configure({ types: ['heading', 'paragraph'] }),
|
|
55236
|
+
Image.configure({ inline: true, allowBase64: true }),
|
|
55237
|
+
],
|
|
55238
|
+
content: this.originContents || '',
|
|
55239
|
+
onUpdate: ({ editor }) => {
|
|
55240
|
+
this.dispatchEvent(new CustomEvent('change', { detail: editor.getHTML() }));
|
|
55241
|
+
}
|
|
55242
|
+
});
|
|
55243
|
+
|
|
55244
|
+
this.#bindEvents();
|
|
55245
|
+
}
|
|
55246
|
+
|
|
55247
|
+
#bindEvents() {
|
|
55248
|
+
const editor = this.#editor;
|
|
55249
|
+
|
|
55250
|
+
// 일반 커맨드 버튼
|
|
55251
|
+
this.shadowRoot.querySelectorAll('[data-cmd]').forEach(btn => {
|
|
55252
|
+
btn.onclick = () => {
|
|
55253
|
+
const cmd = btn.dataset.cmd;
|
|
55254
|
+
if (cmd === 'bold') editor.chain().focus().toggleBold().run();
|
|
55255
|
+
if (cmd === 'italic') editor.chain().focus().toggleItalic().run();
|
|
55256
|
+
if (cmd === 'underline') editor.chain().focus().toggleUnderline().run();
|
|
55257
|
+
if (cmd === 'strike') editor.chain().focus().toggleStrike().run();
|
|
55258
|
+
if (cmd === 'highlight') editor.chain().focus().toggleHighlight().run();
|
|
55259
|
+
if (cmd === 'bulletList') editor.chain().focus().toggleBulletList().run();
|
|
55260
|
+
if (cmd === 'orderedList') editor.chain().focus().toggleOrderedList().run();
|
|
55261
|
+
if (cmd === 'undo') editor.chain().focus().undo().run();
|
|
55262
|
+
if (cmd === 'redo') editor.chain().focus().redo().run();
|
|
55263
|
+
};
|
|
55264
|
+
});
|
|
55265
|
+
|
|
55266
|
+
// 색상 변경
|
|
55267
|
+
this.shadowRoot.querySelector('#color-picker').oninput = (e) => {
|
|
55268
|
+
editor.chain().focus().setColor(e.target.value).run();
|
|
55269
|
+
};
|
|
55270
|
+
|
|
55271
|
+
// 정렬
|
|
55272
|
+
this.shadowRoot.querySelectorAll('[data-align]').forEach(btn => {
|
|
55273
|
+
btn.onclick = () => editor.chain().focus().setTextAlign(btn.dataset.align).run();
|
|
55274
|
+
});
|
|
55275
|
+
|
|
55276
|
+
// 이미지 삽입 (Prompt 방식)
|
|
55277
|
+
this.shadowRoot.querySelector('#img-btn').onclick = () => {
|
|
55278
|
+
const url = window.prompt('Image URL');
|
|
55279
|
+
if (url) editor.chain().focus().setImage({ src: url }).run();
|
|
55280
|
+
};
|
|
55281
|
+
}
|
|
55282
|
+
|
|
55283
|
+
setData = (value) => {
|
|
55284
|
+
if (this.#editor) {
|
|
55285
|
+
this.#editor.commands.setContent(value || "", false);
|
|
55286
|
+
} else {
|
|
55287
|
+
this.originContents = value;
|
|
55288
|
+
}
|
|
55289
|
+
}
|
|
55290
|
+
|
|
55291
|
+
getData = () => {
|
|
55292
|
+
return this.#editor ? this.#editor.getHTML() : "";
|
|
55293
|
+
}
|
|
55294
|
+
|
|
55295
|
+
disconnectedCallback() {
|
|
55296
|
+
if (this.#editor) {
|
|
55297
|
+
this.#editor.destroy();
|
|
55298
|
+
this.#editor = null;
|
|
55299
|
+
}
|
|
54060
55300
|
}
|
|
54061
55301
|
}
|
|
54062
55302
|
|