@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 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: "_root_1avnr_1",
99
- Typography: "_Typography_1avnr_19",
100
- content: "_content_1avnr_47",
101
- linkEditorDropdown: "_linkEditorDropdown_1avnr_292",
102
- tableInsertDropdown: "_tableInsertDropdown_1avnr_296",
103
- tableInsertGrid: "_tableInsertGrid_1avnr_301",
104
- tableInsertCell: "_tableInsertCell_1avnr_307",
105
- tableInsertLabel: "_tableInsertLabel_1avnr_324",
106
- control: "_control_1avnr_331",
107
- controlIcon: "_controlIcon_1avnr_406",
108
- controlsGroup: "_controlsGroup_1avnr_411",
109
- linkEditor: "_linkEditor_1avnr_292",
110
- linkEditorInput: "_linkEditorInput_1avnr_440",
111
- linkEditorExternalControl: "_linkEditorExternalControl_1avnr_446",
112
- linkEditorSave: "_linkEditorSave_1avnr_476",
113
- toolbar: "_toolbar_1avnr_481",
114
- taskList: "_taskList_1avnr_515"
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
- className: attrs.class,
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