@pentestpad/tiptap-extension-figure 1.0.2 → 1.0.4
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/.idea/modules.xml +8 -0
- package/.idea/php.xml +19 -0
- package/.idea/tiptap-figure.iml +8 -0
- package/.idea/vcs.xml +6 -0
- package/README.md +3 -1
- package/demo.gif +0 -0
- package/package.json +2 -2
- package/src/component/tip-tap-image-resize-with-caption.ts +10 -8
- package/dist/component/tip-tap-image-resize-with-caption.d.ts +0 -5
- package/dist/index.cjs.js +0 -467
- package/dist/index.cjs.js.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -463
- package/dist/index.js.map +0 -1
- package/dist/utils/add-caption-controls.util.d.ts +0 -1
- package/dist/utils/add-image-alignment-controls.util.d.ts +0 -1
- package/dist/utils/add-image-resize-controls.util.d.ts +0 -1
- package/dist/utils/is-mobile-screen.util.d.ts +0 -1
- package/dist/utils/remove-image-controls.util.d.ts +0 -1
- package/dist/utils/replace-element.util.d.ts +0 -3
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="ProjectModuleManager">
|
|
4
|
+
<modules>
|
|
5
|
+
<module fileurl="file://$PROJECT_DIR$/.idea/tiptap-figure.iml" filepath="$PROJECT_DIR$/.idea/tiptap-figure.iml" />
|
|
6
|
+
</modules>
|
|
7
|
+
</component>
|
|
8
|
+
</project>
|
package/.idea/php.xml
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="MessDetectorOptionsConfiguration">
|
|
4
|
+
<option name="transferred" value="true" />
|
|
5
|
+
</component>
|
|
6
|
+
<component name="PHPCSFixerOptionsConfiguration">
|
|
7
|
+
<option name="transferred" value="true" />
|
|
8
|
+
</component>
|
|
9
|
+
<component name="PHPCodeSnifferOptionsConfiguration">
|
|
10
|
+
<option name="highlightLevel" value="WARNING" />
|
|
11
|
+
<option name="transferred" value="true" />
|
|
12
|
+
</component>
|
|
13
|
+
<component name="PhpStanOptionsConfiguration">
|
|
14
|
+
<option name="transferred" value="true" />
|
|
15
|
+
</component>
|
|
16
|
+
<component name="PsalmOptionsConfiguration">
|
|
17
|
+
<option name="transferred" value="true" />
|
|
18
|
+
</component>
|
|
19
|
+
</project>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<module type="WEB_MODULE" version="4">
|
|
3
|
+
<component name="NewModuleRootManager">
|
|
4
|
+
<content url="file://$MODULE_DIR$" />
|
|
5
|
+
<orderEntry type="inheritedJdk" />
|
|
6
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
|
7
|
+
</component>
|
|
8
|
+
</module>
|
package/.idea/vcs.xml
ADDED
package/README.md
CHANGED
package/demo.gif
ADDED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pentestpad/tiptap-extension-figure",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "An extension for Tiptap that allows you to add and edit captions for images as well as align and resize them.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.cjs.js",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"@tiptap/core": "^2.11.5",
|
|
19
19
|
"@tiptap/extension-image": "^2.11.5",
|
|
20
20
|
"@tiptap/pm": "^2.11.5",
|
|
21
|
-
"rollup": "^4.34.
|
|
21
|
+
"rollup": "^4.34.8",
|
|
22
22
|
"rollup-plugin-auto-external": "^2.0.0",
|
|
23
23
|
"rollup-plugin-postcss": "^4.0.2",
|
|
24
24
|
"rollup-plugin-sourcemaps": "^0.6.3",
|
|
@@ -99,16 +99,16 @@ const TiptapImageFigureExtension = ImageExtension.extend<ImageOptions>({
|
|
|
99
99
|
const hasCaption = node.content.size > 0;
|
|
100
100
|
|
|
101
101
|
if (hasCaption) {
|
|
102
|
+
// We should ignore src, alt and title from html attributes if the wrapper element is a figure
|
|
103
|
+
const { src, alt, title, ...figureHtmlAttributes } = HTMLAttributes;
|
|
104
|
+
|
|
102
105
|
return [
|
|
103
106
|
"figure",
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
contenteditable: false,
|
|
110
|
-
}),
|
|
111
|
-
],
|
|
107
|
+
mergeAttributes(figureHtmlAttributes, {
|
|
108
|
+
draggable: false,
|
|
109
|
+
contenteditable: false,
|
|
110
|
+
}),
|
|
111
|
+
["img", mergeAttributes(HTMLAttributes)],
|
|
112
112
|
["figcaption", 0],
|
|
113
113
|
];
|
|
114
114
|
}
|
|
@@ -175,6 +175,8 @@ const TiptapImageFigureExtension = ImageExtension.extend<ImageOptions>({
|
|
|
175
175
|
event.stopPropagation();
|
|
176
176
|
event.preventDefault();
|
|
177
177
|
|
|
178
|
+
editor.commands.setNodeSelection(getPos());
|
|
179
|
+
|
|
178
180
|
// If controls are already visible, check if another image or figure is being clicked on
|
|
179
181
|
if (this.storage.elementsVisible) {
|
|
180
182
|
const clickedElement = event.target as HTMLElement;
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { ImageOptions } from "@tiptap/extension-image";
|
|
2
|
-
export declare const inputRegex: RegExp;
|
|
3
|
-
declare const TiptapImageFigureExtension: import("@tiptap/core").Node<ImageOptions, any>;
|
|
4
|
-
export { TiptapImageFigureExtension };
|
|
5
|
-
export default TiptapImageFigureExtension;
|
package/dist/index.cjs.js
DELETED
|
@@ -1,467 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
|
-
var core = require('@tiptap/core');
|
|
6
|
-
var ImageExtension = require('@tiptap/extension-image');
|
|
7
|
-
var state = require('@tiptap/pm/state');
|
|
8
|
-
|
|
9
|
-
const isMobileScreen = () => document.documentElement.clientWidth < 768;
|
|
10
|
-
|
|
11
|
-
const removeImageControlsAndResetStyles = (clickedElement, wrapperElement, styles) => {
|
|
12
|
-
const containerContainsClickedElement = wrapperElement.contains(clickedElement);
|
|
13
|
-
if (containerContainsClickedElement) {
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
// Remove all custom UI elements and styling
|
|
17
|
-
wrapperElement.classList.remove(styles["active"]);
|
|
18
|
-
const children = Array.from(wrapperElement.children);
|
|
19
|
-
children.forEach((child) => {
|
|
20
|
-
if (child.tagName !== "IMG" && child.tagName !== "FIGCAPTION") {
|
|
21
|
-
wrapperElement.removeChild(child);
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
var leftIcon = "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2220px%22%20viewBox%3D%220%20-960%20960%20960%22%20width%3D%2220px%22%20fill%3D%22%23000000%22%3E%3Cpath%20d%3D%22M144-144v-72h672v72H144Zm0-150v-72h480v72H144Zm0-150v-72h672v72H144Zm0-150v-72h480v72H144Zm0-150v-72h672v72H144Z%22%2F%3E%3C%2Fsvg%3E";
|
|
27
|
-
|
|
28
|
-
var centerIcon = "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2220px%22%20viewBox%3D%220%20-960%20960%20960%22%20width%3D%2220px%22%20fill%3D%22%23000000%22%3E%3Cpath%20d%3D%22M144-144v-72h672v72H144Zm144-150v-72h384v72H288ZM144-444v-72h672v72H144Zm144-150v-72h384v72H288ZM144-744v-72h672v72H144Z%22%2F%3E%3C%2Fsvg%3E";
|
|
29
|
-
|
|
30
|
-
var rightIcon = "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2220px%22%20viewBox%3D%220%20-960%20960%20960%22%20width%3D%2220px%22%20fill%3D%22%23000000%22%3E%3Cpath%20d%3D%22M144-744v-72h672v72H144Zm192%20150v-72h480v72H336ZM144-444v-72h672v72H144Zm192%20150v-72h480v72H336ZM144-144v-72h672v72H144Z%22%2F%3E%3C%2Fsvg%3E";
|
|
31
|
-
|
|
32
|
-
const imageAlignmentControls = [
|
|
33
|
-
{
|
|
34
|
-
type: "left",
|
|
35
|
-
icon: leftIcon,
|
|
36
|
-
styleToApply: "margin: 0 auto 0 0;",
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
type: "center",
|
|
40
|
-
icon: centerIcon,
|
|
41
|
-
styleToApply: "margin: 0 auto;",
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
type: "right",
|
|
45
|
-
icon: rightIcon,
|
|
46
|
-
styleToApply: "margin: 0 0 0 auto;",
|
|
47
|
-
},
|
|
48
|
-
];
|
|
49
|
-
const addImageAlignmentControls = (wrapperElement, imageElement, styles, onAlign) => {
|
|
50
|
-
const imageAlignmentContainer = document.createElement("div");
|
|
51
|
-
imageAlignmentContainer.setAttribute("contenteditable", "false");
|
|
52
|
-
imageAlignmentContainer.setAttribute("class", styles["image-alignment-container"]);
|
|
53
|
-
imageAlignmentControls.forEach((imageControl) => {
|
|
54
|
-
const imageAlignmentControl = document.createElement("img");
|
|
55
|
-
imageAlignmentControl.src = imageControl.icon;
|
|
56
|
-
imageAlignmentControl.setAttribute("class", styles["image-alignment-control"]);
|
|
57
|
-
imageAlignmentControl.addEventListener("click", (event) => {
|
|
58
|
-
event.stopPropagation();
|
|
59
|
-
imageElement.style.cssText = `${imageElement.style.cssText} ${imageControl.styleToApply}`;
|
|
60
|
-
onAlign();
|
|
61
|
-
});
|
|
62
|
-
imageAlignmentContainer.appendChild(imageAlignmentControl);
|
|
63
|
-
});
|
|
64
|
-
wrapperElement.appendChild(imageAlignmentContainer);
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
const addImageResizeControls = (wrapperElement, imageElement, isResizing, startX, startWidth, styles, onResize) => {
|
|
68
|
-
const isMobile = isMobileScreen();
|
|
69
|
-
const dotPosition = isMobile ? "-8px" : "-4px";
|
|
70
|
-
const dotSize = isMobile ? 16 : 9;
|
|
71
|
-
const dotsPosition = [
|
|
72
|
-
`top: ${dotPosition}; left: ${dotPosition}; cursor: nwse-resize;`,
|
|
73
|
-
`top: ${dotPosition}; right: ${dotPosition}; cursor: nesw-resize;`,
|
|
74
|
-
`bottom: ${dotPosition}; left: ${dotPosition}; cursor: nesw-resize;`,
|
|
75
|
-
`bottom: ${dotPosition}; right: ${dotPosition}; cursor: nwse-resize;`,
|
|
76
|
-
];
|
|
77
|
-
Array.from({ length: 4 }, (_, index) => {
|
|
78
|
-
const dotElement = document.createElement("div");
|
|
79
|
-
dotElement.setAttribute("class", styles["dot-element"]);
|
|
80
|
-
dotElement.setAttribute("style", `width: ${dotSize}px; height: ${dotSize}px; ${dotsPosition[index]}`);
|
|
81
|
-
dotElement.addEventListener("mousedown", (e) => {
|
|
82
|
-
e.preventDefault();
|
|
83
|
-
isResizing = true;
|
|
84
|
-
startX = e.clientX;
|
|
85
|
-
startWidth = wrapperElement.offsetWidth;
|
|
86
|
-
const onMouseMove = (event) => {
|
|
87
|
-
if (!isResizing) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
const deltaX = index % 2 === 0 ? -(event.clientX - startX) : event.clientX - startX;
|
|
91
|
-
const newWidth = startWidth + deltaX;
|
|
92
|
-
wrapperElement.style.width = newWidth + "px";
|
|
93
|
-
imageElement.style.width = newWidth + "px";
|
|
94
|
-
};
|
|
95
|
-
const onMouseUp = () => {
|
|
96
|
-
if (isResizing) {
|
|
97
|
-
isResizing = false;
|
|
98
|
-
}
|
|
99
|
-
onResize();
|
|
100
|
-
document.removeEventListener("mousemove", onMouseMove);
|
|
101
|
-
document.removeEventListener("mouseup", onMouseUp);
|
|
102
|
-
};
|
|
103
|
-
document.addEventListener("mousemove", onMouseMove);
|
|
104
|
-
document.addEventListener("mouseup", onMouseUp);
|
|
105
|
-
});
|
|
106
|
-
dotElement.addEventListener("touchstart", (e) => {
|
|
107
|
-
e.cancelable && e.preventDefault();
|
|
108
|
-
isResizing = true;
|
|
109
|
-
startX = e.touches[0].clientX;
|
|
110
|
-
startWidth = wrapperElement.offsetWidth;
|
|
111
|
-
const onTouchMove = (e) => {
|
|
112
|
-
if (!isResizing)
|
|
113
|
-
return;
|
|
114
|
-
const deltaX = index % 2 === 0
|
|
115
|
-
? -(e.touches[0].clientX - startX)
|
|
116
|
-
: e.touches[0].clientX - startX;
|
|
117
|
-
const newWidth = startWidth + deltaX;
|
|
118
|
-
wrapperElement.style.width = newWidth + "px";
|
|
119
|
-
imageElement.style.width = newWidth + "px";
|
|
120
|
-
};
|
|
121
|
-
const onTouchEnd = () => {
|
|
122
|
-
if (isResizing) {
|
|
123
|
-
isResizing = false;
|
|
124
|
-
}
|
|
125
|
-
onResize();
|
|
126
|
-
document.removeEventListener("touchmove", onTouchMove);
|
|
127
|
-
document.removeEventListener("touchend", onTouchEnd);
|
|
128
|
-
};
|
|
129
|
-
document.addEventListener("touchmove", onTouchMove);
|
|
130
|
-
document.addEventListener("touchend", onTouchEnd);
|
|
131
|
-
}, { passive: false });
|
|
132
|
-
wrapperElement.appendChild(dotElement);
|
|
133
|
-
});
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
var closedCaptionAddIcon = "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2220px%22%20viewBox%3D%220%20-960%20960%20960%22%20width%3D%2220px%22%20fill%3D%22%23000000%22%3E%3Cpath%20d%3D%22M480-480Zm120%20288H216q-29.7%200-50.85-21.16Q144-234.32%20144-264.04v-432.24Q144-726%20165.15-747T216-768h528q29.7%200%2050.85%2021.15Q816-725.7%20816-696v288h-72v-288H216v432h384v72Zm144%2072v-72h-72v-72h72v-72h72v72h72v72h-72v72h-72ZM293.29-368h111.86Q421-368%20432-378.78q11-10.78%2011-26.72V-443h-56.14v19H312v-112h75v19h56v-37.89q0-16.11-10.64-26.61Q421.73-592%20406-592H293.01q-16.01%200-26.51%2010.71-10.5%2010.7-10.5%2026.52v148.95Q256-390%20266.72-379t26.57%2011Zm261.22%200h112.55q15.94%200%2026.44-10.78Q704-389.56%20704-405.5V-443h-56.14v19H573v-112h75v19h56v-37.89q0-16.11-10.72-26.61T666.71-592H554.85Q539-592%20528-581.29q-11%2010.7-11%2026.52v148.95Q517-390%20527.79-379q10.78%2011%2026.72%2011Z%22%2F%3E%3C%2Fsvg%3E";
|
|
137
|
-
|
|
138
|
-
var deleteIcon = "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2220px%22%20viewBox%3D%220%20-960%20960%20960%22%20width%3D%2220px%22%20fill%3D%22%23000000%22%3E%3Cpath%20d%3D%22M312-144q-29.7%200-50.85-21.15Q240-186.3%20240-216v-480h-48v-72h192v-48h192v48h192v72h-48v479.57Q720-186%20698.85-165T648-144H312Zm336-552H312v480h336v-480ZM384-288h72v-336h-72v336Zm120%200h72v-336h-72v336ZM312-696v480-480Z%22%2F%3E%3C%2Fsvg%3E";
|
|
139
|
-
|
|
140
|
-
const addCaptionControls = (wrapperElement, styles, onCaptionRemove, onCaptionAdd) => {
|
|
141
|
-
const captionControlsContainer = document.createElement("div");
|
|
142
|
-
captionControlsContainer.setAttribute("contenteditable", "false");
|
|
143
|
-
captionControlsContainer.setAttribute("class", styles["caption-controls-element"]);
|
|
144
|
-
// If wrapper element is a figure and the button doesn't already exist, add a button to remove caption
|
|
145
|
-
// Also, wrapper elements needs to become a div
|
|
146
|
-
if (wrapperElement.tagName === "FIGURE" &&
|
|
147
|
-
!wrapperElement.querySelector(styles["remove-caption-button"])) {
|
|
148
|
-
const removeCaptionButton = document.createElement("img");
|
|
149
|
-
removeCaptionButton.src = deleteIcon;
|
|
150
|
-
removeCaptionButton.setAttribute("class", styles["remove-caption-button"]);
|
|
151
|
-
removeCaptionButton.addEventListener("click", (event) => {
|
|
152
|
-
event.stopPropagation();
|
|
153
|
-
onCaptionRemove();
|
|
154
|
-
});
|
|
155
|
-
captionControlsContainer.appendChild(removeCaptionButton);
|
|
156
|
-
wrapperElement.appendChild(captionControlsContainer);
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
// If wrapper element is a div and the button doesn't already exist, add a button to add caption
|
|
160
|
-
if (wrapperElement.tagName === "DIV" &&
|
|
161
|
-
!wrapperElement.querySelector(styles["add-caption-button"])) {
|
|
162
|
-
const addCaptionButton = document.createElement("img");
|
|
163
|
-
addCaptionButton.src = closedCaptionAddIcon;
|
|
164
|
-
addCaptionButton.setAttribute("class", styles["add-caption-button"]);
|
|
165
|
-
addCaptionButton.addEventListener("click", (event) => {
|
|
166
|
-
event.stopPropagation();
|
|
167
|
-
onCaptionAdd();
|
|
168
|
-
});
|
|
169
|
-
captionControlsContainer.appendChild(addCaptionButton);
|
|
170
|
-
wrapperElement.appendChild(captionControlsContainer);
|
|
171
|
-
}
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
const changeFigureToImage = (editor, wrapperElement) => {
|
|
175
|
-
const { state: state$1, view } = editor;
|
|
176
|
-
const { tr } = state$1;
|
|
177
|
-
const imageWrapperElement = document.createElement("div");
|
|
178
|
-
const oldAttributes = wrapperElement.attributes;
|
|
179
|
-
const newAttributes = imageWrapperElement.attributes;
|
|
180
|
-
const imageWrapperPosition = view.posAtDOM(wrapperElement, 0);
|
|
181
|
-
// Copy attributes
|
|
182
|
-
for (let i = 0, len = oldAttributes.length; i < len; i++) {
|
|
183
|
-
newAttributes.setNamedItem(oldAttributes.item(i).cloneNode());
|
|
184
|
-
}
|
|
185
|
-
// Find the image within the old wrapper and set it as the only child of the new wrapper
|
|
186
|
-
const imageElement = wrapperElement.querySelector("img");
|
|
187
|
-
if (imageElement) {
|
|
188
|
-
imageWrapperElement.appendChild(imageElement);
|
|
189
|
-
}
|
|
190
|
-
// Replace wrapperElement with imageWrapperElement
|
|
191
|
-
wrapperElement.replaceWith(imageWrapperElement);
|
|
192
|
-
// Focus on the newly replaced wrapper element
|
|
193
|
-
const newSelection = state.TextSelection.create(tr.doc, imageWrapperPosition);
|
|
194
|
-
const transaction = tr.setSelection(newSelection);
|
|
195
|
-
view.dispatch(transaction);
|
|
196
|
-
};
|
|
197
|
-
const changeImageToFigure = (editor, wrapperElement, captionElement) => {
|
|
198
|
-
const { state: state$1, view } = editor;
|
|
199
|
-
const { tr } = state$1;
|
|
200
|
-
const figureWrapperElement = document.createElement("figure");
|
|
201
|
-
const oldAttributes = wrapperElement.attributes;
|
|
202
|
-
const newAttributes = figureWrapperElement.attributes;
|
|
203
|
-
const figureWrapperPosition = view.posAtDOM(wrapperElement, 0);
|
|
204
|
-
// Copy attributes
|
|
205
|
-
for (let i = 0, len = oldAttributes.length; i < len; i++) {
|
|
206
|
-
newAttributes.setNamedItem(oldAttributes.item(i).cloneNode());
|
|
207
|
-
}
|
|
208
|
-
// Find the image within the old wrapper and set it as the only child of the new wrapper
|
|
209
|
-
const imageElement = wrapperElement.querySelector("img");
|
|
210
|
-
if (imageElement) {
|
|
211
|
-
figureWrapperElement.appendChild(imageElement);
|
|
212
|
-
}
|
|
213
|
-
captionElement.innerHTML = "Caption";
|
|
214
|
-
figureWrapperElement.appendChild(captionElement);
|
|
215
|
-
// Replace wrapperElement with figureWrapperElement
|
|
216
|
-
wrapperElement.replaceWith(figureWrapperElement);
|
|
217
|
-
// Focus on the newly replaced wrapper element
|
|
218
|
-
const newSelection = state.TextSelection.create(tr.doc, figureWrapperPosition);
|
|
219
|
-
const transaction = tr.setSelection(newSelection);
|
|
220
|
-
view.dispatch(transaction);
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
function styleInject(css, ref) {
|
|
224
|
-
if ( ref === void 0 ) ref = {};
|
|
225
|
-
var insertAt = ref.insertAt;
|
|
226
|
-
|
|
227
|
-
if (typeof document === 'undefined') { return; }
|
|
228
|
-
|
|
229
|
-
var head = document.head || document.getElementsByTagName('head')[0];
|
|
230
|
-
var style = document.createElement('style');
|
|
231
|
-
style.type = 'text/css';
|
|
232
|
-
|
|
233
|
-
if (insertAt === 'top') {
|
|
234
|
-
if (head.firstChild) {
|
|
235
|
-
head.insertBefore(style, head.firstChild);
|
|
236
|
-
} else {
|
|
237
|
-
head.appendChild(style);
|
|
238
|
-
}
|
|
239
|
-
} else {
|
|
240
|
-
head.appendChild(style);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if (style.styleSheet) {
|
|
244
|
-
style.styleSheet.cssText = css;
|
|
245
|
-
} else {
|
|
246
|
-
style.appendChild(document.createTextNode(css));
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
var css_248z = ".styles_wrapper-element__SoyDK {\n display: flex;\n flex-direction: column;\n position: relative;\n cursor: pointer;\n width: fit-content;\n margin-inline: 0;\n}\n.styles_wrapper-element__SoyDK.styles_active__kXAaT {\n border: 2px dashed #6c6c6c;\n}\n\n.styles_figure-element__wBqOu {\n}\n\n.styles_caption-element__-9Bt- {\n text-align: center;\n margin-top: 8px;\n min-height: 1em;\n margin: 0.5rem 2rem;\n padding: 0.5rem 0;\n}\n.styles_caption-element__-9Bt-:hover {\n border-radius: 4px;\n border: 2px dashed #6c6c6c;\n}\n\n.styles_caption-controls-element__Pwjxq {\n position: absolute;\n bottom: 7.5%;\n left: 50%;\n width: 40px;\n height: 35px;\n background-color: rgba(255, 255, 255, 0.7);\n border-radius: 4px;\n border: 2px solid #6c6c6c;\n cursor: pointer;\n transform: translate(-50%, -50%);\n display: flex;\n justify-content: center;\n align-items: center;\n}\n\n.styles_remove-caption-button__OzMEn,\n.styles_add-caption-button__2rKuu {\n cursor: pointer;\n font-size: 20px;\n}\n\n.styles_remove-caption-button__OzMEn:hover,\n.styles_add-caption-button__2rKuu:hover {\n opacity: 0.5;\n}\n\n.styles_dot-element__TQRBe {\n position: absolute;\n border: 1.5px solid #6c6c6c;\n border-radius: 50%;\n}\n\n.styles_image-alignment-container__5byQ2 {\n position: absolute;\n top: 0%;\n left: 50%;\n width: 100px;\n height: 25px;\n background-color: rgba(255, 255, 255, 0.7);\n border-radius: 4px;\n border: 2px solid #6c6c6c;\n cursor: pointer;\n transform: translate(-50%, -50%);\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0 10px;\n}\n\n.styles_image-alignment-control__r3rTj {\n cursor: pointer;\n font-size: 20px;\n}\n.styles_image-alignment-control__r3rTj:hover {\n opacity: 0.5;\n}\n";
|
|
251
|
-
var styles = {"wrapper-element":"styles_wrapper-element__SoyDK","active":"styles_active__kXAaT","caption-element":"styles_caption-element__-9Bt-","caption-controls-element":"styles_caption-controls-element__Pwjxq","remove-caption-button":"styles_remove-caption-button__OzMEn","add-caption-button":"styles_add-caption-button__2rKuu","dot-element":"styles_dot-element__TQRBe","image-alignment-container":"styles_image-alignment-container__5byQ2","image-alignment-control":"styles_image-alignment-control__r3rTj"};
|
|
252
|
-
styleInject(css_248z);
|
|
253
|
-
|
|
254
|
-
const inputRegex = /!\[(.+|:?)]\((\S+)(?:(?:\s+)["'](\S+)["'])?\)/;
|
|
255
|
-
const TiptapImageFigureExtension = ImageExtension.extend({
|
|
256
|
-
addOptions() {
|
|
257
|
-
return {
|
|
258
|
-
...this.parent?.(),
|
|
259
|
-
inline: false,
|
|
260
|
-
allowBase64: false,
|
|
261
|
-
HTMLAttributes: {},
|
|
262
|
-
};
|
|
263
|
-
},
|
|
264
|
-
group: "block",
|
|
265
|
-
draggable: true,
|
|
266
|
-
isolating: true,
|
|
267
|
-
content: "inline*",
|
|
268
|
-
addStorage() {
|
|
269
|
-
return {
|
|
270
|
-
elementsVisible: false,
|
|
271
|
-
currentActiveWrapper: null,
|
|
272
|
-
};
|
|
273
|
-
},
|
|
274
|
-
addAttributes() {
|
|
275
|
-
return {
|
|
276
|
-
...this.parent?.(),
|
|
277
|
-
src: {
|
|
278
|
-
default: null,
|
|
279
|
-
parseHTML: (element) => {
|
|
280
|
-
if (element.tagName === "FIGURE") {
|
|
281
|
-
const img = element.querySelector("img");
|
|
282
|
-
return img?.getAttribute("src");
|
|
283
|
-
}
|
|
284
|
-
return element.getAttribute("src");
|
|
285
|
-
},
|
|
286
|
-
},
|
|
287
|
-
alt: {
|
|
288
|
-
default: null,
|
|
289
|
-
parseHTML: (element) => {
|
|
290
|
-
const img = element.querySelector("img");
|
|
291
|
-
return img?.getAttribute("alt");
|
|
292
|
-
},
|
|
293
|
-
},
|
|
294
|
-
title: {
|
|
295
|
-
default: null,
|
|
296
|
-
parseHTML: (element) => {
|
|
297
|
-
const img = element.querySelector("img");
|
|
298
|
-
return img?.getAttribute("title");
|
|
299
|
-
},
|
|
300
|
-
},
|
|
301
|
-
style: {
|
|
302
|
-
// This style is applied to the wrapper element
|
|
303
|
-
default: "width: 100%; height: auto; cursor: pointer;",
|
|
304
|
-
parseHTML: (element) => {
|
|
305
|
-
const width = element.getAttribute("width");
|
|
306
|
-
// const img = element.querySelector("img");
|
|
307
|
-
// const width = img?.getAttribute("width");
|
|
308
|
-
return width
|
|
309
|
-
? `width: ${width}px; height: auto; cursor: pointer;`
|
|
310
|
-
: `${element.style.cssText}`;
|
|
311
|
-
},
|
|
312
|
-
},
|
|
313
|
-
};
|
|
314
|
-
},
|
|
315
|
-
parseHTML() {
|
|
316
|
-
return [
|
|
317
|
-
{
|
|
318
|
-
tag: "figure",
|
|
319
|
-
contentElement: "figcaption",
|
|
320
|
-
},
|
|
321
|
-
{
|
|
322
|
-
tag: this.options.allowBase64
|
|
323
|
-
? "img[src]"
|
|
324
|
-
: 'img[src]:not([src^="data:"])',
|
|
325
|
-
},
|
|
326
|
-
];
|
|
327
|
-
},
|
|
328
|
-
renderHTML({ HTMLAttributes, node }) {
|
|
329
|
-
const hasCaption = node.content.size > 0;
|
|
330
|
-
if (hasCaption) {
|
|
331
|
-
return [
|
|
332
|
-
"figure",
|
|
333
|
-
this.options.HTMLAttributes,
|
|
334
|
-
[
|
|
335
|
-
"img",
|
|
336
|
-
core.mergeAttributes(HTMLAttributes, {
|
|
337
|
-
draggable: false,
|
|
338
|
-
contenteditable: false,
|
|
339
|
-
}),
|
|
340
|
-
],
|
|
341
|
-
["figcaption", 0],
|
|
342
|
-
];
|
|
343
|
-
}
|
|
344
|
-
return ["img", core.mergeAttributes(HTMLAttributes)];
|
|
345
|
-
},
|
|
346
|
-
addNodeView() {
|
|
347
|
-
return ({ node, editor, getPos }) => {
|
|
348
|
-
const dispatchNodeView = () => {
|
|
349
|
-
if (typeof getPos === "function") {
|
|
350
|
-
const newAttrs = {
|
|
351
|
-
...node.attrs,
|
|
352
|
-
style: imageElement.style.cssText,
|
|
353
|
-
};
|
|
354
|
-
editor.view.dispatch(editor.view.state.tr.setNodeMarkup(getPos(), null, newAttrs));
|
|
355
|
-
this.storage.elementsVisible = false;
|
|
356
|
-
}
|
|
357
|
-
};
|
|
358
|
-
const { options: { editable }, } = editor;
|
|
359
|
-
const { style } = node.attrs;
|
|
360
|
-
// Create wrapper based on content
|
|
361
|
-
const wrapperElement = document.createElement(node.content.size > 0 ? "figure" : "div");
|
|
362
|
-
wrapperElement.setAttribute("class", styles["wrapper-element"]);
|
|
363
|
-
wrapperElement.setAttribute("style", style);
|
|
364
|
-
const imageElement = document.createElement("img");
|
|
365
|
-
wrapperElement.appendChild(imageElement);
|
|
366
|
-
const captionElement = document.createElement("figcaption");
|
|
367
|
-
// Set up image attributes
|
|
368
|
-
Object.entries(node.attrs).forEach(([key, value]) => {
|
|
369
|
-
if (value === undefined || value === null) {
|
|
370
|
-
return;
|
|
371
|
-
}
|
|
372
|
-
imageElement.setAttribute(key, value);
|
|
373
|
-
});
|
|
374
|
-
// Add caption if needed
|
|
375
|
-
if (node.content.size > 0) {
|
|
376
|
-
captionElement.setAttribute("class", styles["caption-element"]);
|
|
377
|
-
captionElement.setAttribute("contenteditable", "true");
|
|
378
|
-
wrapperElement.appendChild(captionElement);
|
|
379
|
-
}
|
|
380
|
-
if (!editable)
|
|
381
|
-
return { dom: wrapperElement, contentDOM: captionElement };
|
|
382
|
-
// Initialize control variables
|
|
383
|
-
let isResizing = false;
|
|
384
|
-
let startX = 0;
|
|
385
|
-
let startWidth = 0;
|
|
386
|
-
// Handle click on container
|
|
387
|
-
wrapperElement.addEventListener("click", (event) => {
|
|
388
|
-
event.stopPropagation();
|
|
389
|
-
event.preventDefault();
|
|
390
|
-
// If controls are already visible, check if another image or figure is being clicked on
|
|
391
|
-
if (this.storage.elementsVisible) {
|
|
392
|
-
const clickedElement = event.target;
|
|
393
|
-
const currentActiveWrapper = this.storage.currentActiveWrapper;
|
|
394
|
-
// Check if the clicked element is a child of the current active wrapper
|
|
395
|
-
// If it isn't, we must remove the controls from the previous wrapper and continue
|
|
396
|
-
// If it is, we do nothing
|
|
397
|
-
if (currentActiveWrapper &&
|
|
398
|
-
currentActiveWrapper.contains(clickedElement)) {
|
|
399
|
-
return;
|
|
400
|
-
}
|
|
401
|
-
if (currentActiveWrapper) {
|
|
402
|
-
removeImageControlsAndResetStyles(clickedElement, currentActiveWrapper, styles);
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
this.storage.currentActiveWrapper = wrapperElement;
|
|
406
|
-
const isMobile = isMobileScreen();
|
|
407
|
-
if (isMobile) {
|
|
408
|
-
const focusedElement = document.querySelector(".ProseMirror-focused");
|
|
409
|
-
focusedElement?.blur();
|
|
410
|
-
}
|
|
411
|
-
// Remove existing controls first
|
|
412
|
-
removeImageControlsAndResetStyles(event.target, wrapperElement, styles);
|
|
413
|
-
// Show new controls
|
|
414
|
-
wrapperElement.classList.toggle(styles["active"]);
|
|
415
|
-
addImageAlignmentControls(wrapperElement, imageElement, styles, () => {
|
|
416
|
-
dispatchNodeView();
|
|
417
|
-
editor.commands.focus();
|
|
418
|
-
});
|
|
419
|
-
addImageResizeControls(wrapperElement, imageElement, isResizing, startX, startWidth, styles, () => {
|
|
420
|
-
dispatchNodeView();
|
|
421
|
-
editor.commands.focus();
|
|
422
|
-
});
|
|
423
|
-
addCaptionControls(wrapperElement, styles, () => {
|
|
424
|
-
// On caption remove
|
|
425
|
-
changeFigureToImage(this.editor, wrapperElement);
|
|
426
|
-
this.storage.elementsVisible = false;
|
|
427
|
-
}, () => {
|
|
428
|
-
// On caption add
|
|
429
|
-
changeImageToFigure(this.editor, wrapperElement, captionElement);
|
|
430
|
-
this.storage.elementsVisible = false;
|
|
431
|
-
});
|
|
432
|
-
this.storage.elementsVisible = true;
|
|
433
|
-
});
|
|
434
|
-
// Handle clicks outside
|
|
435
|
-
document.addEventListener("click", (event) => {
|
|
436
|
-
if (!wrapperElement.contains(event.target)) {
|
|
437
|
-
removeImageControlsAndResetStyles(event.target, wrapperElement, styles);
|
|
438
|
-
this.storage.elementsVisible = false;
|
|
439
|
-
this.storage.currentActiveWrapper = null;
|
|
440
|
-
}
|
|
441
|
-
});
|
|
442
|
-
return {
|
|
443
|
-
dom: wrapperElement,
|
|
444
|
-
contentDOM: node.content.size > 0 ? captionElement : undefined,
|
|
445
|
-
ignoreMutation: (mutation) => {
|
|
446
|
-
// We must ignore mutations that happened outside the captionElement
|
|
447
|
-
return !captionElement.contains(mutation.target);
|
|
448
|
-
},
|
|
449
|
-
};
|
|
450
|
-
};
|
|
451
|
-
},
|
|
452
|
-
addInputRules() {
|
|
453
|
-
return [
|
|
454
|
-
core.nodeInputRule({
|
|
455
|
-
find: inputRegex,
|
|
456
|
-
type: this.type,
|
|
457
|
-
getAttributes: (match) => {
|
|
458
|
-
const [, alt, src, title] = match;
|
|
459
|
-
return { src, alt, title };
|
|
460
|
-
},
|
|
461
|
-
}),
|
|
462
|
-
];
|
|
463
|
-
},
|
|
464
|
-
});
|
|
465
|
-
|
|
466
|
-
exports.default = TiptapImageFigureExtension;
|
|
467
|
-
//# sourceMappingURL=index.cjs.js.map
|
package/dist/index.cjs.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../src/assets/icons/format-align-left.svg","../src/assets/icons/format-align-center.svg","../src/assets/icons/format-align-right.svg","../src/assets/icons/closed-caption-add.svg","../src/assets/icons/delete.svg","../node_modules/style-inject/dist/style-inject.es.js"],"sourcesContent":["export default \"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2220px%22%20viewBox%3D%220%20-960%20960%20960%22%20width%3D%2220px%22%20fill%3D%22%23000000%22%3E%3Cpath%20d%3D%22M144-144v-72h672v72H144Zm0-150v-72h480v72H144Zm0-150v-72h672v72H144Zm0-150v-72h480v72H144Zm0-150v-72h672v72H144Z%22%2F%3E%3C%2Fsvg%3E\"","export default \"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2220px%22%20viewBox%3D%220%20-960%20960%20960%22%20width%3D%2220px%22%20fill%3D%22%23000000%22%3E%3Cpath%20d%3D%22M144-144v-72h672v72H144Zm144-150v-72h384v72H288ZM144-444v-72h672v72H144Zm144-150v-72h384v72H288ZM144-744v-72h672v72H144Z%22%2F%3E%3C%2Fsvg%3E\"","export default \"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2220px%22%20viewBox%3D%220%20-960%20960%20960%22%20width%3D%2220px%22%20fill%3D%22%23000000%22%3E%3Cpath%20d%3D%22M144-744v-72h672v72H144Zm192%20150v-72h480v72H336ZM144-444v-72h672v72H144Zm192%20150v-72h480v72H336ZM144-144v-72h672v72H144Z%22%2F%3E%3C%2Fsvg%3E\"","export default \"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2220px%22%20viewBox%3D%220%20-960%20960%20960%22%20width%3D%2220px%22%20fill%3D%22%23000000%22%3E%3Cpath%20d%3D%22M480-480Zm120%20288H216q-29.7%200-50.85-21.16Q144-234.32%20144-264.04v-432.24Q144-726%20165.15-747T216-768h528q29.7%200%2050.85%2021.15Q816-725.7%20816-696v288h-72v-288H216v432h384v72Zm144%2072v-72h-72v-72h72v-72h72v72h72v72h-72v72h-72ZM293.29-368h111.86Q421-368%20432-378.78q11-10.78%2011-26.72V-443h-56.14v19H312v-112h75v19h56v-37.89q0-16.11-10.64-26.61Q421.73-592%20406-592H293.01q-16.01%200-26.51%2010.71-10.5%2010.7-10.5%2026.52v148.95Q256-390%20266.72-379t26.57%2011Zm261.22%200h112.55q15.94%200%2026.44-10.78Q704-389.56%20704-405.5V-443h-56.14v19H573v-112h75v19h56v-37.89q0-16.11-10.72-26.61T666.71-592H554.85Q539-592%20528-581.29q-11%2010.7-11%2026.52v148.95Q517-390%20527.79-379q10.78%2011%2026.72%2011Z%22%2F%3E%3C%2Fsvg%3E\"","export default \"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2220px%22%20viewBox%3D%220%20-960%20960%20960%22%20width%3D%2220px%22%20fill%3D%22%23000000%22%3E%3Cpath%20d%3D%22M312-144q-29.7%200-50.85-21.15Q240-186.3%20240-216v-480h-48v-72h192v-48h192v48h192v72h-48v479.57Q720-186%20698.85-165T648-144H312Zm336-552H312v480h336v-480ZM384-288h72v-336h-72v336Zm120%200h72v-336h-72v336ZM312-696v480-480Z%22%2F%3E%3C%2Fsvg%3E\"","function styleInject(css, ref) {\n if ( ref === void 0 ) ref = {};\n var insertAt = ref.insertAt;\n\n if (!css || typeof document === 'undefined') { return; }\n\n var head = document.head || document.getElementsByTagName('head')[0];\n var style = document.createElement('style');\n style.type = 'text/css';\n\n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild);\n } else {\n head.appendChild(style);\n }\n } else {\n head.appendChild(style);\n }\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n style.appendChild(document.createTextNode(css));\n }\n}\n\nexport default styleInject;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAAA,eAAe;;ACAf,iBAAe;;ACAf,gBAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACAf,2BAAe;;ACAf,iBAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACAf,SAAS,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE;AAC/B,EAAE,KAAK,GAAG,KAAK,MAAM,GAAG,GAAG,GAAG,EAAE;AAChC,EAAE,IAAI,QAAQ,GAAG,GAAG,CAAC,QAAQ;;AAE7B,EAAE,IAAY,OAAO,QAAQ,KAAK,WAAW,EAAE,EAAE,OAAO;;AAExD,EAAE,IAAI,IAAI,GAAG,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AACtE,EAAE,IAAI,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC;AAC7C,EAAE,KAAK,CAAC,IAAI,GAAG,UAAU;;AAEzB,EAAE,IAAI,QAAQ,KAAK,KAAK,EAAE;AAC1B,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE;AACzB,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC;AAC/C,KAAK,MAAM;AACX,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;AAC7B;AACA,GAAG,MAAM;AACT,IAAI,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;AAC3B;;AAEA,EAAE,IAAI,KAAK,CAAC,UAAU,EAAE;AACxB,IAAI,KAAK,CAAC,UAAU,CAAC,OAAO,GAAG,GAAG;AAClC,GAAG,MAAM;AACT,IAAI,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;AACnD;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","x_google_ignoreList":[5]}
|
package/dist/index.d.ts
DELETED
package/dist/index.js
DELETED
|
@@ -1,463 +0,0 @@
|
|
|
1
|
-
import { nodeInputRule, mergeAttributes } from '@tiptap/core';
|
|
2
|
-
import ImageExtension from '@tiptap/extension-image';
|
|
3
|
-
import { TextSelection } from '@tiptap/pm/state';
|
|
4
|
-
|
|
5
|
-
const isMobileScreen = () => document.documentElement.clientWidth < 768;
|
|
6
|
-
|
|
7
|
-
const removeImageControlsAndResetStyles = (clickedElement, wrapperElement, styles) => {
|
|
8
|
-
const containerContainsClickedElement = wrapperElement.contains(clickedElement);
|
|
9
|
-
if (containerContainsClickedElement) {
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
// Remove all custom UI elements and styling
|
|
13
|
-
wrapperElement.classList.remove(styles["active"]);
|
|
14
|
-
const children = Array.from(wrapperElement.children);
|
|
15
|
-
children.forEach((child) => {
|
|
16
|
-
if (child.tagName !== "IMG" && child.tagName !== "FIGCAPTION") {
|
|
17
|
-
wrapperElement.removeChild(child);
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
var leftIcon = "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2220px%22%20viewBox%3D%220%20-960%20960%20960%22%20width%3D%2220px%22%20fill%3D%22%23000000%22%3E%3Cpath%20d%3D%22M144-144v-72h672v72H144Zm0-150v-72h480v72H144Zm0-150v-72h672v72H144Zm0-150v-72h480v72H144Zm0-150v-72h672v72H144Z%22%2F%3E%3C%2Fsvg%3E";
|
|
23
|
-
|
|
24
|
-
var centerIcon = "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2220px%22%20viewBox%3D%220%20-960%20960%20960%22%20width%3D%2220px%22%20fill%3D%22%23000000%22%3E%3Cpath%20d%3D%22M144-144v-72h672v72H144Zm144-150v-72h384v72H288ZM144-444v-72h672v72H144Zm144-150v-72h384v72H288ZM144-744v-72h672v72H144Z%22%2F%3E%3C%2Fsvg%3E";
|
|
25
|
-
|
|
26
|
-
var rightIcon = "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2220px%22%20viewBox%3D%220%20-960%20960%20960%22%20width%3D%2220px%22%20fill%3D%22%23000000%22%3E%3Cpath%20d%3D%22M144-744v-72h672v72H144Zm192%20150v-72h480v72H336ZM144-444v-72h672v72H144Zm192%20150v-72h480v72H336ZM144-144v-72h672v72H144Z%22%2F%3E%3C%2Fsvg%3E";
|
|
27
|
-
|
|
28
|
-
const imageAlignmentControls = [
|
|
29
|
-
{
|
|
30
|
-
type: "left",
|
|
31
|
-
icon: leftIcon,
|
|
32
|
-
styleToApply: "margin: 0 auto 0 0;",
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
type: "center",
|
|
36
|
-
icon: centerIcon,
|
|
37
|
-
styleToApply: "margin: 0 auto;",
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
type: "right",
|
|
41
|
-
icon: rightIcon,
|
|
42
|
-
styleToApply: "margin: 0 0 0 auto;",
|
|
43
|
-
},
|
|
44
|
-
];
|
|
45
|
-
const addImageAlignmentControls = (wrapperElement, imageElement, styles, onAlign) => {
|
|
46
|
-
const imageAlignmentContainer = document.createElement("div");
|
|
47
|
-
imageAlignmentContainer.setAttribute("contenteditable", "false");
|
|
48
|
-
imageAlignmentContainer.setAttribute("class", styles["image-alignment-container"]);
|
|
49
|
-
imageAlignmentControls.forEach((imageControl) => {
|
|
50
|
-
const imageAlignmentControl = document.createElement("img");
|
|
51
|
-
imageAlignmentControl.src = imageControl.icon;
|
|
52
|
-
imageAlignmentControl.setAttribute("class", styles["image-alignment-control"]);
|
|
53
|
-
imageAlignmentControl.addEventListener("click", (event) => {
|
|
54
|
-
event.stopPropagation();
|
|
55
|
-
imageElement.style.cssText = `${imageElement.style.cssText} ${imageControl.styleToApply}`;
|
|
56
|
-
onAlign();
|
|
57
|
-
});
|
|
58
|
-
imageAlignmentContainer.appendChild(imageAlignmentControl);
|
|
59
|
-
});
|
|
60
|
-
wrapperElement.appendChild(imageAlignmentContainer);
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const addImageResizeControls = (wrapperElement, imageElement, isResizing, startX, startWidth, styles, onResize) => {
|
|
64
|
-
const isMobile = isMobileScreen();
|
|
65
|
-
const dotPosition = isMobile ? "-8px" : "-4px";
|
|
66
|
-
const dotSize = isMobile ? 16 : 9;
|
|
67
|
-
const dotsPosition = [
|
|
68
|
-
`top: ${dotPosition}; left: ${dotPosition}; cursor: nwse-resize;`,
|
|
69
|
-
`top: ${dotPosition}; right: ${dotPosition}; cursor: nesw-resize;`,
|
|
70
|
-
`bottom: ${dotPosition}; left: ${dotPosition}; cursor: nesw-resize;`,
|
|
71
|
-
`bottom: ${dotPosition}; right: ${dotPosition}; cursor: nwse-resize;`,
|
|
72
|
-
];
|
|
73
|
-
Array.from({ length: 4 }, (_, index) => {
|
|
74
|
-
const dotElement = document.createElement("div");
|
|
75
|
-
dotElement.setAttribute("class", styles["dot-element"]);
|
|
76
|
-
dotElement.setAttribute("style", `width: ${dotSize}px; height: ${dotSize}px; ${dotsPosition[index]}`);
|
|
77
|
-
dotElement.addEventListener("mousedown", (e) => {
|
|
78
|
-
e.preventDefault();
|
|
79
|
-
isResizing = true;
|
|
80
|
-
startX = e.clientX;
|
|
81
|
-
startWidth = wrapperElement.offsetWidth;
|
|
82
|
-
const onMouseMove = (event) => {
|
|
83
|
-
if (!isResizing) {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
const deltaX = index % 2 === 0 ? -(event.clientX - startX) : event.clientX - startX;
|
|
87
|
-
const newWidth = startWidth + deltaX;
|
|
88
|
-
wrapperElement.style.width = newWidth + "px";
|
|
89
|
-
imageElement.style.width = newWidth + "px";
|
|
90
|
-
};
|
|
91
|
-
const onMouseUp = () => {
|
|
92
|
-
if (isResizing) {
|
|
93
|
-
isResizing = false;
|
|
94
|
-
}
|
|
95
|
-
onResize();
|
|
96
|
-
document.removeEventListener("mousemove", onMouseMove);
|
|
97
|
-
document.removeEventListener("mouseup", onMouseUp);
|
|
98
|
-
};
|
|
99
|
-
document.addEventListener("mousemove", onMouseMove);
|
|
100
|
-
document.addEventListener("mouseup", onMouseUp);
|
|
101
|
-
});
|
|
102
|
-
dotElement.addEventListener("touchstart", (e) => {
|
|
103
|
-
e.cancelable && e.preventDefault();
|
|
104
|
-
isResizing = true;
|
|
105
|
-
startX = e.touches[0].clientX;
|
|
106
|
-
startWidth = wrapperElement.offsetWidth;
|
|
107
|
-
const onTouchMove = (e) => {
|
|
108
|
-
if (!isResizing)
|
|
109
|
-
return;
|
|
110
|
-
const deltaX = index % 2 === 0
|
|
111
|
-
? -(e.touches[0].clientX - startX)
|
|
112
|
-
: e.touches[0].clientX - startX;
|
|
113
|
-
const newWidth = startWidth + deltaX;
|
|
114
|
-
wrapperElement.style.width = newWidth + "px";
|
|
115
|
-
imageElement.style.width = newWidth + "px";
|
|
116
|
-
};
|
|
117
|
-
const onTouchEnd = () => {
|
|
118
|
-
if (isResizing) {
|
|
119
|
-
isResizing = false;
|
|
120
|
-
}
|
|
121
|
-
onResize();
|
|
122
|
-
document.removeEventListener("touchmove", onTouchMove);
|
|
123
|
-
document.removeEventListener("touchend", onTouchEnd);
|
|
124
|
-
};
|
|
125
|
-
document.addEventListener("touchmove", onTouchMove);
|
|
126
|
-
document.addEventListener("touchend", onTouchEnd);
|
|
127
|
-
}, { passive: false });
|
|
128
|
-
wrapperElement.appendChild(dotElement);
|
|
129
|
-
});
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
var closedCaptionAddIcon = "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2220px%22%20viewBox%3D%220%20-960%20960%20960%22%20width%3D%2220px%22%20fill%3D%22%23000000%22%3E%3Cpath%20d%3D%22M480-480Zm120%20288H216q-29.7%200-50.85-21.16Q144-234.32%20144-264.04v-432.24Q144-726%20165.15-747T216-768h528q29.7%200%2050.85%2021.15Q816-725.7%20816-696v288h-72v-288H216v432h384v72Zm144%2072v-72h-72v-72h72v-72h72v72h72v72h-72v72h-72ZM293.29-368h111.86Q421-368%20432-378.78q11-10.78%2011-26.72V-443h-56.14v19H312v-112h75v19h56v-37.89q0-16.11-10.64-26.61Q421.73-592%20406-592H293.01q-16.01%200-26.51%2010.71-10.5%2010.7-10.5%2026.52v148.95Q256-390%20266.72-379t26.57%2011Zm261.22%200h112.55q15.94%200%2026.44-10.78Q704-389.56%20704-405.5V-443h-56.14v19H573v-112h75v19h56v-37.89q0-16.11-10.72-26.61T666.71-592H554.85Q539-592%20528-581.29q-11%2010.7-11%2026.52v148.95Q517-390%20527.79-379q10.78%2011%2026.72%2011Z%22%2F%3E%3C%2Fsvg%3E";
|
|
133
|
-
|
|
134
|
-
var deleteIcon = "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2220px%22%20viewBox%3D%220%20-960%20960%20960%22%20width%3D%2220px%22%20fill%3D%22%23000000%22%3E%3Cpath%20d%3D%22M312-144q-29.7%200-50.85-21.15Q240-186.3%20240-216v-480h-48v-72h192v-48h192v48h192v72h-48v479.57Q720-186%20698.85-165T648-144H312Zm336-552H312v480h336v-480ZM384-288h72v-336h-72v336Zm120%200h72v-336h-72v336ZM312-696v480-480Z%22%2F%3E%3C%2Fsvg%3E";
|
|
135
|
-
|
|
136
|
-
const addCaptionControls = (wrapperElement, styles, onCaptionRemove, onCaptionAdd) => {
|
|
137
|
-
const captionControlsContainer = document.createElement("div");
|
|
138
|
-
captionControlsContainer.setAttribute("contenteditable", "false");
|
|
139
|
-
captionControlsContainer.setAttribute("class", styles["caption-controls-element"]);
|
|
140
|
-
// If wrapper element is a figure and the button doesn't already exist, add a button to remove caption
|
|
141
|
-
// Also, wrapper elements needs to become a div
|
|
142
|
-
if (wrapperElement.tagName === "FIGURE" &&
|
|
143
|
-
!wrapperElement.querySelector(styles["remove-caption-button"])) {
|
|
144
|
-
const removeCaptionButton = document.createElement("img");
|
|
145
|
-
removeCaptionButton.src = deleteIcon;
|
|
146
|
-
removeCaptionButton.setAttribute("class", styles["remove-caption-button"]);
|
|
147
|
-
removeCaptionButton.addEventListener("click", (event) => {
|
|
148
|
-
event.stopPropagation();
|
|
149
|
-
onCaptionRemove();
|
|
150
|
-
});
|
|
151
|
-
captionControlsContainer.appendChild(removeCaptionButton);
|
|
152
|
-
wrapperElement.appendChild(captionControlsContainer);
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
// If wrapper element is a div and the button doesn't already exist, add a button to add caption
|
|
156
|
-
if (wrapperElement.tagName === "DIV" &&
|
|
157
|
-
!wrapperElement.querySelector(styles["add-caption-button"])) {
|
|
158
|
-
const addCaptionButton = document.createElement("img");
|
|
159
|
-
addCaptionButton.src = closedCaptionAddIcon;
|
|
160
|
-
addCaptionButton.setAttribute("class", styles["add-caption-button"]);
|
|
161
|
-
addCaptionButton.addEventListener("click", (event) => {
|
|
162
|
-
event.stopPropagation();
|
|
163
|
-
onCaptionAdd();
|
|
164
|
-
});
|
|
165
|
-
captionControlsContainer.appendChild(addCaptionButton);
|
|
166
|
-
wrapperElement.appendChild(captionControlsContainer);
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
const changeFigureToImage = (editor, wrapperElement) => {
|
|
171
|
-
const { state, view } = editor;
|
|
172
|
-
const { tr } = state;
|
|
173
|
-
const imageWrapperElement = document.createElement("div");
|
|
174
|
-
const oldAttributes = wrapperElement.attributes;
|
|
175
|
-
const newAttributes = imageWrapperElement.attributes;
|
|
176
|
-
const imageWrapperPosition = view.posAtDOM(wrapperElement, 0);
|
|
177
|
-
// Copy attributes
|
|
178
|
-
for (let i = 0, len = oldAttributes.length; i < len; i++) {
|
|
179
|
-
newAttributes.setNamedItem(oldAttributes.item(i).cloneNode());
|
|
180
|
-
}
|
|
181
|
-
// Find the image within the old wrapper and set it as the only child of the new wrapper
|
|
182
|
-
const imageElement = wrapperElement.querySelector("img");
|
|
183
|
-
if (imageElement) {
|
|
184
|
-
imageWrapperElement.appendChild(imageElement);
|
|
185
|
-
}
|
|
186
|
-
// Replace wrapperElement with imageWrapperElement
|
|
187
|
-
wrapperElement.replaceWith(imageWrapperElement);
|
|
188
|
-
// Focus on the newly replaced wrapper element
|
|
189
|
-
const newSelection = TextSelection.create(tr.doc, imageWrapperPosition);
|
|
190
|
-
const transaction = tr.setSelection(newSelection);
|
|
191
|
-
view.dispatch(transaction);
|
|
192
|
-
};
|
|
193
|
-
const changeImageToFigure = (editor, wrapperElement, captionElement) => {
|
|
194
|
-
const { state, view } = editor;
|
|
195
|
-
const { tr } = state;
|
|
196
|
-
const figureWrapperElement = document.createElement("figure");
|
|
197
|
-
const oldAttributes = wrapperElement.attributes;
|
|
198
|
-
const newAttributes = figureWrapperElement.attributes;
|
|
199
|
-
const figureWrapperPosition = view.posAtDOM(wrapperElement, 0);
|
|
200
|
-
// Copy attributes
|
|
201
|
-
for (let i = 0, len = oldAttributes.length; i < len; i++) {
|
|
202
|
-
newAttributes.setNamedItem(oldAttributes.item(i).cloneNode());
|
|
203
|
-
}
|
|
204
|
-
// Find the image within the old wrapper and set it as the only child of the new wrapper
|
|
205
|
-
const imageElement = wrapperElement.querySelector("img");
|
|
206
|
-
if (imageElement) {
|
|
207
|
-
figureWrapperElement.appendChild(imageElement);
|
|
208
|
-
}
|
|
209
|
-
captionElement.innerHTML = "Caption";
|
|
210
|
-
figureWrapperElement.appendChild(captionElement);
|
|
211
|
-
// Replace wrapperElement with figureWrapperElement
|
|
212
|
-
wrapperElement.replaceWith(figureWrapperElement);
|
|
213
|
-
// Focus on the newly replaced wrapper element
|
|
214
|
-
const newSelection = TextSelection.create(tr.doc, figureWrapperPosition);
|
|
215
|
-
const transaction = tr.setSelection(newSelection);
|
|
216
|
-
view.dispatch(transaction);
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
function styleInject(css, ref) {
|
|
220
|
-
if ( ref === void 0 ) ref = {};
|
|
221
|
-
var insertAt = ref.insertAt;
|
|
222
|
-
|
|
223
|
-
if (typeof document === 'undefined') { return; }
|
|
224
|
-
|
|
225
|
-
var head = document.head || document.getElementsByTagName('head')[0];
|
|
226
|
-
var style = document.createElement('style');
|
|
227
|
-
style.type = 'text/css';
|
|
228
|
-
|
|
229
|
-
if (insertAt === 'top') {
|
|
230
|
-
if (head.firstChild) {
|
|
231
|
-
head.insertBefore(style, head.firstChild);
|
|
232
|
-
} else {
|
|
233
|
-
head.appendChild(style);
|
|
234
|
-
}
|
|
235
|
-
} else {
|
|
236
|
-
head.appendChild(style);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
if (style.styleSheet) {
|
|
240
|
-
style.styleSheet.cssText = css;
|
|
241
|
-
} else {
|
|
242
|
-
style.appendChild(document.createTextNode(css));
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
var css_248z = ".styles_wrapper-element__SoyDK {\n display: flex;\n flex-direction: column;\n position: relative;\n cursor: pointer;\n width: fit-content;\n margin-inline: 0;\n}\n.styles_wrapper-element__SoyDK.styles_active__kXAaT {\n border: 2px dashed #6c6c6c;\n}\n\n.styles_figure-element__wBqOu {\n}\n\n.styles_caption-element__-9Bt- {\n text-align: center;\n margin-top: 8px;\n min-height: 1em;\n margin: 0.5rem 2rem;\n padding: 0.5rem 0;\n}\n.styles_caption-element__-9Bt-:hover {\n border-radius: 4px;\n border: 2px dashed #6c6c6c;\n}\n\n.styles_caption-controls-element__Pwjxq {\n position: absolute;\n bottom: 7.5%;\n left: 50%;\n width: 40px;\n height: 35px;\n background-color: rgba(255, 255, 255, 0.7);\n border-radius: 4px;\n border: 2px solid #6c6c6c;\n cursor: pointer;\n transform: translate(-50%, -50%);\n display: flex;\n justify-content: center;\n align-items: center;\n}\n\n.styles_remove-caption-button__OzMEn,\n.styles_add-caption-button__2rKuu {\n cursor: pointer;\n font-size: 20px;\n}\n\n.styles_remove-caption-button__OzMEn:hover,\n.styles_add-caption-button__2rKuu:hover {\n opacity: 0.5;\n}\n\n.styles_dot-element__TQRBe {\n position: absolute;\n border: 1.5px solid #6c6c6c;\n border-radius: 50%;\n}\n\n.styles_image-alignment-container__5byQ2 {\n position: absolute;\n top: 0%;\n left: 50%;\n width: 100px;\n height: 25px;\n background-color: rgba(255, 255, 255, 0.7);\n border-radius: 4px;\n border: 2px solid #6c6c6c;\n cursor: pointer;\n transform: translate(-50%, -50%);\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0 10px;\n}\n\n.styles_image-alignment-control__r3rTj {\n cursor: pointer;\n font-size: 20px;\n}\n.styles_image-alignment-control__r3rTj:hover {\n opacity: 0.5;\n}\n";
|
|
247
|
-
var styles = {"wrapper-element":"styles_wrapper-element__SoyDK","active":"styles_active__kXAaT","caption-element":"styles_caption-element__-9Bt-","caption-controls-element":"styles_caption-controls-element__Pwjxq","remove-caption-button":"styles_remove-caption-button__OzMEn","add-caption-button":"styles_add-caption-button__2rKuu","dot-element":"styles_dot-element__TQRBe","image-alignment-container":"styles_image-alignment-container__5byQ2","image-alignment-control":"styles_image-alignment-control__r3rTj"};
|
|
248
|
-
styleInject(css_248z);
|
|
249
|
-
|
|
250
|
-
const inputRegex = /!\[(.+|:?)]\((\S+)(?:(?:\s+)["'](\S+)["'])?\)/;
|
|
251
|
-
const TiptapImageFigureExtension = ImageExtension.extend({
|
|
252
|
-
addOptions() {
|
|
253
|
-
return {
|
|
254
|
-
...this.parent?.(),
|
|
255
|
-
inline: false,
|
|
256
|
-
allowBase64: false,
|
|
257
|
-
HTMLAttributes: {},
|
|
258
|
-
};
|
|
259
|
-
},
|
|
260
|
-
group: "block",
|
|
261
|
-
draggable: true,
|
|
262
|
-
isolating: true,
|
|
263
|
-
content: "inline*",
|
|
264
|
-
addStorage() {
|
|
265
|
-
return {
|
|
266
|
-
elementsVisible: false,
|
|
267
|
-
currentActiveWrapper: null,
|
|
268
|
-
};
|
|
269
|
-
},
|
|
270
|
-
addAttributes() {
|
|
271
|
-
return {
|
|
272
|
-
...this.parent?.(),
|
|
273
|
-
src: {
|
|
274
|
-
default: null,
|
|
275
|
-
parseHTML: (element) => {
|
|
276
|
-
if (element.tagName === "FIGURE") {
|
|
277
|
-
const img = element.querySelector("img");
|
|
278
|
-
return img?.getAttribute("src");
|
|
279
|
-
}
|
|
280
|
-
return element.getAttribute("src");
|
|
281
|
-
},
|
|
282
|
-
},
|
|
283
|
-
alt: {
|
|
284
|
-
default: null,
|
|
285
|
-
parseHTML: (element) => {
|
|
286
|
-
const img = element.querySelector("img");
|
|
287
|
-
return img?.getAttribute("alt");
|
|
288
|
-
},
|
|
289
|
-
},
|
|
290
|
-
title: {
|
|
291
|
-
default: null,
|
|
292
|
-
parseHTML: (element) => {
|
|
293
|
-
const img = element.querySelector("img");
|
|
294
|
-
return img?.getAttribute("title");
|
|
295
|
-
},
|
|
296
|
-
},
|
|
297
|
-
style: {
|
|
298
|
-
// This style is applied to the wrapper element
|
|
299
|
-
default: "width: 100%; height: auto; cursor: pointer;",
|
|
300
|
-
parseHTML: (element) => {
|
|
301
|
-
const width = element.getAttribute("width");
|
|
302
|
-
// const img = element.querySelector("img");
|
|
303
|
-
// const width = img?.getAttribute("width");
|
|
304
|
-
return width
|
|
305
|
-
? `width: ${width}px; height: auto; cursor: pointer;`
|
|
306
|
-
: `${element.style.cssText}`;
|
|
307
|
-
},
|
|
308
|
-
},
|
|
309
|
-
};
|
|
310
|
-
},
|
|
311
|
-
parseHTML() {
|
|
312
|
-
return [
|
|
313
|
-
{
|
|
314
|
-
tag: "figure",
|
|
315
|
-
contentElement: "figcaption",
|
|
316
|
-
},
|
|
317
|
-
{
|
|
318
|
-
tag: this.options.allowBase64
|
|
319
|
-
? "img[src]"
|
|
320
|
-
: 'img[src]:not([src^="data:"])',
|
|
321
|
-
},
|
|
322
|
-
];
|
|
323
|
-
},
|
|
324
|
-
renderHTML({ HTMLAttributes, node }) {
|
|
325
|
-
const hasCaption = node.content.size > 0;
|
|
326
|
-
if (hasCaption) {
|
|
327
|
-
return [
|
|
328
|
-
"figure",
|
|
329
|
-
this.options.HTMLAttributes,
|
|
330
|
-
[
|
|
331
|
-
"img",
|
|
332
|
-
mergeAttributes(HTMLAttributes, {
|
|
333
|
-
draggable: false,
|
|
334
|
-
contenteditable: false,
|
|
335
|
-
}),
|
|
336
|
-
],
|
|
337
|
-
["figcaption", 0],
|
|
338
|
-
];
|
|
339
|
-
}
|
|
340
|
-
return ["img", mergeAttributes(HTMLAttributes)];
|
|
341
|
-
},
|
|
342
|
-
addNodeView() {
|
|
343
|
-
return ({ node, editor, getPos }) => {
|
|
344
|
-
const dispatchNodeView = () => {
|
|
345
|
-
if (typeof getPos === "function") {
|
|
346
|
-
const newAttrs = {
|
|
347
|
-
...node.attrs,
|
|
348
|
-
style: imageElement.style.cssText,
|
|
349
|
-
};
|
|
350
|
-
editor.view.dispatch(editor.view.state.tr.setNodeMarkup(getPos(), null, newAttrs));
|
|
351
|
-
this.storage.elementsVisible = false;
|
|
352
|
-
}
|
|
353
|
-
};
|
|
354
|
-
const { options: { editable }, } = editor;
|
|
355
|
-
const { style } = node.attrs;
|
|
356
|
-
// Create wrapper based on content
|
|
357
|
-
const wrapperElement = document.createElement(node.content.size > 0 ? "figure" : "div");
|
|
358
|
-
wrapperElement.setAttribute("class", styles["wrapper-element"]);
|
|
359
|
-
wrapperElement.setAttribute("style", style);
|
|
360
|
-
const imageElement = document.createElement("img");
|
|
361
|
-
wrapperElement.appendChild(imageElement);
|
|
362
|
-
const captionElement = document.createElement("figcaption");
|
|
363
|
-
// Set up image attributes
|
|
364
|
-
Object.entries(node.attrs).forEach(([key, value]) => {
|
|
365
|
-
if (value === undefined || value === null) {
|
|
366
|
-
return;
|
|
367
|
-
}
|
|
368
|
-
imageElement.setAttribute(key, value);
|
|
369
|
-
});
|
|
370
|
-
// Add caption if needed
|
|
371
|
-
if (node.content.size > 0) {
|
|
372
|
-
captionElement.setAttribute("class", styles["caption-element"]);
|
|
373
|
-
captionElement.setAttribute("contenteditable", "true");
|
|
374
|
-
wrapperElement.appendChild(captionElement);
|
|
375
|
-
}
|
|
376
|
-
if (!editable)
|
|
377
|
-
return { dom: wrapperElement, contentDOM: captionElement };
|
|
378
|
-
// Initialize control variables
|
|
379
|
-
let isResizing = false;
|
|
380
|
-
let startX = 0;
|
|
381
|
-
let startWidth = 0;
|
|
382
|
-
// Handle click on container
|
|
383
|
-
wrapperElement.addEventListener("click", (event) => {
|
|
384
|
-
event.stopPropagation();
|
|
385
|
-
event.preventDefault();
|
|
386
|
-
// If controls are already visible, check if another image or figure is being clicked on
|
|
387
|
-
if (this.storage.elementsVisible) {
|
|
388
|
-
const clickedElement = event.target;
|
|
389
|
-
const currentActiveWrapper = this.storage.currentActiveWrapper;
|
|
390
|
-
// Check if the clicked element is a child of the current active wrapper
|
|
391
|
-
// If it isn't, we must remove the controls from the previous wrapper and continue
|
|
392
|
-
// If it is, we do nothing
|
|
393
|
-
if (currentActiveWrapper &&
|
|
394
|
-
currentActiveWrapper.contains(clickedElement)) {
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
397
|
-
if (currentActiveWrapper) {
|
|
398
|
-
removeImageControlsAndResetStyles(clickedElement, currentActiveWrapper, styles);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
this.storage.currentActiveWrapper = wrapperElement;
|
|
402
|
-
const isMobile = isMobileScreen();
|
|
403
|
-
if (isMobile) {
|
|
404
|
-
const focusedElement = document.querySelector(".ProseMirror-focused");
|
|
405
|
-
focusedElement?.blur();
|
|
406
|
-
}
|
|
407
|
-
// Remove existing controls first
|
|
408
|
-
removeImageControlsAndResetStyles(event.target, wrapperElement, styles);
|
|
409
|
-
// Show new controls
|
|
410
|
-
wrapperElement.classList.toggle(styles["active"]);
|
|
411
|
-
addImageAlignmentControls(wrapperElement, imageElement, styles, () => {
|
|
412
|
-
dispatchNodeView();
|
|
413
|
-
editor.commands.focus();
|
|
414
|
-
});
|
|
415
|
-
addImageResizeControls(wrapperElement, imageElement, isResizing, startX, startWidth, styles, () => {
|
|
416
|
-
dispatchNodeView();
|
|
417
|
-
editor.commands.focus();
|
|
418
|
-
});
|
|
419
|
-
addCaptionControls(wrapperElement, styles, () => {
|
|
420
|
-
// On caption remove
|
|
421
|
-
changeFigureToImage(this.editor, wrapperElement);
|
|
422
|
-
this.storage.elementsVisible = false;
|
|
423
|
-
}, () => {
|
|
424
|
-
// On caption add
|
|
425
|
-
changeImageToFigure(this.editor, wrapperElement, captionElement);
|
|
426
|
-
this.storage.elementsVisible = false;
|
|
427
|
-
});
|
|
428
|
-
this.storage.elementsVisible = true;
|
|
429
|
-
});
|
|
430
|
-
// Handle clicks outside
|
|
431
|
-
document.addEventListener("click", (event) => {
|
|
432
|
-
if (!wrapperElement.contains(event.target)) {
|
|
433
|
-
removeImageControlsAndResetStyles(event.target, wrapperElement, styles);
|
|
434
|
-
this.storage.elementsVisible = false;
|
|
435
|
-
this.storage.currentActiveWrapper = null;
|
|
436
|
-
}
|
|
437
|
-
});
|
|
438
|
-
return {
|
|
439
|
-
dom: wrapperElement,
|
|
440
|
-
contentDOM: node.content.size > 0 ? captionElement : undefined,
|
|
441
|
-
ignoreMutation: (mutation) => {
|
|
442
|
-
// We must ignore mutations that happened outside the captionElement
|
|
443
|
-
return !captionElement.contains(mutation.target);
|
|
444
|
-
},
|
|
445
|
-
};
|
|
446
|
-
};
|
|
447
|
-
},
|
|
448
|
-
addInputRules() {
|
|
449
|
-
return [
|
|
450
|
-
nodeInputRule({
|
|
451
|
-
find: inputRegex,
|
|
452
|
-
type: this.type,
|
|
453
|
-
getAttributes: (match) => {
|
|
454
|
-
const [, alt, src, title] = match;
|
|
455
|
-
return { src, alt, title };
|
|
456
|
-
},
|
|
457
|
-
}),
|
|
458
|
-
];
|
|
459
|
-
},
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
export { TiptapImageFigureExtension as default };
|
|
463
|
-
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/assets/icons/format-align-left.svg","../src/assets/icons/format-align-center.svg","../src/assets/icons/format-align-right.svg","../src/assets/icons/closed-caption-add.svg","../src/assets/icons/delete.svg","../node_modules/style-inject/dist/style-inject.es.js"],"sourcesContent":["export default \"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2220px%22%20viewBox%3D%220%20-960%20960%20960%22%20width%3D%2220px%22%20fill%3D%22%23000000%22%3E%3Cpath%20d%3D%22M144-144v-72h672v72H144Zm0-150v-72h480v72H144Zm0-150v-72h672v72H144Zm0-150v-72h480v72H144Zm0-150v-72h672v72H144Z%22%2F%3E%3C%2Fsvg%3E\"","export default \"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2220px%22%20viewBox%3D%220%20-960%20960%20960%22%20width%3D%2220px%22%20fill%3D%22%23000000%22%3E%3Cpath%20d%3D%22M144-144v-72h672v72H144Zm144-150v-72h384v72H288ZM144-444v-72h672v72H144Zm144-150v-72h384v72H288ZM144-744v-72h672v72H144Z%22%2F%3E%3C%2Fsvg%3E\"","export default \"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2220px%22%20viewBox%3D%220%20-960%20960%20960%22%20width%3D%2220px%22%20fill%3D%22%23000000%22%3E%3Cpath%20d%3D%22M144-744v-72h672v72H144Zm192%20150v-72h480v72H336ZM144-444v-72h672v72H144Zm192%20150v-72h480v72H336ZM144-144v-72h672v72H144Z%22%2F%3E%3C%2Fsvg%3E\"","export default \"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2220px%22%20viewBox%3D%220%20-960%20960%20960%22%20width%3D%2220px%22%20fill%3D%22%23000000%22%3E%3Cpath%20d%3D%22M480-480Zm120%20288H216q-29.7%200-50.85-21.16Q144-234.32%20144-264.04v-432.24Q144-726%20165.15-747T216-768h528q29.7%200%2050.85%2021.15Q816-725.7%20816-696v288h-72v-288H216v432h384v72Zm144%2072v-72h-72v-72h72v-72h72v72h72v72h-72v72h-72ZM293.29-368h111.86Q421-368%20432-378.78q11-10.78%2011-26.72V-443h-56.14v19H312v-112h75v19h56v-37.89q0-16.11-10.64-26.61Q421.73-592%20406-592H293.01q-16.01%200-26.51%2010.71-10.5%2010.7-10.5%2026.52v148.95Q256-390%20266.72-379t26.57%2011Zm261.22%200h112.55q15.94%200%2026.44-10.78Q704-389.56%20704-405.5V-443h-56.14v19H573v-112h75v19h56v-37.89q0-16.11-10.72-26.61T666.71-592H554.85Q539-592%20528-581.29q-11%2010.7-11%2026.52v148.95Q517-390%20527.79-379q10.78%2011%2026.72%2011Z%22%2F%3E%3C%2Fsvg%3E\"","export default \"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2220px%22%20viewBox%3D%220%20-960%20960%20960%22%20width%3D%2220px%22%20fill%3D%22%23000000%22%3E%3Cpath%20d%3D%22M312-144q-29.7%200-50.85-21.15Q240-186.3%20240-216v-480h-48v-72h192v-48h192v48h192v72h-48v479.57Q720-186%20698.85-165T648-144H312Zm336-552H312v480h336v-480ZM384-288h72v-336h-72v336Zm120%200h72v-336h-72v336ZM312-696v480-480Z%22%2F%3E%3C%2Fsvg%3E\"","function styleInject(css, ref) {\n if ( ref === void 0 ) ref = {};\n var insertAt = ref.insertAt;\n\n if (!css || typeof document === 'undefined') { return; }\n\n var head = document.head || document.getElementsByTagName('head')[0];\n var style = document.createElement('style');\n style.type = 'text/css';\n\n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild);\n } else {\n head.appendChild(style);\n }\n } else {\n head.appendChild(style);\n }\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n style.appendChild(document.createTextNode(css));\n }\n}\n\nexport default styleInject;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA,eAAe;;ACAf,iBAAe;;ACAf,gBAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACAf,2BAAe;;ACAf,iBAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACAf,SAAS,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE;AAC/B,EAAE,KAAK,GAAG,KAAK,MAAM,GAAG,GAAG,GAAG,EAAE;AAChC,EAAE,IAAI,QAAQ,GAAG,GAAG,CAAC,QAAQ;;AAE7B,EAAE,IAAY,OAAO,QAAQ,KAAK,WAAW,EAAE,EAAE,OAAO;;AAExD,EAAE,IAAI,IAAI,GAAG,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AACtE,EAAE,IAAI,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC;AAC7C,EAAE,KAAK,CAAC,IAAI,GAAG,UAAU;;AAEzB,EAAE,IAAI,QAAQ,KAAK,KAAK,EAAE;AAC1B,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE;AACzB,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC;AAC/C,KAAK,MAAM;AACX,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;AAC7B;AACA,GAAG,MAAM;AACT,IAAI,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;AAC3B;;AAEA,EAAE,IAAI,KAAK,CAAC,UAAU,EAAE;AACxB,IAAI,KAAK,CAAC,UAAU,CAAC,OAAO,GAAG,GAAG;AAClC,GAAG,MAAM;AACT,IAAI,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;AACnD;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","x_google_ignoreList":[5]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const addCaptionControls: (wrapperElement: HTMLElement, styles: Record<string, string>, onCaptionRemove: () => void, onCaptionAdd: () => void) => void;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const addImageAlignmentControls: (wrapperElement: HTMLElement, imageElement: HTMLImageElement, styles: Record<string, string>, onAlign: () => void) => void;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const addImageResizeControls: (wrapperElement: HTMLElement, imageElement: HTMLImageElement, isResizing: boolean, startX: number, startWidth: number, styles: Record<string, string>, onResize: () => void) => void;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const isMobileScreen: () => boolean;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const removeImageControlsAndResetStyles: (clickedElement: HTMLElement, wrapperElement: HTMLElement, styles: Record<string, string>) => void;
|