@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/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: "_root_1avnr_1",
75
- Typography: "_Typography_1avnr_19",
76
- content: "_content_1avnr_47",
77
- linkEditorDropdown: "_linkEditorDropdown_1avnr_292",
78
- tableInsertDropdown: "_tableInsertDropdown_1avnr_296",
79
- tableInsertGrid: "_tableInsertGrid_1avnr_301",
80
- tableInsertCell: "_tableInsertCell_1avnr_307",
81
- tableInsertLabel: "_tableInsertLabel_1avnr_324",
82
- control: "_control_1avnr_331",
83
- controlIcon: "_controlIcon_1avnr_406",
84
- controlsGroup: "_controlsGroup_1avnr_411",
85
- linkEditor: "_linkEditor_1avnr_292",
86
- linkEditorInput: "_linkEditorInput_1avnr_440",
87
- linkEditorExternalControl: "_linkEditorExternalControl_1avnr_446",
88
- linkEditorSave: "_linkEditorSave_1avnr_476",
89
- toolbar: "_toolbar_1avnr_481",
90
- taskList: "_taskList_1avnr_515"
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
- className: attrs.class,
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