@bendyline/squisq-editor-react 2.13.3 → 2.13.5
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/dist/{chunk-D77UNB6T.js → chunk-WEEHXZUY.js} +212 -97
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/shell/index.js +1 -1
- package/dist/styles/index.css +16 -1
- package/package.json +6 -6
|
@@ -6521,6 +6521,7 @@ import {
|
|
|
6521
6521
|
deriveTemplateInputs,
|
|
6522
6522
|
extractBodyPlainText,
|
|
6523
6523
|
extractEmbeddedVideos,
|
|
6524
|
+
extractHostedVideos,
|
|
6524
6525
|
extractBlockquoteText,
|
|
6525
6526
|
extractImages,
|
|
6526
6527
|
extractListItems,
|
|
@@ -6658,11 +6659,7 @@ function buildTemplatePreviewInputs(templateName, block, headingText2, bodyText,
|
|
|
6658
6659
|
}
|
|
6659
6660
|
case "leftFeature":
|
|
6660
6661
|
case "rightFeature": {
|
|
6661
|
-
const hasFeature = hasText(provided.imageSrc) ||
|
|
6662
|
-
(node2) => node2.type === "code" && isPictureFence(node2) && !!node2.value.trim()
|
|
6663
|
-
) || ["diagram", "tree", "timeline"].some(
|
|
6664
|
-
(template) => !!deriveTemplateInputs(template, headingText2, contents, { placeholders: false })
|
|
6665
|
-
);
|
|
6662
|
+
const hasFeature = hasText(provided.imageSrc) || hasVisualFeature(contents, headingText2);
|
|
6666
6663
|
return {
|
|
6667
6664
|
inputs: hasFeature ? deriveTemplateInputs(templateName, headingText2, contents, { placeholders: false }) ?? {
|
|
6668
6665
|
title: headingText2,
|
|
@@ -6671,6 +6668,15 @@ function buildTemplatePreviewInputs(templateName, block, headingText2, bodyText,
|
|
|
6671
6668
|
...hasFeature ? {} : { warning: "No visual feature found in this block" }
|
|
6672
6669
|
};
|
|
6673
6670
|
}
|
|
6671
|
+
case "topFeature":
|
|
6672
|
+
case "bottomFeature":
|
|
6673
|
+
case "mediaOnly": {
|
|
6674
|
+
const hasFeature = hasVisualFeature(contents, headingText2);
|
|
6675
|
+
return {
|
|
6676
|
+
inputs: hasFeature ? { title: headingText2 } : null,
|
|
6677
|
+
...hasFeature ? {} : { warning: "No image or video found in this block" }
|
|
6678
|
+
};
|
|
6679
|
+
}
|
|
6674
6680
|
case "imageWithCaption":
|
|
6675
6681
|
case "photoGrid": {
|
|
6676
6682
|
const imageCount = extractImages(contents, templateName === "photoGrid" ? 2 : 1).length;
|
|
@@ -6742,6 +6748,13 @@ function buildTemplatePreviewInputs(templateName, block, headingText2, bodyText,
|
|
|
6742
6748
|
};
|
|
6743
6749
|
}
|
|
6744
6750
|
}
|
|
6751
|
+
function hasVisualFeature(contents, headingText2) {
|
|
6752
|
+
return extractImages(contents, 1).length > 0 || extractEmbeddedVideos(contents, 1).length > 0 || extractHostedVideos(contents, 1).length > 0 || (contents ?? []).some(
|
|
6753
|
+
(node2) => node2.type === "code" && isPictureFence(node2) && !!node2.value.trim()
|
|
6754
|
+
) || ["diagram", "tree", "timeline"].some(
|
|
6755
|
+
(template) => !!deriveTemplateInputs(template, headingText2, contents, { placeholders: false })
|
|
6756
|
+
);
|
|
6757
|
+
}
|
|
6745
6758
|
function derivePullQuoteInputs(block, headingText2, bodyText) {
|
|
6746
6759
|
const image = extractImages(block.contents, 1)[0];
|
|
6747
6760
|
const text = extractBlockquoteText(block.contents) || bodyText || headingText2;
|
|
@@ -7087,6 +7100,39 @@ var TEMPLATE_ENTRIES = [
|
|
|
7087
7100
|
/* @__PURE__ */ jsx18("rect", { x: 3, y: 27, width: 16, height: 2.5, rx: 1, fill: F1, opacity: 0.6 })
|
|
7088
7101
|
] })
|
|
7089
7102
|
},
|
|
7103
|
+
{
|
|
7104
|
+
name: "topFeature",
|
|
7105
|
+
label: "Top Feature",
|
|
7106
|
+
description: "Image or video across the full width at the top, title and body text below.",
|
|
7107
|
+
icon: /* @__PURE__ */ jsxs14(TemplateIcon, { children: [
|
|
7108
|
+
/* @__PURE__ */ jsx18("rect", { x: 3, y: 3, width: 50, height: 19, rx: 2, fill: F2, opacity: 0.55 }),
|
|
7109
|
+
/* @__PURE__ */ jsx18("polygon", { points: "25,8.5 25,16.5 32,12.5", fill: "white", opacity: 0.5 }),
|
|
7110
|
+
/* @__PURE__ */ jsx18("rect", { x: 3, y: 25, width: 22, height: 4, rx: 1, fill: FA }),
|
|
7111
|
+
/* @__PURE__ */ jsx18("rect", { x: 3, y: 31, width: 48, height: 2.5, rx: 1, fill: F1 }),
|
|
7112
|
+
/* @__PURE__ */ jsx18("rect", { x: 3, y: 35.5, width: 34, height: 2.5, rx: 1, fill: F1, opacity: 0.75 })
|
|
7113
|
+
] })
|
|
7114
|
+
},
|
|
7115
|
+
{
|
|
7116
|
+
name: "bottomFeature",
|
|
7117
|
+
label: "Bottom Feature",
|
|
7118
|
+
description: "Title and body text at the top, image or video across the full width below.",
|
|
7119
|
+
icon: /* @__PURE__ */ jsxs14(TemplateIcon, { children: [
|
|
7120
|
+
/* @__PURE__ */ jsx18("rect", { x: 3, y: 3, width: 22, height: 4, rx: 1, fill: FA }),
|
|
7121
|
+
/* @__PURE__ */ jsx18("rect", { x: 3, y: 9, width: 48, height: 2.5, rx: 1, fill: F1 }),
|
|
7122
|
+
/* @__PURE__ */ jsx18("rect", { x: 3, y: 13.5, width: 34, height: 2.5, rx: 1, fill: F1, opacity: 0.75 }),
|
|
7123
|
+
/* @__PURE__ */ jsx18("rect", { x: 3, y: 18, width: 50, height: 19, rx: 2, fill: F2, opacity: 0.55 }),
|
|
7124
|
+
/* @__PURE__ */ jsx18("polygon", { points: "25,23.5 25,31.5 32,27.5", fill: "white", opacity: 0.5 })
|
|
7125
|
+
] })
|
|
7126
|
+
},
|
|
7127
|
+
{
|
|
7128
|
+
name: "mediaOnly",
|
|
7129
|
+
label: "Media Only",
|
|
7130
|
+
description: "The image or video fills the slide, with no title, text or caption.",
|
|
7131
|
+
icon: /* @__PURE__ */ jsxs14(TemplateIcon, { children: [
|
|
7132
|
+
/* @__PURE__ */ jsx18("rect", { x: 3, y: 3, width: 50, height: 34, rx: 2, fill: F2, opacity: 0.55 }),
|
|
7133
|
+
/* @__PURE__ */ jsx18("polygon", { points: "24,14 24,26 34,20", fill: "white", opacity: 0.5 })
|
|
7134
|
+
] })
|
|
7135
|
+
},
|
|
7090
7136
|
{
|
|
7091
7137
|
name: "map",
|
|
7092
7138
|
label: "Map",
|
|
@@ -8789,7 +8835,6 @@ var BUTTONS = [
|
|
|
8789
8835
|
var MEDIA_BUTTONS = BUTTONS.filter((b) => b.group === "media");
|
|
8790
8836
|
var FILE_STORING_BUTTONS = /* @__PURE__ */ new Set(["image", "file"]);
|
|
8791
8837
|
var CONVERT_BUTTONS = MEDIA_BUTTONS.filter((b) => b.id === "table" || b.id === "tasklist");
|
|
8792
|
-
var INSERT_MENU_WIDTH = 200;
|
|
8793
8838
|
var CODE_SNIPPET_MENU_WIDTH = 220;
|
|
8794
8839
|
var MERMAID_TYPE_MENU_WIDTH = 520;
|
|
8795
8840
|
var CHART_TYPE_MENU_WIDTH = 360;
|
|
@@ -20529,7 +20574,7 @@ function Toolbar({
|
|
|
20529
20574
|
const codeSnippetMenuRef = useRef36(null);
|
|
20530
20575
|
const mermaidTypeMenuRef = useRef36(null);
|
|
20531
20576
|
const chartTypeMenuRef = useRef36(null);
|
|
20532
|
-
const
|
|
20577
|
+
const insertMenuTriggerRef = useRef36(null);
|
|
20533
20578
|
const [insertMenuAnchor, setInsertMenuAnchor] = useState40(null);
|
|
20534
20579
|
const [codeSnippetMenuAnchor, setCodeSnippetMenuAnchor] = useState40(null);
|
|
20535
20580
|
const [mermaidTypeMenuAnchor, setMermaidTypeMenuAnchor] = useState40(null);
|
|
@@ -20539,15 +20584,8 @@ function Toolbar({
|
|
|
20539
20584
|
if (!btn) return;
|
|
20540
20585
|
insertMenuReturnFocusRef.current = trigger ? overflowTriggerRef.current : insertMenuButtonRef.current;
|
|
20541
20586
|
const rect = btn.getBoundingClientRect();
|
|
20542
|
-
|
|
20543
|
-
|
|
20544
|
-
const vw = window.innerWidth;
|
|
20545
|
-
let left = rect.left;
|
|
20546
|
-
if (left + INSERT_MENU_WIDTH + margin > vw) {
|
|
20547
|
-
left = Math.max(margin, rect.right - INSERT_MENU_WIDTH);
|
|
20548
|
-
}
|
|
20549
|
-
insertMenuTriggerRectRef.current = rect;
|
|
20550
|
-
setInsertMenuAnchor({ top: rect.bottom + gap, left, placement: "down" });
|
|
20587
|
+
insertMenuTriggerRef.current = { element: btn, rect };
|
|
20588
|
+
setInsertMenuAnchor({ top: rect.bottom + 4, left: rect.left, placement: "down" });
|
|
20551
20589
|
setCodeSnippetMenuAnchor(null);
|
|
20552
20590
|
setMermaidTypeMenuAnchor(null);
|
|
20553
20591
|
setChartTypeMenuAnchor(null);
|
|
@@ -20601,7 +20639,7 @@ function Toolbar({
|
|
|
20601
20639
|
setMermaidTypeMenuAnchor(null);
|
|
20602
20640
|
}, []);
|
|
20603
20641
|
const closeInsertMenu = useCallback32(() => {
|
|
20604
|
-
|
|
20642
|
+
insertMenuTriggerRef.current = null;
|
|
20605
20643
|
setInsertMenuAnchor(null);
|
|
20606
20644
|
setCodeSnippetMenuAnchor(null);
|
|
20607
20645
|
setMermaidTypeMenuAnchor(null);
|
|
@@ -20687,23 +20725,39 @@ function Toolbar({
|
|
|
20687
20725
|
useLayoutEffect9(() => {
|
|
20688
20726
|
if (!insertMenuOpen) return;
|
|
20689
20727
|
const menu = insertMenuRef.current;
|
|
20690
|
-
const
|
|
20691
|
-
if (!menu || !
|
|
20692
|
-
const
|
|
20693
|
-
|
|
20694
|
-
|
|
20695
|
-
|
|
20696
|
-
|
|
20697
|
-
|
|
20698
|
-
|
|
20699
|
-
|
|
20700
|
-
|
|
20701
|
-
|
|
20702
|
-
|
|
20703
|
-
|
|
20704
|
-
|
|
20705
|
-
|
|
20706
|
-
|
|
20728
|
+
const trigger = insertMenuTriggerRef.current;
|
|
20729
|
+
if (!menu || !trigger) return;
|
|
20730
|
+
const positionMenu = () => {
|
|
20731
|
+
const liveRect = trigger.element.getBoundingClientRect();
|
|
20732
|
+
const triggerRect = trigger.element.isConnected && liveRect.width > 0 ? liveRect : trigger.rect;
|
|
20733
|
+
const gap = 4;
|
|
20734
|
+
const margin = 8;
|
|
20735
|
+
const menuRect = menu.getBoundingClientRect();
|
|
20736
|
+
const rightEdge = window.innerWidth - margin;
|
|
20737
|
+
const preferredLeft = triggerRect.left + menuRect.width <= rightEdge ? triggerRect.left : triggerRect.right - menuRect.width;
|
|
20738
|
+
const left = Math.max(margin, Math.min(preferredLeft, rightEdge - menuRect.width));
|
|
20739
|
+
const spaceBelow = Math.max(0, window.innerHeight - triggerRect.bottom - gap - margin);
|
|
20740
|
+
const spaceAbove = Math.max(0, triggerRect.top - gap - margin);
|
|
20741
|
+
const naturalHeight = Math.max(menu.scrollHeight, menuRect.height);
|
|
20742
|
+
const placement = naturalHeight > spaceBelow && spaceAbove > spaceBelow ? "up" : "down";
|
|
20743
|
+
const maxHeight = placement === "up" ? spaceAbove : spaceBelow;
|
|
20744
|
+
const visibleHeight = Math.min(naturalHeight, maxHeight);
|
|
20745
|
+
const top = placement === "up" ? Math.max(margin, triggerRect.top - gap - visibleHeight) : triggerRect.bottom + gap;
|
|
20746
|
+
setInsertMenuAnchor((current) => {
|
|
20747
|
+
if (!current || current.left === left && current.top === top && current.maxHeight === maxHeight && current.placement === placement) {
|
|
20748
|
+
return current;
|
|
20749
|
+
}
|
|
20750
|
+
return { ...current, left, top, maxHeight, placement };
|
|
20751
|
+
});
|
|
20752
|
+
};
|
|
20753
|
+
positionMenu();
|
|
20754
|
+
const observer = new ResizeObserver(positionMenu);
|
|
20755
|
+
observer.observe(menu);
|
|
20756
|
+
window.addEventListener("resize", positionMenu);
|
|
20757
|
+
return () => {
|
|
20758
|
+
observer.disconnect();
|
|
20759
|
+
window.removeEventListener("resize", positionMenu);
|
|
20760
|
+
};
|
|
20707
20761
|
}, [insertMenuOpen]);
|
|
20708
20762
|
const [, forceUpdate] = useReducer2((c) => c + 1, 0);
|
|
20709
20763
|
useEffect31(() => {
|
|
@@ -35324,6 +35378,35 @@ function looksLikeMarkdown(text) {
|
|
|
35324
35378
|
}
|
|
35325
35379
|
|
|
35326
35380
|
// src/wysiwygImageUpload.ts
|
|
35381
|
+
function isPlayableMediaFile(file) {
|
|
35382
|
+
const kind = squisqMediaKind(mediaMimeType(file));
|
|
35383
|
+
return kind === "video" || kind === "audio";
|
|
35384
|
+
}
|
|
35385
|
+
async function uploadAndInsertMediaPlayers(view, files, mediaProvider, onMediaUploaded) {
|
|
35386
|
+
const paths = await processMediaFiles(files, mediaProvider);
|
|
35387
|
+
if (!paths.some(Boolean)) return;
|
|
35388
|
+
onMediaUploaded?.();
|
|
35389
|
+
const { schema } = view.state;
|
|
35390
|
+
const players = [];
|
|
35391
|
+
for (const [index2, path] of paths.entries()) {
|
|
35392
|
+
if (!path) continue;
|
|
35393
|
+
const spec = squisqMediaNode(fileReference(path, files[index2].name, files[index2].type));
|
|
35394
|
+
const nodeType = spec ? schema.nodes[spec.type] : void 0;
|
|
35395
|
+
if (spec && nodeType) players.push(nodeType.create(spec.attrs));
|
|
35396
|
+
}
|
|
35397
|
+
if (players.length > 0) insertTopLevelNodes(view, players);
|
|
35398
|
+
}
|
|
35399
|
+
function insertTopLevelNodes(view, nodes) {
|
|
35400
|
+
const { state } = view;
|
|
35401
|
+
const { $from } = state.selection;
|
|
35402
|
+
if ($from.depth < 1) {
|
|
35403
|
+
view.dispatch(state.tr.insert(state.doc.content.size, nodes).scrollIntoView());
|
|
35404
|
+
return;
|
|
35405
|
+
}
|
|
35406
|
+
const block = $from.node(1);
|
|
35407
|
+
const tr = block.type.name === "paragraph" && block.content.size === 0 ? state.tr.replaceWith($from.before(1), $from.after(1), nodes) : state.tr.insert($from.after(1), nodes);
|
|
35408
|
+
view.dispatch(tr.scrollIntoView());
|
|
35409
|
+
}
|
|
35327
35410
|
async function uploadAndInsertImages(view, files, mediaProvider, onMediaUploaded) {
|
|
35328
35411
|
const placeholders = files.map(() => `squisq-upload:${uniquePasteToken()}`);
|
|
35329
35412
|
const imageType = view.state.schema.nodes.image;
|
|
@@ -35900,47 +35983,23 @@ function WysiwygEditor({
|
|
|
35900
35983
|
view.pasteHTML(html);
|
|
35901
35984
|
return true;
|
|
35902
35985
|
},
|
|
35903
|
-
// When image files are dropped onto the editor, upload
|
|
35904
|
-
// MediaProvider and insert
|
|
35905
|
-
// Drags from the MediaBin can also
|
|
35906
|
-
// or generic-file references
|
|
35907
|
-
// Falls through to default handling for
|
|
35908
|
-
// MediaProvider is available.
|
|
35986
|
+
// When image, video or audio files are dropped onto the editor, upload
|
|
35987
|
+
// them via the MediaProvider and insert image nodes / players
|
|
35988
|
+
// referencing the relative paths. Drags from the MediaBin can also
|
|
35989
|
+
// insert existing image, video, audio, or generic-file references
|
|
35990
|
+
// without uploading them again. Falls through to default handling for
|
|
35991
|
+
// other drops or when no MediaProvider is available.
|
|
35909
35992
|
handleDrop: (view, event, _slice, moved) => {
|
|
35910
35993
|
const dt = event.dataTransfer;
|
|
35911
35994
|
if (!dt) return false;
|
|
35912
35995
|
if (moved) return false;
|
|
35913
|
-
|
|
35914
|
-
|
|
35915
|
-
|
|
35916
|
-
|
|
35917
|
-
|
|
35918
|
-
|
|
35919
|
-
|
|
35920
|
-
}
|
|
35921
|
-
}
|
|
35922
|
-
const imageFiles = filesFromDataTransfer(dt);
|
|
35923
|
-
if (imageFiles.length === 0) {
|
|
35924
|
-
if (dt.files.length > 0 || dt.items.length > 0) {
|
|
35925
|
-
console.warn(
|
|
35926
|
-
"[squisq-editor] Drop received with no recognizable image File. Types:",
|
|
35927
|
-
Array.from(dt.types ?? []),
|
|
35928
|
-
"files:",
|
|
35929
|
-
Array.from(dt.files).map((f) => `${f.name} (${f.type || "no-type"})`)
|
|
35930
|
-
);
|
|
35931
|
-
}
|
|
35932
|
-
return false;
|
|
35933
|
-
}
|
|
35934
|
-
if (!mediaProviderRef.current) {
|
|
35935
|
-
console.warn(
|
|
35936
|
-
"[squisq-editor] Image drop received but no MediaProvider is wired up; cannot persist the image. Pass `mediaProvider` to EditorShell / EditorProvider."
|
|
35937
|
-
);
|
|
35938
|
-
return false;
|
|
35939
|
-
}
|
|
35940
|
-
event.preventDefault();
|
|
35941
|
-
moveSelectionToDropPoint(view, event);
|
|
35942
|
-
uploadAndInsertImages(view, imageFiles, mediaProviderRef.current, bumpMediaRevision);
|
|
35943
|
-
return true;
|
|
35996
|
+
return insertDroppedMedia(
|
|
35997
|
+
view,
|
|
35998
|
+
event,
|
|
35999
|
+
{ left: event.clientX, top: event.clientY },
|
|
36000
|
+
mediaProviderRef.current,
|
|
36001
|
+
bumpMediaRevision
|
|
36002
|
+
);
|
|
35944
36003
|
}
|
|
35945
36004
|
}
|
|
35946
36005
|
});
|
|
@@ -36153,6 +36212,25 @@ function WysiwygEditor({
|
|
|
36153
36212
|
if (!editor || editor.isDestroyed) return;
|
|
36154
36213
|
editor.view.dispatch(editor.state.tr.setMeta(DOCUMENT_TRAY_KEY, layoutMode === "document"));
|
|
36155
36214
|
}, [editor, layoutMode]);
|
|
36215
|
+
const handleDeskDrop = useCallback43(
|
|
36216
|
+
(event) => {
|
|
36217
|
+
if (event.defaultPrevented || readOnly || !editor || editor.isDestroyed) return;
|
|
36218
|
+
const { view } = editor;
|
|
36219
|
+
if (view.dragging || view.dom.contains(event.target)) return;
|
|
36220
|
+
const page = view.dom.getBoundingClientRect();
|
|
36221
|
+
insertDroppedMedia(
|
|
36222
|
+
view,
|
|
36223
|
+
event.nativeEvent,
|
|
36224
|
+
{
|
|
36225
|
+
left: Math.min(Math.max(event.clientX, page.left + 1), page.right - 1),
|
|
36226
|
+
top: Math.min(Math.max(event.clientY, page.top + 1), page.bottom - 1)
|
|
36227
|
+
},
|
|
36228
|
+
mediaProviderRef.current,
|
|
36229
|
+
bumpMediaRevision
|
|
36230
|
+
);
|
|
36231
|
+
},
|
|
36232
|
+
[bumpMediaRevision, editor, readOnly]
|
|
36233
|
+
);
|
|
36156
36234
|
return /* @__PURE__ */ jsx72(CustomTemplateProvider, { docTemplates, onDocTemplatesChange, children: /* @__PURE__ */ jsxs58(
|
|
36157
36235
|
"div",
|
|
36158
36236
|
{
|
|
@@ -36168,6 +36246,7 @@ function WysiwygEditor({
|
|
|
36168
36246
|
"data-block-tags": BLOCK_TAG_DATA_VALUES[blockTagVisibility],
|
|
36169
36247
|
"data-theme-inheritance": themeInheritance,
|
|
36170
36248
|
ref: containerRef,
|
|
36249
|
+
onDrop: handleDeskDrop,
|
|
36171
36250
|
children: [
|
|
36172
36251
|
editor && layoutMode === "document" && /* @__PURE__ */ jsx72(DocumentTray, { editor }),
|
|
36173
36252
|
/* @__PURE__ */ jsx72(EditorContent2, { editor, style: { height: "100%" } }),
|
|
@@ -36255,10 +36334,8 @@ function filesFromDataTransfer(dt) {
|
|
|
36255
36334
|
const seen = /* @__PURE__ */ new Set();
|
|
36256
36335
|
for (let i = 0; i < dt.files.length; i++) {
|
|
36257
36336
|
const file = dt.files[i];
|
|
36258
|
-
|
|
36259
|
-
|
|
36260
|
-
seen.add(`${file.name}|${file.size}`);
|
|
36261
|
-
}
|
|
36337
|
+
files.push(file);
|
|
36338
|
+
seen.add(`${file.name}|${file.size}`);
|
|
36262
36339
|
}
|
|
36263
36340
|
if (dt.items) {
|
|
36264
36341
|
for (let i = 0; i < dt.items.length; i++) {
|
|
@@ -36266,7 +36343,6 @@ function filesFromDataTransfer(dt) {
|
|
|
36266
36343
|
if (item.kind !== "file") continue;
|
|
36267
36344
|
const file = item.getAsFile();
|
|
36268
36345
|
if (!file) continue;
|
|
36269
|
-
if (!looksLikeImageFile(file)) continue;
|
|
36270
36346
|
const key = `${file.name}|${file.size}`;
|
|
36271
36347
|
if (seen.has(key)) continue;
|
|
36272
36348
|
files.push(file);
|
|
@@ -36306,10 +36382,7 @@ function insertExistingMediaReference(view, payload) {
|
|
|
36306
36382
|
if (spec) {
|
|
36307
36383
|
const nodeType = state.schema.nodes[spec.type];
|
|
36308
36384
|
if (!nodeType) return false;
|
|
36309
|
-
|
|
36310
|
-
const { $from } = state.selection;
|
|
36311
|
-
const pos = $from.depth >= 1 ? $from.after(1) : state.doc.content.size;
|
|
36312
|
-
view.dispatch(state.tr.insert(pos, node3).scrollIntoView());
|
|
36385
|
+
insertTopLevelNodes(view, [nodeType.create(spec.attrs)]);
|
|
36313
36386
|
return true;
|
|
36314
36387
|
}
|
|
36315
36388
|
const linkType = state.schema.marks.link;
|
|
@@ -36319,12 +36392,54 @@ function insertExistingMediaReference(view, payload) {
|
|
|
36319
36392
|
view.dispatch(state.tr.replaceSelectionWith(node2).scrollIntoView());
|
|
36320
36393
|
return true;
|
|
36321
36394
|
}
|
|
36322
|
-
function
|
|
36323
|
-
const coords = view.posAtCoords(
|
|
36395
|
+
function moveSelectionToPoint(view, point) {
|
|
36396
|
+
const coords = view.posAtCoords(point);
|
|
36324
36397
|
if (!coords) return;
|
|
36325
36398
|
const tr = view.state.tr.setSelection(Selection2.near(view.state.doc.resolve(coords.pos)));
|
|
36326
36399
|
view.dispatch(tr);
|
|
36327
36400
|
}
|
|
36401
|
+
function insertDroppedMedia(view, event, point, mediaProvider, onMediaUploaded) {
|
|
36402
|
+
const dt = event.dataTransfer;
|
|
36403
|
+
if (!dt) return false;
|
|
36404
|
+
const squisqRaw = dt.getData(SQUISQ_MEDIA_MIME);
|
|
36405
|
+
if (squisqRaw) {
|
|
36406
|
+
const payload = parseSquisqMediaPayload(squisqRaw);
|
|
36407
|
+
if (payload) {
|
|
36408
|
+
event.preventDefault();
|
|
36409
|
+
moveSelectionToPoint(view, point);
|
|
36410
|
+
return insertExistingMediaReference(view, payload);
|
|
36411
|
+
}
|
|
36412
|
+
}
|
|
36413
|
+
const droppedFiles = filesFromDataTransfer(dt);
|
|
36414
|
+
const imageFiles = droppedFiles.filter(looksLikeImageFile);
|
|
36415
|
+
const playerFiles = droppedFiles.filter(isPlayableMediaFile);
|
|
36416
|
+
if (imageFiles.length === 0 && playerFiles.length === 0) {
|
|
36417
|
+
if (dt.files.length > 0 || dt.items.length > 0) {
|
|
36418
|
+
console.warn(
|
|
36419
|
+
"[squisq-editor] Drop received with no recognizable image, video or audio File. Types:",
|
|
36420
|
+
Array.from(dt.types ?? []),
|
|
36421
|
+
"files:",
|
|
36422
|
+
Array.from(dt.files).map((f) => `${f.name} (${f.type || "no-type"})`)
|
|
36423
|
+
);
|
|
36424
|
+
}
|
|
36425
|
+
return false;
|
|
36426
|
+
}
|
|
36427
|
+
if (!mediaProvider) {
|
|
36428
|
+
console.warn(
|
|
36429
|
+
"[squisq-editor] Media drop received but no MediaProvider is wired up; cannot persist the file. Pass `mediaProvider` to EditorShell / EditorProvider."
|
|
36430
|
+
);
|
|
36431
|
+
return false;
|
|
36432
|
+
}
|
|
36433
|
+
event.preventDefault();
|
|
36434
|
+
moveSelectionToPoint(view, point);
|
|
36435
|
+
if (imageFiles.length > 0) {
|
|
36436
|
+
uploadAndInsertImages(view, imageFiles, mediaProvider, onMediaUploaded);
|
|
36437
|
+
}
|
|
36438
|
+
if (playerFiles.length > 0) {
|
|
36439
|
+
void uploadAndInsertMediaPlayers(view, playerFiles, mediaProvider, onMediaUploaded);
|
|
36440
|
+
}
|
|
36441
|
+
return true;
|
|
36442
|
+
}
|
|
36328
36443
|
|
|
36329
36444
|
// src/InlinePreviewGutter.tsx
|
|
36330
36445
|
import { useLayoutEffect as useLayoutEffect13, useMemo as useMemo43, useRef as useRef51, useState as useState70 } from "react";
|
|
@@ -41894,19 +42009,19 @@ function iconForMime(mimeType) {
|
|
|
41894
42009
|
function isImageMime(mimeType) {
|
|
41895
42010
|
return mimeType.startsWith("image/");
|
|
41896
42011
|
}
|
|
41897
|
-
|
|
41898
|
-
|
|
41899
|
-
|
|
41900
|
-
|
|
41901
|
-
return
|
|
42012
|
+
function isMediaFile(file) {
|
|
42013
|
+
return squisqMediaKind(mediaMimeType(file)) !== "file";
|
|
42014
|
+
}
|
|
42015
|
+
function isMediaMime(mimeType) {
|
|
42016
|
+
return squisqMediaKind(mimeType) !== "file";
|
|
41902
42017
|
}
|
|
41903
|
-
function
|
|
42018
|
+
function dataTransferMayContainMedia(dataTransfer) {
|
|
41904
42019
|
if (dataTransfer.items.length > 0) {
|
|
41905
42020
|
return Array.from(dataTransfer.items).some(
|
|
41906
|
-
(item) => item.kind === "file" && (item.type === "" ||
|
|
42021
|
+
(item) => item.kind === "file" && (item.type === "" || isMediaMime(item.type))
|
|
41907
42022
|
);
|
|
41908
42023
|
}
|
|
41909
|
-
return Array.from(dataTransfer.files).some(
|
|
42024
|
+
return Array.from(dataTransfer.files).some(isMediaFile);
|
|
41910
42025
|
}
|
|
41911
42026
|
async function resolveThumbnailUrl(provider, name) {
|
|
41912
42027
|
const url = await provider.resolveUrl(name);
|
|
@@ -42047,7 +42162,7 @@ function MediaBin({
|
|
|
42047
42162
|
try {
|
|
42048
42163
|
for (const file of files) {
|
|
42049
42164
|
const buffer = await file.arrayBuffer();
|
|
42050
|
-
const mimeType = file
|
|
42165
|
+
const mimeType = mediaMimeType(file);
|
|
42051
42166
|
const relativePath = await mediaProvider.addMedia(file.name, buffer, mimeType);
|
|
42052
42167
|
if (onMediaUploaded) {
|
|
42053
42168
|
try {
|
|
@@ -42075,7 +42190,7 @@ function MediaBin({
|
|
|
42075
42190
|
);
|
|
42076
42191
|
const handleDragEnter = useCallback55(
|
|
42077
42192
|
(event) => {
|
|
42078
|
-
if (!mediaProvider || loading || !
|
|
42193
|
+
if (!mediaProvider || loading || !dataTransferMayContainMedia(event.dataTransfer)) return;
|
|
42079
42194
|
event.preventDefault();
|
|
42080
42195
|
event.stopPropagation();
|
|
42081
42196
|
dropDepthRef.current += 1;
|
|
@@ -42085,7 +42200,7 @@ function MediaBin({
|
|
|
42085
42200
|
);
|
|
42086
42201
|
const handleDragOver = useCallback55(
|
|
42087
42202
|
(event) => {
|
|
42088
|
-
if (!mediaProvider || loading || !
|
|
42203
|
+
if (!mediaProvider || loading || !dataTransferMayContainMedia(event.dataTransfer)) return;
|
|
42089
42204
|
event.preventDefault();
|
|
42090
42205
|
event.stopPropagation();
|
|
42091
42206
|
event.dataTransfer.dropEffect = "copy";
|
|
@@ -42104,13 +42219,13 @@ function MediaBin({
|
|
|
42104
42219
|
);
|
|
42105
42220
|
const handleDrop = useCallback55(
|
|
42106
42221
|
(event) => {
|
|
42107
|
-
const
|
|
42108
|
-
if (!mediaProvider || !isDropActive &&
|
|
42222
|
+
const mediaFiles = Array.from(event.dataTransfer.files).filter(isMediaFile);
|
|
42223
|
+
if (!mediaProvider || !isDropActive && mediaFiles.length === 0) return;
|
|
42109
42224
|
event.preventDefault();
|
|
42110
42225
|
event.stopPropagation();
|
|
42111
42226
|
dropDepthRef.current = 0;
|
|
42112
42227
|
setIsDropActive(false);
|
|
42113
|
-
if (
|
|
42228
|
+
if (mediaFiles.length > 0) void uploadFiles(mediaFiles);
|
|
42114
42229
|
},
|
|
42115
42230
|
[isDropActive, mediaProvider, uploadFiles]
|
|
42116
42231
|
);
|
|
@@ -42309,7 +42424,7 @@ ${formatSize(entry.size)}`,
|
|
|
42309
42424
|
] }),
|
|
42310
42425
|
isDropActive && /* @__PURE__ */ jsxs72("div", { className: "squisq-media-bin-drop-target", "aria-hidden": "true", children: [
|
|
42311
42426
|
/* @__PURE__ */ jsx89("span", { className: "squisq-media-bin-drop-target-icon", children: "+" }),
|
|
42312
|
-
/* @__PURE__ */ jsx89("strong", { children: "Drop
|
|
42427
|
+
/* @__PURE__ */ jsx89("strong", { children: "Drop media here" }),
|
|
42313
42428
|
/* @__PURE__ */ jsx89("span", { children: "Attach to this document" })
|
|
42314
42429
|
] }),
|
|
42315
42430
|
contextMenu && /* @__PURE__ */ jsx89(
|
package/dist/index.d.ts
CHANGED
|
@@ -1335,7 +1335,7 @@ interface MediaBinProps {
|
|
|
1335
1335
|
usedMediaPaths?: ReadonlySet<string>;
|
|
1336
1336
|
/**
|
|
1337
1337
|
* Fired after a successful upload via the MediaBin's "+ Upload"
|
|
1338
|
-
* button or
|
|
1338
|
+
* button or media drop target. `relativePath` is what the provider returned (the same
|
|
1339
1339
|
* value embedded in markdown refs, e.g. `attachments/xyz.png`);
|
|
1340
1340
|
* `name` is the uploader-chosen filename before storage renamed
|
|
1341
1341
|
* it. Consumers typically use this to insert a markdown image ref
|
package/dist/index.js
CHANGED
package/dist/shell/index.js
CHANGED
package/dist/styles/index.css
CHANGED
|
@@ -8681,7 +8681,10 @@
|
|
|
8681
8681
|
}
|
|
8682
8682
|
.squisq-insert-menu {
|
|
8683
8683
|
z-index: 100;
|
|
8684
|
-
|
|
8684
|
+
box-sizing: border-box;
|
|
8685
|
+
width: max-content;
|
|
8686
|
+
min-width: min(200px, calc(100vw - 16px));
|
|
8687
|
+
max-width: calc(100vw - 16px);
|
|
8685
8688
|
padding: 4px 0;
|
|
8686
8689
|
overflow-y: auto;
|
|
8687
8690
|
overscroll-behavior: contain;
|
|
@@ -8691,6 +8694,17 @@
|
|
|
8691
8694
|
border-radius: 6px;
|
|
8692
8695
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
|
|
8693
8696
|
}
|
|
8697
|
+
.squisq-insert-menu .squisq-toolbar-overflow-item {
|
|
8698
|
+
white-space: normal;
|
|
8699
|
+
overflow-wrap: anywhere;
|
|
8700
|
+
}
|
|
8701
|
+
.squisq-insert-menu .squisq-toolbar-overflow-item > span {
|
|
8702
|
+
min-width: 0;
|
|
8703
|
+
}
|
|
8704
|
+
.squisq-insert-menu .squisq-toolbar-overflow-icon,
|
|
8705
|
+
.squisq-insert-menu .squisq-toolbar-overflow-item > svg {
|
|
8706
|
+
flex-shrink: 0;
|
|
8707
|
+
}
|
|
8694
8708
|
.squisq-insert-menu-header {
|
|
8695
8709
|
padding: 6px 12px 4px;
|
|
8696
8710
|
font-size: 11px;
|
|
@@ -13599,6 +13613,7 @@ button.squisq-view-menu-radio-row:focus-visible {
|
|
|
13599
13613
|
y2 120ms ease-out;
|
|
13600
13614
|
}
|
|
13601
13615
|
@container squisq-editor-body (max-width: 720px) {
|
|
13616
|
+
.squisq-block-preview-panel,
|
|
13602
13617
|
.squisq-inline-preview-gutter,
|
|
13603
13618
|
.squisq-inline-preview-connectors,
|
|
13604
13619
|
.squisq-outline {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bendyline/squisq-editor-react",
|
|
3
|
-
"version": "2.13.
|
|
3
|
+
"version": "2.13.5",
|
|
4
4
|
"description": "React editor shell with raw/WYSIWYG/preview modes for Squisq documents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bendyline",
|
|
@@ -102,12 +102,12 @@
|
|
|
102
102
|
}
|
|
103
103
|
},
|
|
104
104
|
"dependencies": {
|
|
105
|
-
"@bendyline/squisq": "2.13.
|
|
105
|
+
"@bendyline/squisq": "2.13.3",
|
|
106
106
|
"@bendyline/squisq-calc": "2.11.2",
|
|
107
|
-
"@bendyline/squisq-formats": "2.8.
|
|
108
|
-
"@bendyline/squisq-grid-react": "2.11.
|
|
109
|
-
"@bendyline/squisq-react": "2.12.
|
|
110
|
-
"@bendyline/squisq-video-react": "2.5.
|
|
107
|
+
"@bendyline/squisq-formats": "2.8.4",
|
|
108
|
+
"@bendyline/squisq-grid-react": "2.11.32",
|
|
109
|
+
"@bendyline/squisq-react": "2.12.12",
|
|
110
|
+
"@bendyline/squisq-video-react": "2.5.13",
|
|
111
111
|
"@fortawesome/fontawesome-free": "7.3.1",
|
|
112
112
|
"@tiptap/core": "2.27.3",
|
|
113
113
|
"@tiptap/extension-heading": "2.27.3",
|