@mantine-vue/tiptap 3.4.0 → 3.6.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/cjs/index.cjs +513 -22
- package/cjs/index.cjs.map +1 -1
- package/esm/index.mjs +508 -23
- package/esm/index.mjs.map +1 -1
- package/lib/RichTextEditor.context.d.ts +3 -0
- package/lib/RichTextEditor.d.ts +2 -0
- package/lib/RichTextEditor.types.d.ts +7 -0
- package/lib/RichTextEditor.vue.d.ts +3 -0
- package/lib/RichTextEditorControl/RichTextEditorImageUploadControl.d.ts +2 -0
- package/lib/RichTextEditorControl/RichTextEditorImageUploadControl.types.d.ts +11 -0
- package/lib/RichTextEditorControl/RichTextEditorImageUploadControl.vue.d.ts +4 -0
- package/lib/RichTextEditorControl/index.d.ts +1 -0
- package/lib/extensions/UploadImage.d.ts +21 -0
- package/lib/extensions/index.d.ts +1 -0
- package/lib/icons/Icons.d.ts +1 -0
- package/lib/labels.d.ts +1 -0
- package/package.json +3 -3
- package/styles.css +180 -135
- package/styles.layer.css +180 -135
package/cjs/index.cjs
CHANGED
|
@@ -25,6 +25,9 @@ let _mantine_vue_core = require("@mantine-vue/core");
|
|
|
25
25
|
let vue = require("vue");
|
|
26
26
|
let _tiptap_vue_3 = require("@tiptap/vue-3");
|
|
27
27
|
let _mantine_vue_hooks = require("@mantine-vue/hooks");
|
|
28
|
+
let _tiptap_pm_model = require("@tiptap/pm/model");
|
|
29
|
+
let _tiptap_pm_state = require("@tiptap/pm/state");
|
|
30
|
+
let _tiptap_pm_transform = require("@tiptap/pm/transform");
|
|
28
31
|
let _tiptap_extension_link = require("@tiptap/extension-link");
|
|
29
32
|
_tiptap_extension_link = __toESM(_tiptap_extension_link);
|
|
30
33
|
//#region src/labels.ts
|
|
@@ -79,6 +82,7 @@ var DEFAULT_LABELS = {
|
|
|
79
82
|
tableSplitCellControlLabel: "Split cell",
|
|
80
83
|
detailsControlLabel: "Toggle collapsible section",
|
|
81
84
|
invisibleCharactersControlLabel: "Toggle invisible characters",
|
|
85
|
+
imageUploadControlLabel: "Upload image",
|
|
82
86
|
linkEditorInputLabel: "Enter URL",
|
|
83
87
|
linkEditorInputPlaceholder: "https://example.com/",
|
|
84
88
|
linkEditorExternalLink: "Open link in a new tab",
|
|
@@ -95,23 +99,24 @@ var DEFAULT_LABELS = {
|
|
|
95
99
|
//#region src/RichTextEditor.context.ts
|
|
96
100
|
var [provideRichTextEditorContext, useRichTextEditorContext] = (0, _mantine_vue_core.createSafeContext)("RichTextEditor component was not found in tree");
|
|
97
101
|
var RichTextEditor_module_default = {
|
|
98
|
-
root: "
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
102
|
+
root: "_root_1jng1_1",
|
|
103
|
+
"mantine-upload-image-pulse": "_mantine-upload-image-pulse_1jng1_1",
|
|
104
|
+
Typography: "_Typography_1jng1_64",
|
|
105
|
+
content: "_content_1jng1_92",
|
|
106
|
+
linkEditorDropdown: "_linkEditorDropdown_1jng1_337",
|
|
107
|
+
tableInsertDropdown: "_tableInsertDropdown_1jng1_341",
|
|
108
|
+
tableInsertGrid: "_tableInsertGrid_1jng1_346",
|
|
109
|
+
tableInsertCell: "_tableInsertCell_1jng1_352",
|
|
110
|
+
tableInsertLabel: "_tableInsertLabel_1jng1_369",
|
|
111
|
+
control: "_control_1jng1_376",
|
|
112
|
+
controlIcon: "_controlIcon_1jng1_451",
|
|
113
|
+
controlsGroup: "_controlsGroup_1jng1_456",
|
|
114
|
+
linkEditor: "_linkEditor_1jng1_337",
|
|
115
|
+
linkEditorInput: "_linkEditorInput_1jng1_485",
|
|
116
|
+
linkEditorExternalControl: "_linkEditorExternalControl_1jng1_491",
|
|
117
|
+
linkEditorSave: "_linkEditorSave_1jng1_521",
|
|
118
|
+
toolbar: "_toolbar_1jng1_526",
|
|
119
|
+
taskList: "_taskList_1jng1_560"
|
|
115
120
|
};
|
|
116
121
|
//#endregion
|
|
117
122
|
//#region src/RichTextEditor.vue
|
|
@@ -135,6 +140,14 @@ var RichTextEditor_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
135
140
|
},
|
|
136
141
|
labels: { default: void 0 },
|
|
137
142
|
variant: { default: "default" },
|
|
143
|
+
onImageUpload: {
|
|
144
|
+
type: Function,
|
|
145
|
+
default: void 0
|
|
146
|
+
},
|
|
147
|
+
onImageUploadError: {
|
|
148
|
+
type: Function,
|
|
149
|
+
default: void 0
|
|
150
|
+
},
|
|
138
151
|
classNames: { default: void 0 },
|
|
139
152
|
styles: { default: void 0 },
|
|
140
153
|
vars: { default: void 0 },
|
|
@@ -151,8 +164,7 @@ var RichTextEditor_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
151
164
|
name: "RichTextEditor",
|
|
152
165
|
classes: RichTextEditor_module_default,
|
|
153
166
|
props,
|
|
154
|
-
|
|
155
|
-
style: attrs.style,
|
|
167
|
+
attrs,
|
|
156
168
|
classNames: props.classNames,
|
|
157
169
|
styles: props.styles,
|
|
158
170
|
vars: props.vars,
|
|
@@ -193,6 +205,12 @@ var RichTextEditor_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
193
205
|
},
|
|
194
206
|
get variant() {
|
|
195
207
|
return props.variant;
|
|
208
|
+
},
|
|
209
|
+
get onImageUpload() {
|
|
210
|
+
return props.onImageUpload;
|
|
211
|
+
},
|
|
212
|
+
get onImageUploadError() {
|
|
213
|
+
return props.onImageUploadError;
|
|
196
214
|
}
|
|
197
215
|
});
|
|
198
216
|
return (_ctx, _cache) => {
|
|
@@ -541,7 +559,7 @@ var RichTextEditorColorControl_default = /* @__PURE__ */ (0, vue.defineComponent
|
|
|
541
559
|
var RichTextEditorColorControl = (0, _mantine_vue_core.factory)(RichTextEditorColorControl_default, { classes: RichTextEditor_module_default });
|
|
542
560
|
//#endregion
|
|
543
561
|
//#region src/icons/RichTextEditorIcon.vue?vue&type=script&setup=true&lang.ts
|
|
544
|
-
var _hoisted_1 = ["d"];
|
|
562
|
+
var _hoisted_1$1 = ["d"];
|
|
545
563
|
//#endregion
|
|
546
564
|
//#region src/icons/RichTextEditorIcon.vue
|
|
547
565
|
var RichTextEditorIcon_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
@@ -568,7 +586,7 @@ var RichTextEditorIcon_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
568
586
|
return (0, vue.openBlock)(), (0, vue.createElementBlock)("path", {
|
|
569
587
|
key: path,
|
|
570
588
|
d: path
|
|
571
|
-
}, null, 8, _hoisted_1);
|
|
589
|
+
}, null, 8, _hoisted_1$1);
|
|
572
590
|
}), 128))], 16);
|
|
573
591
|
};
|
|
574
592
|
}
|
|
@@ -842,6 +860,12 @@ var IconPilcrow = createIcon("IconPilcrow", [
|
|
|
842
860
|
path("M17 4v16"),
|
|
843
861
|
path("M19 4h-9.5a4.5 4.5 0 0 0 0 9h3.5")
|
|
844
862
|
]);
|
|
863
|
+
var IconPhoto = createIcon("IconPhoto", [
|
|
864
|
+
path("M15 8h.01"),
|
|
865
|
+
path("M3 6a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v12a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3z"),
|
|
866
|
+
path("M3 16l5 -5c.928 -.893 2.072 -.893 3 0l5 5"),
|
|
867
|
+
path("M14 14l1 -1c.928 -.893 2.072 -.893 3 0l3 3")
|
|
868
|
+
]);
|
|
845
869
|
//#endregion
|
|
846
870
|
//#region src/RichTextEditorControl/RichTextEditorColorPickerControl.vue
|
|
847
871
|
var RichTextEditorColorPickerControl_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
@@ -1469,6 +1493,466 @@ var RichTextEditorInvisibleCharactersControl_default = /* @__PURE__ */ (0, vue.d
|
|
|
1469
1493
|
}
|
|
1470
1494
|
});
|
|
1471
1495
|
//#endregion
|
|
1496
|
+
//#region src/extensions/UploadImage.ts
|
|
1497
|
+
var FAILED_UPLOAD_REMOVAL_DELAY = 3e3;
|
|
1498
|
+
var uploadImagePluginKey = new _tiptap_pm_state.PluginKey("uploadImage");
|
|
1499
|
+
var settledUploads = /* @__PURE__ */ new WeakMap();
|
|
1500
|
+
var missingExtensionWarned = false;
|
|
1501
|
+
function warnMissingUploadExtension() {
|
|
1502
|
+
if (missingExtensionWarned) return;
|
|
1503
|
+
missingExtensionWarned = true;
|
|
1504
|
+
console.warn("[@mantine-vue/tiptap] Image upload requires the image extension created with getUploadImageExtension");
|
|
1505
|
+
}
|
|
1506
|
+
function getSettledUploads(view) {
|
|
1507
|
+
let settled = settledUploads.get(view);
|
|
1508
|
+
if (!settled) {
|
|
1509
|
+
settled = /* @__PURE__ */ new Map();
|
|
1510
|
+
settledUploads.set(view, settled);
|
|
1511
|
+
}
|
|
1512
|
+
return settled;
|
|
1513
|
+
}
|
|
1514
|
+
function findUploadNode(doc, uploadId) {
|
|
1515
|
+
let result = null;
|
|
1516
|
+
doc.descendants((node, pos) => {
|
|
1517
|
+
if (result) return false;
|
|
1518
|
+
if (node.type.name === "image" && node.attrs.uploadId === uploadId) {
|
|
1519
|
+
result = {
|
|
1520
|
+
node,
|
|
1521
|
+
pos
|
|
1522
|
+
};
|
|
1523
|
+
return false;
|
|
1524
|
+
}
|
|
1525
|
+
return true;
|
|
1526
|
+
});
|
|
1527
|
+
return result;
|
|
1528
|
+
}
|
|
1529
|
+
function revokeBlobUrl(upload) {
|
|
1530
|
+
if (upload.blobUrl) {
|
|
1531
|
+
URL.revokeObjectURL(upload.blobUrl);
|
|
1532
|
+
upload.blobUrl = null;
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
function settleUploadNode(tr, pos, node, upload) {
|
|
1536
|
+
if (upload.status === "success") return tr.setNodeMarkup(pos, void 0, {
|
|
1537
|
+
...node.attrs,
|
|
1538
|
+
...upload.attrs,
|
|
1539
|
+
uploading: false,
|
|
1540
|
+
uploadError: false,
|
|
1541
|
+
uploadId: null
|
|
1542
|
+
});
|
|
1543
|
+
return tr.setNodeMarkup(pos, void 0, {
|
|
1544
|
+
...node.attrs,
|
|
1545
|
+
uploading: false,
|
|
1546
|
+
uploadError: true
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
function scheduleFailedUploadRemoval(view, uploadId, upload) {
|
|
1550
|
+
upload.removalTimer = setTimeout(() => {
|
|
1551
|
+
upload.removalTimer = null;
|
|
1552
|
+
revokeBlobUrl(upload);
|
|
1553
|
+
if (view.isDestroyed) return;
|
|
1554
|
+
const found = findUploadNode(view.state.doc, uploadId);
|
|
1555
|
+
if (found) {
|
|
1556
|
+
getSettledUploads(view).delete(uploadId);
|
|
1557
|
+
view.dispatch(view.state.tr.delete(found.pos, found.pos + found.node.nodeSize).setMeta("addToHistory", false));
|
|
1558
|
+
}
|
|
1559
|
+
}, FAILED_UPLOAD_REMOVAL_DELAY);
|
|
1560
|
+
}
|
|
1561
|
+
function repairSettledUploads(view, transactions, newState) {
|
|
1562
|
+
if (!view || !transactions.some((tr) => tr.docChanged)) return null;
|
|
1563
|
+
const settled = settledUploads.get(view);
|
|
1564
|
+
if (!settled || settled.size === 0) return null;
|
|
1565
|
+
let tr = null;
|
|
1566
|
+
newState.doc.descendants((node, pos) => {
|
|
1567
|
+
if (node.type.name !== "image") return true;
|
|
1568
|
+
const upload = node.attrs.uploadId ? settled.get(node.attrs.uploadId) : void 0;
|
|
1569
|
+
if (!upload) return false;
|
|
1570
|
+
if (upload.status === "success") {
|
|
1571
|
+
tr = settleUploadNode(tr ?? newState.tr, pos, node, upload);
|
|
1572
|
+
settled.delete(node.attrs.uploadId);
|
|
1573
|
+
return false;
|
|
1574
|
+
}
|
|
1575
|
+
if (!node.attrs.uploadError) tr = settleUploadNode(tr ?? newState.tr, pos, node, upload);
|
|
1576
|
+
if (!upload.removalTimer) scheduleFailedUploadRemoval(view, node.attrs.uploadId, upload);
|
|
1577
|
+
return false;
|
|
1578
|
+
});
|
|
1579
|
+
return tr ? tr.setMeta("addToHistory", false) : null;
|
|
1580
|
+
}
|
|
1581
|
+
function createUploadId() {
|
|
1582
|
+
return Math.random().toString(36).slice(2, 10);
|
|
1583
|
+
}
|
|
1584
|
+
function startUpload(view, file, blobUrl, uploadId, options) {
|
|
1585
|
+
options.onImageUpload(file).then((result) => {
|
|
1586
|
+
if (blobUrl) URL.revokeObjectURL(blobUrl);
|
|
1587
|
+
if (view.isDestroyed) return;
|
|
1588
|
+
const upload = {
|
|
1589
|
+
status: "success",
|
|
1590
|
+
attrs: typeof result === "string" ? { src: result } : result
|
|
1591
|
+
};
|
|
1592
|
+
const found = findUploadNode(view.state.doc, uploadId);
|
|
1593
|
+
if (!found) {
|
|
1594
|
+
getSettledUploads(view).set(uploadId, upload);
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
view.dispatch(settleUploadNode(view.state.tr, found.pos, found.node, upload).setMeta("addToHistory", false));
|
|
1598
|
+
}).catch((error) => {
|
|
1599
|
+
options.onImageUploadError?.(file, error);
|
|
1600
|
+
const upload = {
|
|
1601
|
+
status: "error",
|
|
1602
|
+
blobUrl,
|
|
1603
|
+
removalTimer: null
|
|
1604
|
+
};
|
|
1605
|
+
if (view.isDestroyed) {
|
|
1606
|
+
revokeBlobUrl(upload);
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
getSettledUploads(view).set(uploadId, upload);
|
|
1610
|
+
const found = findUploadNode(view.state.doc, uploadId);
|
|
1611
|
+
if (!found) {
|
|
1612
|
+
revokeBlobUrl(upload);
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
view.dispatch(settleUploadNode(view.state.tr, found.pos, found.node, upload).setMeta("addToHistory", false));
|
|
1616
|
+
scheduleFailedUploadRemoval(view, uploadId, upload);
|
|
1617
|
+
});
|
|
1618
|
+
}
|
|
1619
|
+
function getImageFiles(files) {
|
|
1620
|
+
return Array.from(files ?? []).filter((file) => file.type.startsWith("image/"));
|
|
1621
|
+
}
|
|
1622
|
+
function insertImageFilesWithUpload(view, files, pos, options) {
|
|
1623
|
+
if (files.length === 0) return;
|
|
1624
|
+
const { schema } = view.state;
|
|
1625
|
+
if (!schema.nodes.image?.spec.attrs?.uploadId) {
|
|
1626
|
+
warnMissingUploadExtension();
|
|
1627
|
+
return;
|
|
1628
|
+
}
|
|
1629
|
+
const uploads = files.map((file) => ({
|
|
1630
|
+
file,
|
|
1631
|
+
blobUrl: URL.createObjectURL(file),
|
|
1632
|
+
uploadId: createUploadId()
|
|
1633
|
+
}));
|
|
1634
|
+
view.dispatch(view.state.tr.insert(pos, uploads.map(({ blobUrl, uploadId }) => schema.nodes.image.create({
|
|
1635
|
+
src: blobUrl,
|
|
1636
|
+
uploading: true,
|
|
1637
|
+
uploadError: false,
|
|
1638
|
+
uploadId
|
|
1639
|
+
}))));
|
|
1640
|
+
uploads.forEach(({ file, blobUrl, uploadId }) => {
|
|
1641
|
+
startUpload(view, file, blobUrl, uploadId, options);
|
|
1642
|
+
});
|
|
1643
|
+
}
|
|
1644
|
+
function insertImageWithUpload(view, file, pos, options) {
|
|
1645
|
+
insertImageFilesWithUpload(view, [file], pos, options);
|
|
1646
|
+
}
|
|
1647
|
+
function isDataUrl(src) {
|
|
1648
|
+
return /^data:/i.test(src);
|
|
1649
|
+
}
|
|
1650
|
+
function getExtensionFromMimeType(type) {
|
|
1651
|
+
return (type.split("/")[1] ?? "").split("+")[0] || "bin";
|
|
1652
|
+
}
|
|
1653
|
+
function dataUrlToFile(src) {
|
|
1654
|
+
const separator = src.indexOf(",");
|
|
1655
|
+
if (separator === -1) throw new Error("Invalid data URL");
|
|
1656
|
+
const header = src.slice(5, separator);
|
|
1657
|
+
const data = src.slice(separator + 1);
|
|
1658
|
+
const [type = "", ...params] = header.split(";");
|
|
1659
|
+
const mimeType = type || "application/octet-stream";
|
|
1660
|
+
const bytes = params.includes("base64") ? Uint8Array.from(atob(data), (char) => char.charCodeAt(0)) : new TextEncoder().encode(decodeURIComponent(data));
|
|
1661
|
+
return new File([bytes], `pasted-image.${getExtensionFromMimeType(mimeType)}`, { type: mimeType });
|
|
1662
|
+
}
|
|
1663
|
+
function getRemoteFileName(src, type) {
|
|
1664
|
+
let name = "";
|
|
1665
|
+
try {
|
|
1666
|
+
name = decodeURIComponent(new URL(src, window.location.href).pathname.split("/").pop() ?? "");
|
|
1667
|
+
} catch {
|
|
1668
|
+
name = "";
|
|
1669
|
+
}
|
|
1670
|
+
if (!name) return `pasted-image.${getExtensionFromMimeType(type)}`;
|
|
1671
|
+
return name.includes(".") ? name : `${name}.${getExtensionFromMimeType(type)}`;
|
|
1672
|
+
}
|
|
1673
|
+
async function remoteUrlToFile(src) {
|
|
1674
|
+
const response = await fetch(src);
|
|
1675
|
+
if (!response.ok) throw new Error(`Failed to fetch image: ${response.status}`);
|
|
1676
|
+
const blob = await response.blob();
|
|
1677
|
+
const type = blob.type || "application/octet-stream";
|
|
1678
|
+
return new File([blob], getRemoteFileName(src, type), { type });
|
|
1679
|
+
}
|
|
1680
|
+
async function pastedImageToFile(src) {
|
|
1681
|
+
return isDataUrl(src) ? dataUrlToFile(src) : remoteUrlToFile(src);
|
|
1682
|
+
}
|
|
1683
|
+
function restoreImageNode(view, uploadId, src) {
|
|
1684
|
+
if (view.isDestroyed) return;
|
|
1685
|
+
const upload = {
|
|
1686
|
+
status: "success",
|
|
1687
|
+
attrs: { src }
|
|
1688
|
+
};
|
|
1689
|
+
const found = findUploadNode(view.state.doc, uploadId);
|
|
1690
|
+
if (!found) {
|
|
1691
|
+
getSettledUploads(view).set(uploadId, upload);
|
|
1692
|
+
return;
|
|
1693
|
+
}
|
|
1694
|
+
view.dispatch(settleUploadNode(view.state.tr, found.pos, found.node, upload).setMeta("addToHistory", false));
|
|
1695
|
+
}
|
|
1696
|
+
function shouldUploadPastedImage(node, options) {
|
|
1697
|
+
if (node.type.name !== "image" || node.attrs.uploadId || typeof node.attrs.src !== "string") return false;
|
|
1698
|
+
return options.shouldUploadPastedImage ? options.shouldUploadPastedImage(node.attrs.src) : isDataUrl(node.attrs.src);
|
|
1699
|
+
}
|
|
1700
|
+
function mapPastedSlice(slice, options) {
|
|
1701
|
+
const pending = [];
|
|
1702
|
+
const mapFragment = (fragment) => {
|
|
1703
|
+
const nodes = [];
|
|
1704
|
+
fragment.forEach((node) => {
|
|
1705
|
+
if (shouldUploadPastedImage(node, options)) {
|
|
1706
|
+
const uploadId = createUploadId();
|
|
1707
|
+
pending.push({
|
|
1708
|
+
uploadId,
|
|
1709
|
+
src: node.attrs.src
|
|
1710
|
+
});
|
|
1711
|
+
nodes.push(node.type.create({
|
|
1712
|
+
...node.attrs,
|
|
1713
|
+
uploading: true,
|
|
1714
|
+
uploadError: false,
|
|
1715
|
+
uploadId
|
|
1716
|
+
}, null, node.marks));
|
|
1717
|
+
return;
|
|
1718
|
+
}
|
|
1719
|
+
nodes.push(node.content.size > 0 ? node.copy(mapFragment(node.content)) : node);
|
|
1720
|
+
});
|
|
1721
|
+
return _tiptap_pm_model.Fragment.fromArray(nodes);
|
|
1722
|
+
};
|
|
1723
|
+
const content = mapFragment(slice.content);
|
|
1724
|
+
return {
|
|
1725
|
+
slice: pending.length > 0 ? new _tiptap_pm_model.Slice(content, slice.openStart, slice.openEnd) : slice,
|
|
1726
|
+
pending
|
|
1727
|
+
};
|
|
1728
|
+
}
|
|
1729
|
+
function stripImages(slice) {
|
|
1730
|
+
const stripFragment = (fragment) => {
|
|
1731
|
+
const nodes = [];
|
|
1732
|
+
fragment.forEach((node) => {
|
|
1733
|
+
if (node.type.name === "image") return;
|
|
1734
|
+
nodes.push(node.content.size > 0 ? node.copy(stripFragment(node.content)) : node);
|
|
1735
|
+
});
|
|
1736
|
+
return _tiptap_pm_model.Fragment.fromArray(nodes);
|
|
1737
|
+
};
|
|
1738
|
+
return new _tiptap_pm_model.Slice(stripFragment(slice.content), slice.openStart, slice.openEnd);
|
|
1739
|
+
}
|
|
1740
|
+
function startPastedUploads(view, pending, options) {
|
|
1741
|
+
pending.forEach(({ uploadId, src }) => {
|
|
1742
|
+
pastedImageToFile(src).then((file) => startUpload(view, file, null, uploadId, options), () => restoreImageNode(view, uploadId, src));
|
|
1743
|
+
});
|
|
1744
|
+
}
|
|
1745
|
+
function getBlockInsertPos(state) {
|
|
1746
|
+
const { $from } = state.selection;
|
|
1747
|
+
if (state.schema.nodes.image?.isInline || !$from.parent.isTextblock) return $from.pos;
|
|
1748
|
+
if ($from.parentOffset === 0) return $from.before();
|
|
1749
|
+
if ($from.parentOffset === $from.parent.content.size) return $from.after();
|
|
1750
|
+
return $from.pos;
|
|
1751
|
+
}
|
|
1752
|
+
function hasUploadImageNode(view) {
|
|
1753
|
+
return Boolean(view.state.schema.nodes.image?.spec.attrs?.uploadId);
|
|
1754
|
+
}
|
|
1755
|
+
function getUploadImageExtensionOptions(editor) {
|
|
1756
|
+
const extension = editor?.extensionManager?.extensions?.find((item) => item.name === "image");
|
|
1757
|
+
return {
|
|
1758
|
+
onImageUpload: extension?.options?.onImageUpload,
|
|
1759
|
+
onImageUploadError: extension?.options?.onImageUploadError
|
|
1760
|
+
};
|
|
1761
|
+
}
|
|
1762
|
+
function getUploadImageExtension(TipTapImage, options) {
|
|
1763
|
+
return TipTapImage.extend({
|
|
1764
|
+
addOptions() {
|
|
1765
|
+
return {
|
|
1766
|
+
...this.parent?.(),
|
|
1767
|
+
onImageUpload: options.onImageUpload,
|
|
1768
|
+
onImageUploadError: options.onImageUploadError,
|
|
1769
|
+
shouldUploadPastedImage: options.shouldUploadPastedImage
|
|
1770
|
+
};
|
|
1771
|
+
},
|
|
1772
|
+
parseHTML() {
|
|
1773
|
+
return [{ tag: "img[src]" }];
|
|
1774
|
+
},
|
|
1775
|
+
addAttributes() {
|
|
1776
|
+
return {
|
|
1777
|
+
...this.parent?.(),
|
|
1778
|
+
uploading: {
|
|
1779
|
+
default: false,
|
|
1780
|
+
renderHTML: (attrs) => attrs.uploading ? { "data-uploading": "" } : {}
|
|
1781
|
+
},
|
|
1782
|
+
uploadError: {
|
|
1783
|
+
default: false,
|
|
1784
|
+
renderHTML: (attrs) => attrs.uploadError ? { "data-upload-error": "" } : {}
|
|
1785
|
+
},
|
|
1786
|
+
uploadId: {
|
|
1787
|
+
default: null,
|
|
1788
|
+
renderHTML: () => ({})
|
|
1789
|
+
}
|
|
1790
|
+
};
|
|
1791
|
+
},
|
|
1792
|
+
addNodeView() {
|
|
1793
|
+
const parentNodeView = this.parent?.();
|
|
1794
|
+
return (props) => {
|
|
1795
|
+
const { node, HTMLAttributes } = props;
|
|
1796
|
+
if (!node.attrs.uploading && !node.attrs.uploadError) {
|
|
1797
|
+
if (parentNodeView) return parentNodeView(props);
|
|
1798
|
+
const img = document.createElement("img");
|
|
1799
|
+
Object.entries(HTMLAttributes).forEach(([key, value]) => {
|
|
1800
|
+
if (value !== null && value !== void 0) img.setAttribute(key, String(value));
|
|
1801
|
+
});
|
|
1802
|
+
img.src = node.attrs.src;
|
|
1803
|
+
return { dom: img };
|
|
1804
|
+
}
|
|
1805
|
+
const isInline = this.options?.inline;
|
|
1806
|
+
const wrapper = document.createElement(isInline ? "span" : "div");
|
|
1807
|
+
wrapper.classList.add("mantine-upload-image-wrapper");
|
|
1808
|
+
if (node.attrs.uploading) wrapper.setAttribute("data-uploading", "");
|
|
1809
|
+
if (node.attrs.uploadError) wrapper.setAttribute("data-upload-error", "");
|
|
1810
|
+
const img = document.createElement("img");
|
|
1811
|
+
Object.entries(HTMLAttributes).forEach(([key, value]) => {
|
|
1812
|
+
if (value !== null && value !== void 0 && key !== "data-uploading" && key !== "data-upload-error") img.setAttribute(key, String(value));
|
|
1813
|
+
});
|
|
1814
|
+
img.src = node.attrs.src;
|
|
1815
|
+
if (node.attrs.alt) img.alt = node.attrs.alt;
|
|
1816
|
+
if (node.attrs.title) img.title = node.attrs.title;
|
|
1817
|
+
wrapper.appendChild(img);
|
|
1818
|
+
return { dom: wrapper };
|
|
1819
|
+
};
|
|
1820
|
+
},
|
|
1821
|
+
addProseMirrorPlugins() {
|
|
1822
|
+
const uploadOptions = {
|
|
1823
|
+
onImageUpload: this.options.onImageUpload,
|
|
1824
|
+
onImageUploadError: this.options.onImageUploadError,
|
|
1825
|
+
shouldUploadPastedImage: this.options.shouldUploadPastedImage
|
|
1826
|
+
};
|
|
1827
|
+
let editorView = null;
|
|
1828
|
+
return [new _tiptap_pm_state.Plugin({
|
|
1829
|
+
key: uploadImagePluginKey,
|
|
1830
|
+
view(view) {
|
|
1831
|
+
editorView = view;
|
|
1832
|
+
return { destroy: () => {
|
|
1833
|
+
editorView = null;
|
|
1834
|
+
} };
|
|
1835
|
+
},
|
|
1836
|
+
appendTransaction(transactions, _oldState, newState) {
|
|
1837
|
+
return repairSettledUploads(editorView, transactions, newState);
|
|
1838
|
+
},
|
|
1839
|
+
props: {
|
|
1840
|
+
handleDrop(view, event, slice, moved) {
|
|
1841
|
+
if (!event.dataTransfer || !uploadOptions.onImageUpload) return false;
|
|
1842
|
+
const files = getImageFiles(event.dataTransfer.files);
|
|
1843
|
+
const pos = view.posAtCoords({
|
|
1844
|
+
left: event.clientX,
|
|
1845
|
+
top: event.clientY
|
|
1846
|
+
});
|
|
1847
|
+
if (files.length > 0) {
|
|
1848
|
+
event.preventDefault();
|
|
1849
|
+
if (pos) insertImageFilesWithUpload(view, files, pos.pos, uploadOptions);
|
|
1850
|
+
return true;
|
|
1851
|
+
}
|
|
1852
|
+
if (moved || !pos || !hasUploadImageNode(view)) return false;
|
|
1853
|
+
const mapped = mapPastedSlice(slice, uploadOptions);
|
|
1854
|
+
if (mapped.pending.length === 0) return false;
|
|
1855
|
+
event.preventDefault();
|
|
1856
|
+
const insertPos = (0, _tiptap_pm_transform.dropPoint)(view.state.doc, pos.pos, mapped.slice) ?? pos.pos;
|
|
1857
|
+
view.dispatch(view.state.tr.replaceRange(insertPos, insertPos, mapped.slice).setMeta("uiEvent", "drop"));
|
|
1858
|
+
startPastedUploads(view, mapped.pending, uploadOptions);
|
|
1859
|
+
return true;
|
|
1860
|
+
},
|
|
1861
|
+
handlePaste(view, event, slice) {
|
|
1862
|
+
if (!event.clipboardData || !uploadOptions.onImageUpload) return false;
|
|
1863
|
+
const files = getImageFiles(event.clipboardData.files);
|
|
1864
|
+
if (files.length > 0) {
|
|
1865
|
+
event.preventDefault();
|
|
1866
|
+
const text = stripImages(slice);
|
|
1867
|
+
if (text.content.size > 0 && text.content.textBetween(0, text.content.size)) view.dispatch(view.state.tr.replaceSelection(text).setMeta("paste", true).setMeta("uiEvent", "paste"));
|
|
1868
|
+
insertImageFilesWithUpload(view, files, getBlockInsertPos(view.state), uploadOptions);
|
|
1869
|
+
return true;
|
|
1870
|
+
}
|
|
1871
|
+
if (!hasUploadImageNode(view)) return false;
|
|
1872
|
+
const mapped = mapPastedSlice(slice, uploadOptions);
|
|
1873
|
+
if (mapped.pending.length === 0) return false;
|
|
1874
|
+
event.preventDefault();
|
|
1875
|
+
view.dispatch(view.state.tr.replaceSelection(mapped.slice).scrollIntoView().setMeta("paste", true).setMeta("uiEvent", "paste"));
|
|
1876
|
+
startPastedUploads(view, mapped.pending, uploadOptions);
|
|
1877
|
+
return true;
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
})];
|
|
1881
|
+
}
|
|
1882
|
+
});
|
|
1883
|
+
}
|
|
1884
|
+
//#endregion
|
|
1885
|
+
//#region src/RichTextEditorControl/RichTextEditorImageUploadControl.vue?vue&type=script&setup=true&lang.ts
|
|
1886
|
+
var _hoisted_1 = ["accept"];
|
|
1887
|
+
//#endregion
|
|
1888
|
+
//#region src/RichTextEditorControl/RichTextEditorImageUploadControl.ts
|
|
1889
|
+
var RichTextEditorImageUploadControl = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
1890
|
+
name: "RichTextEditorImageUploadControl",
|
|
1891
|
+
inheritAttrs: false,
|
|
1892
|
+
__name: "RichTextEditorImageUploadControl",
|
|
1893
|
+
props: {
|
|
1894
|
+
accept: { default: "image/*" },
|
|
1895
|
+
disabled: {
|
|
1896
|
+
type: Boolean,
|
|
1897
|
+
default: false
|
|
1898
|
+
},
|
|
1899
|
+
icon: {}
|
|
1900
|
+
},
|
|
1901
|
+
setup(__props) {
|
|
1902
|
+
const props = __props;
|
|
1903
|
+
const attrs = (0, vue.useAttrs)();
|
|
1904
|
+
const ctx = useRichTextEditorContext();
|
|
1905
|
+
const input = (0, vue.ref)(null);
|
|
1906
|
+
const usable = (0, vue.computed)(() => !!ctx.editor && !ctx.editor.isDestroyed && ctx.editor.isEditable);
|
|
1907
|
+
const disabled = (0, vue.computed)(() => !usable.value || props.disabled);
|
|
1908
|
+
function open(event) {
|
|
1909
|
+
const handler = attrs.onClick;
|
|
1910
|
+
if (Array.isArray(handler)) handler.forEach((item) => item?.(event));
|
|
1911
|
+
else handler?.(event);
|
|
1912
|
+
if (!event.defaultPrevented) input.value?.click();
|
|
1913
|
+
}
|
|
1914
|
+
function change(event) {
|
|
1915
|
+
const target = event.currentTarget;
|
|
1916
|
+
const files = getImageFiles(target.files);
|
|
1917
|
+
if (!files.length || !ctx.editor || !usable.value) return;
|
|
1918
|
+
const extension = getUploadImageExtensionOptions(ctx.editor);
|
|
1919
|
+
const onImageUpload = ctx.onImageUpload ?? extension.onImageUpload;
|
|
1920
|
+
if (!onImageUpload) return;
|
|
1921
|
+
const { view } = ctx.editor;
|
|
1922
|
+
insertImageFilesWithUpload(view, files, view.state.selection.from, {
|
|
1923
|
+
onImageUpload,
|
|
1924
|
+
onImageUploadError: ctx.onImageUploadError ?? extension.onImageUploadError
|
|
1925
|
+
});
|
|
1926
|
+
target.value = "";
|
|
1927
|
+
}
|
|
1928
|
+
return (_ctx, _cache) => {
|
|
1929
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, null, [(0, vue.createVNode)(RichTextEditorControlBase_default, (0, vue.mergeProps)((0, vue.unref)(attrs), {
|
|
1930
|
+
icon: props.icon || (0, vue.unref)(IconPhoto),
|
|
1931
|
+
disabled: disabled.value,
|
|
1932
|
+
interactive: true,
|
|
1933
|
+
variant: (0, vue.unref)(ctx).variant,
|
|
1934
|
+
"aria-label": (0, vue.unref)(ctx).labels.imageUploadControlLabel,
|
|
1935
|
+
title: (0, vue.unref)(ctx).labels.imageUploadControlLabel,
|
|
1936
|
+
onClick: open
|
|
1937
|
+
}), null, 16, [
|
|
1938
|
+
"icon",
|
|
1939
|
+
"disabled",
|
|
1940
|
+
"variant",
|
|
1941
|
+
"aria-label",
|
|
1942
|
+
"title"
|
|
1943
|
+
]), (0, vue.createElementVNode)("input", {
|
|
1944
|
+
ref_key: "input",
|
|
1945
|
+
ref: input,
|
|
1946
|
+
type: "file",
|
|
1947
|
+
accept: props.accept,
|
|
1948
|
+
multiple: "",
|
|
1949
|
+
hidden: "",
|
|
1950
|
+
onChange: change
|
|
1951
|
+
}, null, 40, _hoisted_1)], 64);
|
|
1952
|
+
};
|
|
1953
|
+
}
|
|
1954
|
+
});
|
|
1955
|
+
//#endregion
|
|
1472
1956
|
//#region src/RichTextEditorControl/controls.ts
|
|
1473
1957
|
var BoldControl = createControl({
|
|
1474
1958
|
label: "boldControlLabel",
|
|
@@ -1915,7 +2399,8 @@ var RichTextEditor = (0, _mantine_vue_core.factory)(RichTextEditor_default, {
|
|
|
1915
2399
|
TableMergeCells: TableMergeCellsControl,
|
|
1916
2400
|
TableSplitCell: TableSplitCellControl,
|
|
1917
2401
|
Details: RichTextEditorDetailsControl_default,
|
|
1918
|
-
InvisibleCharacters: RichTextEditorInvisibleCharactersControl_default
|
|
2402
|
+
InvisibleCharacters: RichTextEditorInvisibleCharactersControl_default,
|
|
2403
|
+
ImageUpload: RichTextEditorImageUploadControl
|
|
1919
2404
|
});
|
|
1920
2405
|
//#endregion
|
|
1921
2406
|
//#region src/extensions/Link.ts
|
|
@@ -1965,6 +2450,7 @@ exports.RichTextEditorColorPickerControl = RichTextEditorColorPickerControl;
|
|
|
1965
2450
|
exports.RichTextEditorControl = RichTextEditorControl;
|
|
1966
2451
|
exports.RichTextEditorControlBase = RichTextEditorControlBase;
|
|
1967
2452
|
exports.RichTextEditorDetailsControl = RichTextEditorDetailsControl_default;
|
|
2453
|
+
exports.RichTextEditorImageUploadControl = RichTextEditorImageUploadControl;
|
|
1968
2454
|
exports.RichTextEditorInvisibleCharactersControl = RichTextEditorInvisibleCharactersControl_default;
|
|
1969
2455
|
exports.RichTextEditorLinkControl = RichTextEditorLinkControl;
|
|
1970
2456
|
exports.RichTextEditorSourceCodeControl = RichTextEditorSourceCodeControl;
|
|
@@ -1991,7 +2477,12 @@ exports.UndoControl = UndoControl;
|
|
|
1991
2477
|
exports.UnlinkControl = UnlinkControl;
|
|
1992
2478
|
exports.UnsetColorControl = UnsetColorControl;
|
|
1993
2479
|
exports.createControl = createControl;
|
|
2480
|
+
exports.getImageFiles = getImageFiles;
|
|
1994
2481
|
exports.getTaskListExtension = getTaskListExtension;
|
|
2482
|
+
exports.getUploadImageExtension = getUploadImageExtension;
|
|
2483
|
+
exports.getUploadImageExtensionOptions = getUploadImageExtensionOptions;
|
|
2484
|
+
exports.insertImageFilesWithUpload = insertImageFilesWithUpload;
|
|
2485
|
+
exports.insertImageWithUpload = insertImageWithUpload;
|
|
1995
2486
|
exports.useRichTextEditorContext = useRichTextEditorContext;
|
|
1996
2487
|
|
|
1997
2488
|
//# sourceMappingURL=index.cjs.map
|