@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/esm/index.mjs
CHANGED
|
@@ -2,6 +2,9 @@ import { ActionIcon, Box, Button, ColorPicker, ColorSwatch, Group, Popover, Simp
|
|
|
2
2
|
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, guardReactiveProps, mergeProps, normalizeProps, normalizeStyle, onBeforeUnmount, onMounted, openBlock, ref, renderList, renderSlot, resolveDynamicComponent, toDisplayString, unref, useAttrs, watch, withCtx } from "vue";
|
|
3
3
|
import { EditorContent } from "@tiptap/vue-3";
|
|
4
4
|
import { useDisclosure } from "@mantine-vue/hooks";
|
|
5
|
+
import { Fragment as Fragment$1, Slice } from "@tiptap/pm/model";
|
|
6
|
+
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
|
7
|
+
import { dropPoint } from "@tiptap/pm/transform";
|
|
5
8
|
import TipTapLink from "@tiptap/extension-link";
|
|
6
9
|
//#region src/labels.ts
|
|
7
10
|
var DEFAULT_LABELS = {
|
|
@@ -55,6 +58,7 @@ var DEFAULT_LABELS = {
|
|
|
55
58
|
tableSplitCellControlLabel: "Split cell",
|
|
56
59
|
detailsControlLabel: "Toggle collapsible section",
|
|
57
60
|
invisibleCharactersControlLabel: "Toggle invisible characters",
|
|
61
|
+
imageUploadControlLabel: "Upload image",
|
|
58
62
|
linkEditorInputLabel: "Enter URL",
|
|
59
63
|
linkEditorInputPlaceholder: "https://example.com/",
|
|
60
64
|
linkEditorExternalLink: "Open link in a new tab",
|
|
@@ -71,23 +75,24 @@ var DEFAULT_LABELS = {
|
|
|
71
75
|
//#region src/RichTextEditor.context.ts
|
|
72
76
|
var [provideRichTextEditorContext, useRichTextEditorContext] = createSafeContext("RichTextEditor component was not found in tree");
|
|
73
77
|
var RichTextEditor_module_default = {
|
|
74
|
-
root: "
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
78
|
+
root: "_root_1jng1_1",
|
|
79
|
+
"mantine-upload-image-pulse": "_mantine-upload-image-pulse_1jng1_1",
|
|
80
|
+
Typography: "_Typography_1jng1_64",
|
|
81
|
+
content: "_content_1jng1_92",
|
|
82
|
+
linkEditorDropdown: "_linkEditorDropdown_1jng1_337",
|
|
83
|
+
tableInsertDropdown: "_tableInsertDropdown_1jng1_341",
|
|
84
|
+
tableInsertGrid: "_tableInsertGrid_1jng1_346",
|
|
85
|
+
tableInsertCell: "_tableInsertCell_1jng1_352",
|
|
86
|
+
tableInsertLabel: "_tableInsertLabel_1jng1_369",
|
|
87
|
+
control: "_control_1jng1_376",
|
|
88
|
+
controlIcon: "_controlIcon_1jng1_451",
|
|
89
|
+
controlsGroup: "_controlsGroup_1jng1_456",
|
|
90
|
+
linkEditor: "_linkEditor_1jng1_337",
|
|
91
|
+
linkEditorInput: "_linkEditorInput_1jng1_485",
|
|
92
|
+
linkEditorExternalControl: "_linkEditorExternalControl_1jng1_491",
|
|
93
|
+
linkEditorSave: "_linkEditorSave_1jng1_521",
|
|
94
|
+
toolbar: "_toolbar_1jng1_526",
|
|
95
|
+
taskList: "_taskList_1jng1_560"
|
|
91
96
|
};
|
|
92
97
|
//#endregion
|
|
93
98
|
//#region src/RichTextEditor.vue
|
|
@@ -111,6 +116,14 @@ var RichTextEditor_default = /* @__PURE__ */ defineComponent({
|
|
|
111
116
|
},
|
|
112
117
|
labels: { default: void 0 },
|
|
113
118
|
variant: { default: "default" },
|
|
119
|
+
onImageUpload: {
|
|
120
|
+
type: Function,
|
|
121
|
+
default: void 0
|
|
122
|
+
},
|
|
123
|
+
onImageUploadError: {
|
|
124
|
+
type: Function,
|
|
125
|
+
default: void 0
|
|
126
|
+
},
|
|
114
127
|
classNames: { default: void 0 },
|
|
115
128
|
styles: { default: void 0 },
|
|
116
129
|
vars: { default: void 0 },
|
|
@@ -127,8 +140,7 @@ var RichTextEditor_default = /* @__PURE__ */ defineComponent({
|
|
|
127
140
|
name: "RichTextEditor",
|
|
128
141
|
classes: RichTextEditor_module_default,
|
|
129
142
|
props,
|
|
130
|
-
|
|
131
|
-
style: attrs.style,
|
|
143
|
+
attrs,
|
|
132
144
|
classNames: props.classNames,
|
|
133
145
|
styles: props.styles,
|
|
134
146
|
vars: props.vars,
|
|
@@ -169,6 +181,12 @@ var RichTextEditor_default = /* @__PURE__ */ defineComponent({
|
|
|
169
181
|
},
|
|
170
182
|
get variant() {
|
|
171
183
|
return props.variant;
|
|
184
|
+
},
|
|
185
|
+
get onImageUpload() {
|
|
186
|
+
return props.onImageUpload;
|
|
187
|
+
},
|
|
188
|
+
get onImageUploadError() {
|
|
189
|
+
return props.onImageUploadError;
|
|
172
190
|
}
|
|
173
191
|
});
|
|
174
192
|
return (_ctx, _cache) => {
|
|
@@ -511,7 +529,7 @@ var RichTextEditorColorControl = factory(/* @__PURE__ */ defineComponent({
|
|
|
511
529
|
}), { classes: RichTextEditor_module_default });
|
|
512
530
|
//#endregion
|
|
513
531
|
//#region src/icons/RichTextEditorIcon.vue?vue&type=script&setup=true&lang.ts
|
|
514
|
-
var _hoisted_1 = ["d"];
|
|
532
|
+
var _hoisted_1$1 = ["d"];
|
|
515
533
|
//#endregion
|
|
516
534
|
//#region src/icons/RichTextEditorIcon.ts
|
|
517
535
|
var RichTextEditorIcon = factory(/* @__PURE__ */ defineComponent({
|
|
@@ -538,7 +556,7 @@ var RichTextEditorIcon = factory(/* @__PURE__ */ defineComponent({
|
|
|
538
556
|
return openBlock(), createElementBlock("path", {
|
|
539
557
|
key: path,
|
|
540
558
|
d: path
|
|
541
|
-
}, null, 8, _hoisted_1);
|
|
559
|
+
}, null, 8, _hoisted_1$1);
|
|
542
560
|
}), 128))], 16);
|
|
543
561
|
};
|
|
544
562
|
}
|
|
@@ -809,6 +827,12 @@ var IconPilcrow = createIcon("IconPilcrow", [
|
|
|
809
827
|
path("M17 4v16"),
|
|
810
828
|
path("M19 4h-9.5a4.5 4.5 0 0 0 0 9h3.5")
|
|
811
829
|
]);
|
|
830
|
+
var IconPhoto = createIcon("IconPhoto", [
|
|
831
|
+
path("M15 8h.01"),
|
|
832
|
+
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"),
|
|
833
|
+
path("M3 16l5 -5c.928 -.893 2.072 -.893 3 0l5 5"),
|
|
834
|
+
path("M14 14l1 -1c.928 -.893 2.072 -.893 3 0l3 3")
|
|
835
|
+
]);
|
|
812
836
|
//#endregion
|
|
813
837
|
//#region src/RichTextEditorControl/RichTextEditorColorPickerControl.ts
|
|
814
838
|
var RichTextEditorColorPickerControl = factory(/* @__PURE__ */ defineComponent({
|
|
@@ -1427,6 +1451,466 @@ var RichTextEditorInvisibleCharactersControl_default = /* @__PURE__ */ defineCom
|
|
|
1427
1451
|
}
|
|
1428
1452
|
});
|
|
1429
1453
|
//#endregion
|
|
1454
|
+
//#region src/extensions/UploadImage.ts
|
|
1455
|
+
var FAILED_UPLOAD_REMOVAL_DELAY = 3e3;
|
|
1456
|
+
var uploadImagePluginKey = new PluginKey("uploadImage");
|
|
1457
|
+
var settledUploads = /* @__PURE__ */ new WeakMap();
|
|
1458
|
+
var missingExtensionWarned = false;
|
|
1459
|
+
function warnMissingUploadExtension() {
|
|
1460
|
+
if (missingExtensionWarned) return;
|
|
1461
|
+
missingExtensionWarned = true;
|
|
1462
|
+
console.warn("[@mantine-vue/tiptap] Image upload requires the image extension created with getUploadImageExtension");
|
|
1463
|
+
}
|
|
1464
|
+
function getSettledUploads(view) {
|
|
1465
|
+
let settled = settledUploads.get(view);
|
|
1466
|
+
if (!settled) {
|
|
1467
|
+
settled = /* @__PURE__ */ new Map();
|
|
1468
|
+
settledUploads.set(view, settled);
|
|
1469
|
+
}
|
|
1470
|
+
return settled;
|
|
1471
|
+
}
|
|
1472
|
+
function findUploadNode(doc, uploadId) {
|
|
1473
|
+
let result = null;
|
|
1474
|
+
doc.descendants((node, pos) => {
|
|
1475
|
+
if (result) return false;
|
|
1476
|
+
if (node.type.name === "image" && node.attrs.uploadId === uploadId) {
|
|
1477
|
+
result = {
|
|
1478
|
+
node,
|
|
1479
|
+
pos
|
|
1480
|
+
};
|
|
1481
|
+
return false;
|
|
1482
|
+
}
|
|
1483
|
+
return true;
|
|
1484
|
+
});
|
|
1485
|
+
return result;
|
|
1486
|
+
}
|
|
1487
|
+
function revokeBlobUrl(upload) {
|
|
1488
|
+
if (upload.blobUrl) {
|
|
1489
|
+
URL.revokeObjectURL(upload.blobUrl);
|
|
1490
|
+
upload.blobUrl = null;
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
function settleUploadNode(tr, pos, node, upload) {
|
|
1494
|
+
if (upload.status === "success") return tr.setNodeMarkup(pos, void 0, {
|
|
1495
|
+
...node.attrs,
|
|
1496
|
+
...upload.attrs,
|
|
1497
|
+
uploading: false,
|
|
1498
|
+
uploadError: false,
|
|
1499
|
+
uploadId: null
|
|
1500
|
+
});
|
|
1501
|
+
return tr.setNodeMarkup(pos, void 0, {
|
|
1502
|
+
...node.attrs,
|
|
1503
|
+
uploading: false,
|
|
1504
|
+
uploadError: true
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
function scheduleFailedUploadRemoval(view, uploadId, upload) {
|
|
1508
|
+
upload.removalTimer = setTimeout(() => {
|
|
1509
|
+
upload.removalTimer = null;
|
|
1510
|
+
revokeBlobUrl(upload);
|
|
1511
|
+
if (view.isDestroyed) return;
|
|
1512
|
+
const found = findUploadNode(view.state.doc, uploadId);
|
|
1513
|
+
if (found) {
|
|
1514
|
+
getSettledUploads(view).delete(uploadId);
|
|
1515
|
+
view.dispatch(view.state.tr.delete(found.pos, found.pos + found.node.nodeSize).setMeta("addToHistory", false));
|
|
1516
|
+
}
|
|
1517
|
+
}, FAILED_UPLOAD_REMOVAL_DELAY);
|
|
1518
|
+
}
|
|
1519
|
+
function repairSettledUploads(view, transactions, newState) {
|
|
1520
|
+
if (!view || !transactions.some((tr) => tr.docChanged)) return null;
|
|
1521
|
+
const settled = settledUploads.get(view);
|
|
1522
|
+
if (!settled || settled.size === 0) return null;
|
|
1523
|
+
let tr = null;
|
|
1524
|
+
newState.doc.descendants((node, pos) => {
|
|
1525
|
+
if (node.type.name !== "image") return true;
|
|
1526
|
+
const upload = node.attrs.uploadId ? settled.get(node.attrs.uploadId) : void 0;
|
|
1527
|
+
if (!upload) return false;
|
|
1528
|
+
if (upload.status === "success") {
|
|
1529
|
+
tr = settleUploadNode(tr ?? newState.tr, pos, node, upload);
|
|
1530
|
+
settled.delete(node.attrs.uploadId);
|
|
1531
|
+
return false;
|
|
1532
|
+
}
|
|
1533
|
+
if (!node.attrs.uploadError) tr = settleUploadNode(tr ?? newState.tr, pos, node, upload);
|
|
1534
|
+
if (!upload.removalTimer) scheduleFailedUploadRemoval(view, node.attrs.uploadId, upload);
|
|
1535
|
+
return false;
|
|
1536
|
+
});
|
|
1537
|
+
return tr ? tr.setMeta("addToHistory", false) : null;
|
|
1538
|
+
}
|
|
1539
|
+
function createUploadId() {
|
|
1540
|
+
return Math.random().toString(36).slice(2, 10);
|
|
1541
|
+
}
|
|
1542
|
+
function startUpload(view, file, blobUrl, uploadId, options) {
|
|
1543
|
+
options.onImageUpload(file).then((result) => {
|
|
1544
|
+
if (blobUrl) URL.revokeObjectURL(blobUrl);
|
|
1545
|
+
if (view.isDestroyed) return;
|
|
1546
|
+
const upload = {
|
|
1547
|
+
status: "success",
|
|
1548
|
+
attrs: typeof result === "string" ? { src: result } : result
|
|
1549
|
+
};
|
|
1550
|
+
const found = findUploadNode(view.state.doc, uploadId);
|
|
1551
|
+
if (!found) {
|
|
1552
|
+
getSettledUploads(view).set(uploadId, upload);
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
1555
|
+
view.dispatch(settleUploadNode(view.state.tr, found.pos, found.node, upload).setMeta("addToHistory", false));
|
|
1556
|
+
}).catch((error) => {
|
|
1557
|
+
options.onImageUploadError?.(file, error);
|
|
1558
|
+
const upload = {
|
|
1559
|
+
status: "error",
|
|
1560
|
+
blobUrl,
|
|
1561
|
+
removalTimer: null
|
|
1562
|
+
};
|
|
1563
|
+
if (view.isDestroyed) {
|
|
1564
|
+
revokeBlobUrl(upload);
|
|
1565
|
+
return;
|
|
1566
|
+
}
|
|
1567
|
+
getSettledUploads(view).set(uploadId, upload);
|
|
1568
|
+
const found = findUploadNode(view.state.doc, uploadId);
|
|
1569
|
+
if (!found) {
|
|
1570
|
+
revokeBlobUrl(upload);
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1573
|
+
view.dispatch(settleUploadNode(view.state.tr, found.pos, found.node, upload).setMeta("addToHistory", false));
|
|
1574
|
+
scheduleFailedUploadRemoval(view, uploadId, upload);
|
|
1575
|
+
});
|
|
1576
|
+
}
|
|
1577
|
+
function getImageFiles(files) {
|
|
1578
|
+
return Array.from(files ?? []).filter((file) => file.type.startsWith("image/"));
|
|
1579
|
+
}
|
|
1580
|
+
function insertImageFilesWithUpload(view, files, pos, options) {
|
|
1581
|
+
if (files.length === 0) return;
|
|
1582
|
+
const { schema } = view.state;
|
|
1583
|
+
if (!schema.nodes.image?.spec.attrs?.uploadId) {
|
|
1584
|
+
warnMissingUploadExtension();
|
|
1585
|
+
return;
|
|
1586
|
+
}
|
|
1587
|
+
const uploads = files.map((file) => ({
|
|
1588
|
+
file,
|
|
1589
|
+
blobUrl: URL.createObjectURL(file),
|
|
1590
|
+
uploadId: createUploadId()
|
|
1591
|
+
}));
|
|
1592
|
+
view.dispatch(view.state.tr.insert(pos, uploads.map(({ blobUrl, uploadId }) => schema.nodes.image.create({
|
|
1593
|
+
src: blobUrl,
|
|
1594
|
+
uploading: true,
|
|
1595
|
+
uploadError: false,
|
|
1596
|
+
uploadId
|
|
1597
|
+
}))));
|
|
1598
|
+
uploads.forEach(({ file, blobUrl, uploadId }) => {
|
|
1599
|
+
startUpload(view, file, blobUrl, uploadId, options);
|
|
1600
|
+
});
|
|
1601
|
+
}
|
|
1602
|
+
function insertImageWithUpload(view, file, pos, options) {
|
|
1603
|
+
insertImageFilesWithUpload(view, [file], pos, options);
|
|
1604
|
+
}
|
|
1605
|
+
function isDataUrl(src) {
|
|
1606
|
+
return /^data:/i.test(src);
|
|
1607
|
+
}
|
|
1608
|
+
function getExtensionFromMimeType(type) {
|
|
1609
|
+
return (type.split("/")[1] ?? "").split("+")[0] || "bin";
|
|
1610
|
+
}
|
|
1611
|
+
function dataUrlToFile(src) {
|
|
1612
|
+
const separator = src.indexOf(",");
|
|
1613
|
+
if (separator === -1) throw new Error("Invalid data URL");
|
|
1614
|
+
const header = src.slice(5, separator);
|
|
1615
|
+
const data = src.slice(separator + 1);
|
|
1616
|
+
const [type = "", ...params] = header.split(";");
|
|
1617
|
+
const mimeType = type || "application/octet-stream";
|
|
1618
|
+
const bytes = params.includes("base64") ? Uint8Array.from(atob(data), (char) => char.charCodeAt(0)) : new TextEncoder().encode(decodeURIComponent(data));
|
|
1619
|
+
return new File([bytes], `pasted-image.${getExtensionFromMimeType(mimeType)}`, { type: mimeType });
|
|
1620
|
+
}
|
|
1621
|
+
function getRemoteFileName(src, type) {
|
|
1622
|
+
let name = "";
|
|
1623
|
+
try {
|
|
1624
|
+
name = decodeURIComponent(new URL(src, window.location.href).pathname.split("/").pop() ?? "");
|
|
1625
|
+
} catch {
|
|
1626
|
+
name = "";
|
|
1627
|
+
}
|
|
1628
|
+
if (!name) return `pasted-image.${getExtensionFromMimeType(type)}`;
|
|
1629
|
+
return name.includes(".") ? name : `${name}.${getExtensionFromMimeType(type)}`;
|
|
1630
|
+
}
|
|
1631
|
+
async function remoteUrlToFile(src) {
|
|
1632
|
+
const response = await fetch(src);
|
|
1633
|
+
if (!response.ok) throw new Error(`Failed to fetch image: ${response.status}`);
|
|
1634
|
+
const blob = await response.blob();
|
|
1635
|
+
const type = blob.type || "application/octet-stream";
|
|
1636
|
+
return new File([blob], getRemoteFileName(src, type), { type });
|
|
1637
|
+
}
|
|
1638
|
+
async function pastedImageToFile(src) {
|
|
1639
|
+
return isDataUrl(src) ? dataUrlToFile(src) : remoteUrlToFile(src);
|
|
1640
|
+
}
|
|
1641
|
+
function restoreImageNode(view, uploadId, src) {
|
|
1642
|
+
if (view.isDestroyed) return;
|
|
1643
|
+
const upload = {
|
|
1644
|
+
status: "success",
|
|
1645
|
+
attrs: { src }
|
|
1646
|
+
};
|
|
1647
|
+
const found = findUploadNode(view.state.doc, uploadId);
|
|
1648
|
+
if (!found) {
|
|
1649
|
+
getSettledUploads(view).set(uploadId, upload);
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1652
|
+
view.dispatch(settleUploadNode(view.state.tr, found.pos, found.node, upload).setMeta("addToHistory", false));
|
|
1653
|
+
}
|
|
1654
|
+
function shouldUploadPastedImage(node, options) {
|
|
1655
|
+
if (node.type.name !== "image" || node.attrs.uploadId || typeof node.attrs.src !== "string") return false;
|
|
1656
|
+
return options.shouldUploadPastedImage ? options.shouldUploadPastedImage(node.attrs.src) : isDataUrl(node.attrs.src);
|
|
1657
|
+
}
|
|
1658
|
+
function mapPastedSlice(slice, options) {
|
|
1659
|
+
const pending = [];
|
|
1660
|
+
const mapFragment = (fragment) => {
|
|
1661
|
+
const nodes = [];
|
|
1662
|
+
fragment.forEach((node) => {
|
|
1663
|
+
if (shouldUploadPastedImage(node, options)) {
|
|
1664
|
+
const uploadId = createUploadId();
|
|
1665
|
+
pending.push({
|
|
1666
|
+
uploadId,
|
|
1667
|
+
src: node.attrs.src
|
|
1668
|
+
});
|
|
1669
|
+
nodes.push(node.type.create({
|
|
1670
|
+
...node.attrs,
|
|
1671
|
+
uploading: true,
|
|
1672
|
+
uploadError: false,
|
|
1673
|
+
uploadId
|
|
1674
|
+
}, null, node.marks));
|
|
1675
|
+
return;
|
|
1676
|
+
}
|
|
1677
|
+
nodes.push(node.content.size > 0 ? node.copy(mapFragment(node.content)) : node);
|
|
1678
|
+
});
|
|
1679
|
+
return Fragment$1.fromArray(nodes);
|
|
1680
|
+
};
|
|
1681
|
+
const content = mapFragment(slice.content);
|
|
1682
|
+
return {
|
|
1683
|
+
slice: pending.length > 0 ? new Slice(content, slice.openStart, slice.openEnd) : slice,
|
|
1684
|
+
pending
|
|
1685
|
+
};
|
|
1686
|
+
}
|
|
1687
|
+
function stripImages(slice) {
|
|
1688
|
+
const stripFragment = (fragment) => {
|
|
1689
|
+
const nodes = [];
|
|
1690
|
+
fragment.forEach((node) => {
|
|
1691
|
+
if (node.type.name === "image") return;
|
|
1692
|
+
nodes.push(node.content.size > 0 ? node.copy(stripFragment(node.content)) : node);
|
|
1693
|
+
});
|
|
1694
|
+
return Fragment$1.fromArray(nodes);
|
|
1695
|
+
};
|
|
1696
|
+
return new Slice(stripFragment(slice.content), slice.openStart, slice.openEnd);
|
|
1697
|
+
}
|
|
1698
|
+
function startPastedUploads(view, pending, options) {
|
|
1699
|
+
pending.forEach(({ uploadId, src }) => {
|
|
1700
|
+
pastedImageToFile(src).then((file) => startUpload(view, file, null, uploadId, options), () => restoreImageNode(view, uploadId, src));
|
|
1701
|
+
});
|
|
1702
|
+
}
|
|
1703
|
+
function getBlockInsertPos(state) {
|
|
1704
|
+
const { $from } = state.selection;
|
|
1705
|
+
if (state.schema.nodes.image?.isInline || !$from.parent.isTextblock) return $from.pos;
|
|
1706
|
+
if ($from.parentOffset === 0) return $from.before();
|
|
1707
|
+
if ($from.parentOffset === $from.parent.content.size) return $from.after();
|
|
1708
|
+
return $from.pos;
|
|
1709
|
+
}
|
|
1710
|
+
function hasUploadImageNode(view) {
|
|
1711
|
+
return Boolean(view.state.schema.nodes.image?.spec.attrs?.uploadId);
|
|
1712
|
+
}
|
|
1713
|
+
function getUploadImageExtensionOptions(editor) {
|
|
1714
|
+
const extension = editor?.extensionManager?.extensions?.find((item) => item.name === "image");
|
|
1715
|
+
return {
|
|
1716
|
+
onImageUpload: extension?.options?.onImageUpload,
|
|
1717
|
+
onImageUploadError: extension?.options?.onImageUploadError
|
|
1718
|
+
};
|
|
1719
|
+
}
|
|
1720
|
+
function getUploadImageExtension(TipTapImage, options) {
|
|
1721
|
+
return TipTapImage.extend({
|
|
1722
|
+
addOptions() {
|
|
1723
|
+
return {
|
|
1724
|
+
...this.parent?.(),
|
|
1725
|
+
onImageUpload: options.onImageUpload,
|
|
1726
|
+
onImageUploadError: options.onImageUploadError,
|
|
1727
|
+
shouldUploadPastedImage: options.shouldUploadPastedImage
|
|
1728
|
+
};
|
|
1729
|
+
},
|
|
1730
|
+
parseHTML() {
|
|
1731
|
+
return [{ tag: "img[src]" }];
|
|
1732
|
+
},
|
|
1733
|
+
addAttributes() {
|
|
1734
|
+
return {
|
|
1735
|
+
...this.parent?.(),
|
|
1736
|
+
uploading: {
|
|
1737
|
+
default: false,
|
|
1738
|
+
renderHTML: (attrs) => attrs.uploading ? { "data-uploading": "" } : {}
|
|
1739
|
+
},
|
|
1740
|
+
uploadError: {
|
|
1741
|
+
default: false,
|
|
1742
|
+
renderHTML: (attrs) => attrs.uploadError ? { "data-upload-error": "" } : {}
|
|
1743
|
+
},
|
|
1744
|
+
uploadId: {
|
|
1745
|
+
default: null,
|
|
1746
|
+
renderHTML: () => ({})
|
|
1747
|
+
}
|
|
1748
|
+
};
|
|
1749
|
+
},
|
|
1750
|
+
addNodeView() {
|
|
1751
|
+
const parentNodeView = this.parent?.();
|
|
1752
|
+
return (props) => {
|
|
1753
|
+
const { node, HTMLAttributes } = props;
|
|
1754
|
+
if (!node.attrs.uploading && !node.attrs.uploadError) {
|
|
1755
|
+
if (parentNodeView) return parentNodeView(props);
|
|
1756
|
+
const img = document.createElement("img");
|
|
1757
|
+
Object.entries(HTMLAttributes).forEach(([key, value]) => {
|
|
1758
|
+
if (value !== null && value !== void 0) img.setAttribute(key, String(value));
|
|
1759
|
+
});
|
|
1760
|
+
img.src = node.attrs.src;
|
|
1761
|
+
return { dom: img };
|
|
1762
|
+
}
|
|
1763
|
+
const isInline = this.options?.inline;
|
|
1764
|
+
const wrapper = document.createElement(isInline ? "span" : "div");
|
|
1765
|
+
wrapper.classList.add("mantine-upload-image-wrapper");
|
|
1766
|
+
if (node.attrs.uploading) wrapper.setAttribute("data-uploading", "");
|
|
1767
|
+
if (node.attrs.uploadError) wrapper.setAttribute("data-upload-error", "");
|
|
1768
|
+
const img = document.createElement("img");
|
|
1769
|
+
Object.entries(HTMLAttributes).forEach(([key, value]) => {
|
|
1770
|
+
if (value !== null && value !== void 0 && key !== "data-uploading" && key !== "data-upload-error") img.setAttribute(key, String(value));
|
|
1771
|
+
});
|
|
1772
|
+
img.src = node.attrs.src;
|
|
1773
|
+
if (node.attrs.alt) img.alt = node.attrs.alt;
|
|
1774
|
+
if (node.attrs.title) img.title = node.attrs.title;
|
|
1775
|
+
wrapper.appendChild(img);
|
|
1776
|
+
return { dom: wrapper };
|
|
1777
|
+
};
|
|
1778
|
+
},
|
|
1779
|
+
addProseMirrorPlugins() {
|
|
1780
|
+
const uploadOptions = {
|
|
1781
|
+
onImageUpload: this.options.onImageUpload,
|
|
1782
|
+
onImageUploadError: this.options.onImageUploadError,
|
|
1783
|
+
shouldUploadPastedImage: this.options.shouldUploadPastedImage
|
|
1784
|
+
};
|
|
1785
|
+
let editorView = null;
|
|
1786
|
+
return [new Plugin({
|
|
1787
|
+
key: uploadImagePluginKey,
|
|
1788
|
+
view(view) {
|
|
1789
|
+
editorView = view;
|
|
1790
|
+
return { destroy: () => {
|
|
1791
|
+
editorView = null;
|
|
1792
|
+
} };
|
|
1793
|
+
},
|
|
1794
|
+
appendTransaction(transactions, _oldState, newState) {
|
|
1795
|
+
return repairSettledUploads(editorView, transactions, newState);
|
|
1796
|
+
},
|
|
1797
|
+
props: {
|
|
1798
|
+
handleDrop(view, event, slice, moved) {
|
|
1799
|
+
if (!event.dataTransfer || !uploadOptions.onImageUpload) return false;
|
|
1800
|
+
const files = getImageFiles(event.dataTransfer.files);
|
|
1801
|
+
const pos = view.posAtCoords({
|
|
1802
|
+
left: event.clientX,
|
|
1803
|
+
top: event.clientY
|
|
1804
|
+
});
|
|
1805
|
+
if (files.length > 0) {
|
|
1806
|
+
event.preventDefault();
|
|
1807
|
+
if (pos) insertImageFilesWithUpload(view, files, pos.pos, uploadOptions);
|
|
1808
|
+
return true;
|
|
1809
|
+
}
|
|
1810
|
+
if (moved || !pos || !hasUploadImageNode(view)) return false;
|
|
1811
|
+
const mapped = mapPastedSlice(slice, uploadOptions);
|
|
1812
|
+
if (mapped.pending.length === 0) return false;
|
|
1813
|
+
event.preventDefault();
|
|
1814
|
+
const insertPos = dropPoint(view.state.doc, pos.pos, mapped.slice) ?? pos.pos;
|
|
1815
|
+
view.dispatch(view.state.tr.replaceRange(insertPos, insertPos, mapped.slice).setMeta("uiEvent", "drop"));
|
|
1816
|
+
startPastedUploads(view, mapped.pending, uploadOptions);
|
|
1817
|
+
return true;
|
|
1818
|
+
},
|
|
1819
|
+
handlePaste(view, event, slice) {
|
|
1820
|
+
if (!event.clipboardData || !uploadOptions.onImageUpload) return false;
|
|
1821
|
+
const files = getImageFiles(event.clipboardData.files);
|
|
1822
|
+
if (files.length > 0) {
|
|
1823
|
+
event.preventDefault();
|
|
1824
|
+
const text = stripImages(slice);
|
|
1825
|
+
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"));
|
|
1826
|
+
insertImageFilesWithUpload(view, files, getBlockInsertPos(view.state), uploadOptions);
|
|
1827
|
+
return true;
|
|
1828
|
+
}
|
|
1829
|
+
if (!hasUploadImageNode(view)) return false;
|
|
1830
|
+
const mapped = mapPastedSlice(slice, uploadOptions);
|
|
1831
|
+
if (mapped.pending.length === 0) return false;
|
|
1832
|
+
event.preventDefault();
|
|
1833
|
+
view.dispatch(view.state.tr.replaceSelection(mapped.slice).scrollIntoView().setMeta("paste", true).setMeta("uiEvent", "paste"));
|
|
1834
|
+
startPastedUploads(view, mapped.pending, uploadOptions);
|
|
1835
|
+
return true;
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
})];
|
|
1839
|
+
}
|
|
1840
|
+
});
|
|
1841
|
+
}
|
|
1842
|
+
//#endregion
|
|
1843
|
+
//#region src/RichTextEditorControl/RichTextEditorImageUploadControl.vue?vue&type=script&setup=true&lang.ts
|
|
1844
|
+
var _hoisted_1 = ["accept"];
|
|
1845
|
+
//#endregion
|
|
1846
|
+
//#region src/RichTextEditorControl/RichTextEditorImageUploadControl.ts
|
|
1847
|
+
var RichTextEditorImageUploadControl = /* @__PURE__ */ defineComponent({
|
|
1848
|
+
name: "RichTextEditorImageUploadControl",
|
|
1849
|
+
inheritAttrs: false,
|
|
1850
|
+
__name: "RichTextEditorImageUploadControl",
|
|
1851
|
+
props: {
|
|
1852
|
+
accept: { default: "image/*" },
|
|
1853
|
+
disabled: {
|
|
1854
|
+
type: Boolean,
|
|
1855
|
+
default: false
|
|
1856
|
+
},
|
|
1857
|
+
icon: {}
|
|
1858
|
+
},
|
|
1859
|
+
setup(__props) {
|
|
1860
|
+
const props = __props;
|
|
1861
|
+
const attrs = useAttrs();
|
|
1862
|
+
const ctx = useRichTextEditorContext();
|
|
1863
|
+
const input = ref(null);
|
|
1864
|
+
const usable = computed(() => !!ctx.editor && !ctx.editor.isDestroyed && ctx.editor.isEditable);
|
|
1865
|
+
const disabled = computed(() => !usable.value || props.disabled);
|
|
1866
|
+
function open(event) {
|
|
1867
|
+
const handler = attrs.onClick;
|
|
1868
|
+
if (Array.isArray(handler)) handler.forEach((item) => item?.(event));
|
|
1869
|
+
else handler?.(event);
|
|
1870
|
+
if (!event.defaultPrevented) input.value?.click();
|
|
1871
|
+
}
|
|
1872
|
+
function change(event) {
|
|
1873
|
+
const target = event.currentTarget;
|
|
1874
|
+
const files = getImageFiles(target.files);
|
|
1875
|
+
if (!files.length || !ctx.editor || !usable.value) return;
|
|
1876
|
+
const extension = getUploadImageExtensionOptions(ctx.editor);
|
|
1877
|
+
const onImageUpload = ctx.onImageUpload ?? extension.onImageUpload;
|
|
1878
|
+
if (!onImageUpload) return;
|
|
1879
|
+
const { view } = ctx.editor;
|
|
1880
|
+
insertImageFilesWithUpload(view, files, view.state.selection.from, {
|
|
1881
|
+
onImageUpload,
|
|
1882
|
+
onImageUploadError: ctx.onImageUploadError ?? extension.onImageUploadError
|
|
1883
|
+
});
|
|
1884
|
+
target.value = "";
|
|
1885
|
+
}
|
|
1886
|
+
return (_ctx, _cache) => {
|
|
1887
|
+
return openBlock(), createElementBlock(Fragment, null, [createVNode(RichTextEditorControlBase_default, mergeProps(unref(attrs), {
|
|
1888
|
+
icon: props.icon || unref(IconPhoto),
|
|
1889
|
+
disabled: disabled.value,
|
|
1890
|
+
interactive: true,
|
|
1891
|
+
variant: unref(ctx).variant,
|
|
1892
|
+
"aria-label": unref(ctx).labels.imageUploadControlLabel,
|
|
1893
|
+
title: unref(ctx).labels.imageUploadControlLabel,
|
|
1894
|
+
onClick: open
|
|
1895
|
+
}), null, 16, [
|
|
1896
|
+
"icon",
|
|
1897
|
+
"disabled",
|
|
1898
|
+
"variant",
|
|
1899
|
+
"aria-label",
|
|
1900
|
+
"title"
|
|
1901
|
+
]), createElementVNode("input", {
|
|
1902
|
+
ref_key: "input",
|
|
1903
|
+
ref: input,
|
|
1904
|
+
type: "file",
|
|
1905
|
+
accept: props.accept,
|
|
1906
|
+
multiple: "",
|
|
1907
|
+
hidden: "",
|
|
1908
|
+
onChange: change
|
|
1909
|
+
}, null, 40, _hoisted_1)], 64);
|
|
1910
|
+
};
|
|
1911
|
+
}
|
|
1912
|
+
});
|
|
1913
|
+
//#endregion
|
|
1430
1914
|
//#region src/RichTextEditorControl/controls.ts
|
|
1431
1915
|
var BoldControl = createControl({
|
|
1432
1916
|
label: "boldControlLabel",
|
|
@@ -1864,7 +2348,8 @@ var RichTextEditor = factory(RichTextEditor_default, {
|
|
|
1864
2348
|
TableMergeCells: TableMergeCellsControl,
|
|
1865
2349
|
TableSplitCell: TableSplitCellControl,
|
|
1866
2350
|
Details: RichTextEditorDetailsControl_default,
|
|
1867
|
-
InvisibleCharacters: RichTextEditorInvisibleCharactersControl_default
|
|
2351
|
+
InvisibleCharacters: RichTextEditorInvisibleCharactersControl_default,
|
|
2352
|
+
ImageUpload: RichTextEditorImageUploadControl
|
|
1868
2353
|
});
|
|
1869
2354
|
//#endregion
|
|
1870
2355
|
//#region src/extensions/Link.ts
|
|
@@ -1885,6 +2370,6 @@ var getTaskListExtension = (TipTapTaskList) => TipTapTaskList.extend({ addKeyboa
|
|
|
1885
2370
|
}
|
|
1886
2371
|
}) }).configure({ HTMLAttributes: { class: `${RichTextEditor_module_default.taskList} mantine-RichTextEditor-taskList` } });
|
|
1887
2372
|
//#endregion
|
|
1888
|
-
export { AlignCenterControl, AlignJustifyControl, AlignLeftControl, AlignRightControl, BlockquoteControl, BoldControl, BulletListControl, ClearFormattingControl, CodeBlockControl, CodeControl, DEFAULT_LABELS, H1Control, H2Control, H3Control, H4Control, H5Control, H6Control, HighlightControl, HrControl, ItalicControl, Link, OrderedListControl, RedoControl, RichTextEditor, RichTextEditorColorControl, RichTextEditorColorPickerControl, RichTextEditorControl, RichTextEditorControlBase, RichTextEditorDetailsControl_default as RichTextEditorDetailsControl, RichTextEditorInvisibleCharactersControl_default as RichTextEditorInvisibleCharactersControl, RichTextEditorLinkControl, RichTextEditorSourceCodeControl, RichTextEditorTableInsertControl_default as RichTextEditorTableInsertControl, StrikeThroughControl, SubscriptControl, SuperscriptControl, TableColumnAfterControl, TableColumnBeforeControl, TableColumnDeleteControl, TableDeleteControl, TableMergeCellsControl, TableRowAfterControl, TableRowBeforeControl, TableRowDeleteControl, TableSplitCellControl, TableToggleHeaderColumnControl, TableToggleHeaderRowControl, TaskListControl, TaskListLiftControl, TaskListSinkControl, UnderlineControl, UndoControl, UnlinkControl, UnsetColorControl, createControl, getTaskListExtension, useRichTextEditorContext };
|
|
2373
|
+
export { AlignCenterControl, AlignJustifyControl, AlignLeftControl, AlignRightControl, BlockquoteControl, BoldControl, BulletListControl, ClearFormattingControl, CodeBlockControl, CodeControl, DEFAULT_LABELS, H1Control, H2Control, H3Control, H4Control, H5Control, H6Control, HighlightControl, HrControl, ItalicControl, Link, OrderedListControl, RedoControl, RichTextEditor, RichTextEditorColorControl, RichTextEditorColorPickerControl, RichTextEditorControl, RichTextEditorControlBase, RichTextEditorDetailsControl_default as RichTextEditorDetailsControl, RichTextEditorImageUploadControl, RichTextEditorInvisibleCharactersControl_default as RichTextEditorInvisibleCharactersControl, RichTextEditorLinkControl, RichTextEditorSourceCodeControl, RichTextEditorTableInsertControl_default as RichTextEditorTableInsertControl, StrikeThroughControl, SubscriptControl, SuperscriptControl, TableColumnAfterControl, TableColumnBeforeControl, TableColumnDeleteControl, TableDeleteControl, TableMergeCellsControl, TableRowAfterControl, TableRowBeforeControl, TableRowDeleteControl, TableSplitCellControl, TableToggleHeaderColumnControl, TableToggleHeaderRowControl, TaskListControl, TaskListLiftControl, TaskListSinkControl, UnderlineControl, UndoControl, UnlinkControl, UnsetColorControl, createControl, getImageFiles, getTaskListExtension, getUploadImageExtension, getUploadImageExtensionOptions, insertImageFilesWithUpload, insertImageWithUpload, useRichTextEditorContext };
|
|
1889
2374
|
|
|
1890
2375
|
//# sourceMappingURL=index.mjs.map
|