@8btc/xcanvas 0.0.14-beta.17 → 0.0.14-beta.19
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/index.d.ts +2 -0
- package/dist/index.js +225 -37
- package/dist/index.umd.cjs +225 -37
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -77,6 +77,8 @@ export declare interface XCanvasAPI {
|
|
|
77
77
|
deleteSelection: () => void;
|
|
78
78
|
/** 插入一个视频 */
|
|
79
79
|
insertVideo: (urls: string) => Promise<void>;
|
|
80
|
+
/** 插入一个SVG */
|
|
81
|
+
insertSvg: (urls: string) => Promise<void>;
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
export declare interface XcanvasProps extends ExcalidrawProps {
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
|
|
3
3
|
import { Excalidraw, convertToExcalidrawElements, exportToCanvas } from "@8btc/excalidraw";
|
|
4
4
|
import { useEffect } from "react";
|
|
5
|
-
const version = "0.0.14-beta.
|
|
5
|
+
const version = "0.0.14-beta.19";
|
|
6
6
|
const packageJson = {
|
|
7
7
|
version
|
|
8
8
|
};
|
|
@@ -7863,8 +7863,8 @@ async function fetchSvgAsDataUrl(src) {
|
|
|
7863
7863
|
throw new Error("Failed to parse SVG");
|
|
7864
7864
|
}
|
|
7865
7865
|
const viewBox = svgElement.getAttribute("viewBox");
|
|
7866
|
-
let width = parseFloat(svgElement.getAttribute("width") || "0");
|
|
7867
|
-
let height = parseFloat(svgElement.getAttribute("height") || "0");
|
|
7866
|
+
let width = parseFloat(svgElement.getAttribute("width") || "0") / 0.2;
|
|
7867
|
+
let height = parseFloat(svgElement.getAttribute("height") || "0") / 0.2;
|
|
7868
7868
|
if (!width || !height) {
|
|
7869
7869
|
if (viewBox) {
|
|
7870
7870
|
const [, , vbWidth, vbHeight] = viewBox.split(/\s+/).map(Number);
|
|
@@ -7936,16 +7936,19 @@ function findPlaceForNewElements(appState, elements, newElements) {
|
|
|
7936
7936
|
const py = Math.max(maxElY, minY) + gap;
|
|
7937
7937
|
let accWidth = 0;
|
|
7938
7938
|
let startY = py;
|
|
7939
|
+
let maxRowHeight = 0;
|
|
7939
7940
|
const ret = newElements.map((it, index) => {
|
|
7940
|
-
if (accWidth + it.width > maxX) {
|
|
7941
|
+
if (accWidth > 0 && px + accWidth + it.width > maxX) {
|
|
7941
7942
|
accWidth = 0;
|
|
7942
|
-
startY +=
|
|
7943
|
+
startY += maxRowHeight + gap;
|
|
7944
|
+
maxRowHeight = 0;
|
|
7943
7945
|
}
|
|
7944
7946
|
const place = {
|
|
7945
|
-
x:
|
|
7947
|
+
x: px + accWidth,
|
|
7946
7948
|
y: startY
|
|
7947
7949
|
};
|
|
7948
7950
|
accWidth += it.width + gap;
|
|
7951
|
+
maxRowHeight = Math.max(maxRowHeight, it.height);
|
|
7949
7952
|
return place;
|
|
7950
7953
|
});
|
|
7951
7954
|
return ret;
|
|
@@ -7970,6 +7973,51 @@ function getVideoDimensions(videoUrl) {
|
|
|
7970
7973
|
video.load();
|
|
7971
7974
|
});
|
|
7972
7975
|
}
|
|
7976
|
+
function getSvgDimensions(svgUrl) {
|
|
7977
|
+
return new Promise(
|
|
7978
|
+
async (resolve, reject) => {
|
|
7979
|
+
try {
|
|
7980
|
+
let svgText;
|
|
7981
|
+
if (svgUrl.startsWith("data:")) {
|
|
7982
|
+
const base64Data = svgUrl.split(",")[1];
|
|
7983
|
+
svgText = atob(base64Data);
|
|
7984
|
+
} else {
|
|
7985
|
+
const response = await fetch(svgUrl);
|
|
7986
|
+
if (!response.ok) {
|
|
7987
|
+
throw new Error(`Failed to fetch SVG: ${response.statusText}`);
|
|
7988
|
+
}
|
|
7989
|
+
svgText = await response.text();
|
|
7990
|
+
}
|
|
7991
|
+
const parser = new DOMParser();
|
|
7992
|
+
const svgDoc = parser.parseFromString(svgText, "image/svg+xml");
|
|
7993
|
+
const svgElement = svgDoc.documentElement;
|
|
7994
|
+
if (svgElement.querySelector("parsererror")) {
|
|
7995
|
+
throw new Error("Failed to parse SVG");
|
|
7996
|
+
}
|
|
7997
|
+
const viewBox = svgElement.getAttribute("viewBox");
|
|
7998
|
+
let width = parseFloat(svgElement.getAttribute("width") || "0");
|
|
7999
|
+
let height = parseFloat(svgElement.getAttribute("height") || "0");
|
|
8000
|
+
if (!width || !height) {
|
|
8001
|
+
const widthAttr = svgElement.getAttribute("width");
|
|
8002
|
+
const heightAttr = svgElement.getAttribute("height");
|
|
8003
|
+
if (widthAttr) width = parseFloat(widthAttr);
|
|
8004
|
+
if (heightAttr) height = parseFloat(heightAttr);
|
|
8005
|
+
}
|
|
8006
|
+
if ((!width || !height) && viewBox) {
|
|
8007
|
+
const [, , vbWidth, vbHeight] = viewBox.split(/\s+/).map(Number);
|
|
8008
|
+
width = width || vbWidth;
|
|
8009
|
+
height = height || vbHeight;
|
|
8010
|
+
}
|
|
8011
|
+
if (!width) width = 300;
|
|
8012
|
+
if (!height) height = 150;
|
|
8013
|
+
resolve({ width: width / 0.2, height: height / 0.2 });
|
|
8014
|
+
} catch (error) {
|
|
8015
|
+
console.error(`failed to get SVG dimensions: ${svgUrl}`, error);
|
|
8016
|
+
reject(error);
|
|
8017
|
+
}
|
|
8018
|
+
}
|
|
8019
|
+
);
|
|
8020
|
+
}
|
|
7973
8021
|
function ApiFactory() {
|
|
7974
8022
|
const excalidrawAPI = useAtomValue(excalidrawApiAtom);
|
|
7975
8023
|
const setApi = useSetAtom(apiAtom);
|
|
@@ -7979,18 +8027,32 @@ function ApiFactory() {
|
|
|
7979
8027
|
}
|
|
7980
8028
|
const insertImgs = async (urls) => {
|
|
7981
8029
|
if (urls.length === 0) return;
|
|
7982
|
-
const
|
|
7983
|
-
|
|
7984
|
-
|
|
7985
|
-
|
|
7986
|
-
|
|
7987
|
-
|
|
7988
|
-
|
|
8030
|
+
const isSvg = (url) => {
|
|
8031
|
+
return url.toLowerCase().endsWith(".svg") || url.includes("data:image/svg+xml");
|
|
8032
|
+
};
|
|
8033
|
+
const currentElements = excalidrawAPI.getSceneElements();
|
|
8034
|
+
const allRets = await Promise.all(
|
|
8035
|
+
urls.map(async (url) => {
|
|
8036
|
+
if (isSvg(url)) {
|
|
8037
|
+
const dimension = await getSvgDimensions(url);
|
|
8038
|
+
return {
|
|
8039
|
+
...dimension,
|
|
8040
|
+
url,
|
|
8041
|
+
isSvg: true
|
|
8042
|
+
};
|
|
8043
|
+
} else {
|
|
8044
|
+
const temp = await fetchImageAsDataUrl(url);
|
|
8045
|
+
return {
|
|
8046
|
+
...temp,
|
|
8047
|
+
url,
|
|
8048
|
+
isSvg: false
|
|
8049
|
+
};
|
|
8050
|
+
}
|
|
7989
8051
|
})
|
|
7990
8052
|
);
|
|
7991
8053
|
const files = excalidrawAPI.getFiles();
|
|
7992
|
-
|
|
7993
|
-
if (!files[it.url]) {
|
|
8054
|
+
allRets.forEach((it) => {
|
|
8055
|
+
if (!it.isSvg && !files[it.url]) {
|
|
7994
8056
|
excalidrawAPI.addFiles([
|
|
7995
8057
|
{
|
|
7996
8058
|
mimeType: "image/png",
|
|
@@ -8004,31 +8066,70 @@ function ApiFactory() {
|
|
|
8004
8066
|
const places = findPlaceForNewElements(
|
|
8005
8067
|
excalidrawAPI.getAppState(),
|
|
8006
8068
|
excalidrawAPI.getSceneElements(),
|
|
8007
|
-
|
|
8069
|
+
allRets
|
|
8008
8070
|
);
|
|
8009
|
-
const
|
|
8010
|
-
|
|
8011
|
-
|
|
8012
|
-
|
|
8013
|
-
|
|
8014
|
-
|
|
8015
|
-
|
|
8016
|
-
|
|
8017
|
-
|
|
8018
|
-
|
|
8019
|
-
|
|
8020
|
-
|
|
8021
|
-
|
|
8022
|
-
|
|
8071
|
+
const allNewElements = convertToExcalidrawElements(
|
|
8072
|
+
allRets.map((it, index) => {
|
|
8073
|
+
if (it.isSvg) {
|
|
8074
|
+
return {
|
|
8075
|
+
type: "embeddable",
|
|
8076
|
+
width: it.width,
|
|
8077
|
+
height: it.height,
|
|
8078
|
+
x: places[index].x,
|
|
8079
|
+
y: places[index].y,
|
|
8080
|
+
strokeColor: "#ffffff00",
|
|
8081
|
+
backgroundColor: "transparent",
|
|
8082
|
+
fillStyle: "solid",
|
|
8083
|
+
strokeWidth: 2,
|
|
8084
|
+
strokeStyle: "solid",
|
|
8085
|
+
roundness: null,
|
|
8086
|
+
roughness: 0,
|
|
8087
|
+
opacity: 100,
|
|
8088
|
+
id: `svg-${Date.now()}-${index}`,
|
|
8089
|
+
link: it.url ?? "https://www.google.com",
|
|
8090
|
+
angle: 0,
|
|
8091
|
+
seed: Math.random(),
|
|
8092
|
+
version: 1,
|
|
8093
|
+
versionNonce: Math.random(),
|
|
8094
|
+
locked: false,
|
|
8095
|
+
isDeleted: false,
|
|
8096
|
+
groupIds: [],
|
|
8097
|
+
boundElements: [],
|
|
8098
|
+
updated: Date.now(),
|
|
8099
|
+
frameId: null,
|
|
8100
|
+
index: null,
|
|
8101
|
+
customData: {
|
|
8102
|
+
custom: true,
|
|
8103
|
+
component: "svg",
|
|
8104
|
+
naturalWidth: it.width,
|
|
8105
|
+
naturalHeight: it.height,
|
|
8106
|
+
url: it.url
|
|
8107
|
+
}
|
|
8108
|
+
};
|
|
8109
|
+
} else {
|
|
8110
|
+
return {
|
|
8111
|
+
type: "image",
|
|
8112
|
+
width: it.width,
|
|
8113
|
+
height: it.height,
|
|
8114
|
+
x: places[index].x,
|
|
8115
|
+
y: places[index].y,
|
|
8116
|
+
fileId: it.url,
|
|
8117
|
+
customData: {
|
|
8118
|
+
naturalWidth: it.width,
|
|
8119
|
+
naturalHeight: it.height
|
|
8120
|
+
}
|
|
8121
|
+
};
|
|
8122
|
+
}
|
|
8023
8123
|
})
|
|
8024
8124
|
);
|
|
8025
|
-
|
|
8026
|
-
|
|
8027
|
-
|
|
8028
|
-
|
|
8029
|
-
|
|
8030
|
-
|
|
8031
|
-
|
|
8125
|
+
if (allNewElements.length > 0) {
|
|
8126
|
+
excalidrawAPI.updateScene({
|
|
8127
|
+
elements: [...currentElements, ...allNewElements]
|
|
8128
|
+
});
|
|
8129
|
+
excalidrawAPI.scrollToContent(allNewElements, {
|
|
8130
|
+
animate: currentElements.length === 0 ? false : true
|
|
8131
|
+
});
|
|
8132
|
+
}
|
|
8032
8133
|
};
|
|
8033
8134
|
const getSelectedElements = (elements, appState) => {
|
|
8034
8135
|
const selectedIds = appState.selectedElementIds;
|
|
@@ -8059,6 +8160,18 @@ function ApiFactory() {
|
|
|
8059
8160
|
intersecitonEls.push(el);
|
|
8060
8161
|
}
|
|
8061
8162
|
});
|
|
8163
|
+
const svgEls = els.filter(
|
|
8164
|
+
(el) => el.type === "embeddable" && el.customData?.component === "svg"
|
|
8165
|
+
);
|
|
8166
|
+
svgEls.forEach((el) => {
|
|
8167
|
+
const svgUrl = el.customData?.url;
|
|
8168
|
+
if (svgUrl) {
|
|
8169
|
+
idSet.add(svgUrl);
|
|
8170
|
+
if (urls.includes(svgUrl)) {
|
|
8171
|
+
intersecitonEls.push(el);
|
|
8172
|
+
}
|
|
8173
|
+
}
|
|
8174
|
+
});
|
|
8062
8175
|
const newUrls = urls.filter((url) => !idSet.has(url));
|
|
8063
8176
|
if (newUrls.length !== 0) {
|
|
8064
8177
|
await insertImgs(newUrls);
|
|
@@ -8424,6 +8537,81 @@ function ApiFactory() {
|
|
|
8424
8537
|
excalidrawAPI.scrollToContent(els, {
|
|
8425
8538
|
animate: currentElements.length === 0 ? false : true
|
|
8426
8539
|
});
|
|
8540
|
+
},
|
|
8541
|
+
insertSvg: async (url) => {
|
|
8542
|
+
const currentElements = excalidrawAPI.getSceneElements();
|
|
8543
|
+
const dimension = await getSvgDimensions(url);
|
|
8544
|
+
const rets = [
|
|
8545
|
+
{
|
|
8546
|
+
...dimension,
|
|
8547
|
+
url
|
|
8548
|
+
}
|
|
8549
|
+
];
|
|
8550
|
+
const existElements = currentElements.filter(
|
|
8551
|
+
(el) => el.type === "embeddable" && el.customData?.component === "svg" && el.customData?.url === url
|
|
8552
|
+
);
|
|
8553
|
+
if (existElements.length > 0) {
|
|
8554
|
+
excalidrawAPI.scrollToContent(existElements, {
|
|
8555
|
+
animate: true
|
|
8556
|
+
});
|
|
8557
|
+
return;
|
|
8558
|
+
}
|
|
8559
|
+
const places = findPlaceForNewElements(
|
|
8560
|
+
excalidrawAPI.getAppState(),
|
|
8561
|
+
excalidrawAPI.getSceneElements(),
|
|
8562
|
+
rets
|
|
8563
|
+
);
|
|
8564
|
+
const els = convertToExcalidrawElements(
|
|
8565
|
+
rets.map((it, index) => {
|
|
8566
|
+
return {
|
|
8567
|
+
type: "embeddable",
|
|
8568
|
+
width: it.width,
|
|
8569
|
+
height: it.height,
|
|
8570
|
+
x: places[index].x,
|
|
8571
|
+
y: places[index].y,
|
|
8572
|
+
// 添加必需的基本样式属性
|
|
8573
|
+
strokeColor: "#ffffff00",
|
|
8574
|
+
backgroundColor: "transparent",
|
|
8575
|
+
fillStyle: "solid",
|
|
8576
|
+
strokeWidth: 2,
|
|
8577
|
+
strokeStyle: "solid",
|
|
8578
|
+
roundness: null,
|
|
8579
|
+
roughness: 0,
|
|
8580
|
+
opacity: 100,
|
|
8581
|
+
id: `svg-${Date.now()}`,
|
|
8582
|
+
link: it.url ?? "https://www.google.com",
|
|
8583
|
+
// 添加必需的变换属性
|
|
8584
|
+
angle: 0,
|
|
8585
|
+
seed: Math.random(),
|
|
8586
|
+
version: 1,
|
|
8587
|
+
versionNonce: Math.random(),
|
|
8588
|
+
// 添加必需的状态属性
|
|
8589
|
+
locked: false,
|
|
8590
|
+
isDeleted: false,
|
|
8591
|
+
groupIds: [],
|
|
8592
|
+
// 添加绑定框属性
|
|
8593
|
+
boundElements: [],
|
|
8594
|
+
updated: Date.now(),
|
|
8595
|
+
// 添加必需的索引和帧ID属性
|
|
8596
|
+
frameId: null,
|
|
8597
|
+
index: null,
|
|
8598
|
+
// 添加缺失的index属性
|
|
8599
|
+
customData: {
|
|
8600
|
+
custom: true,
|
|
8601
|
+
component: "svg",
|
|
8602
|
+
naturalWidth: it.width,
|
|
8603
|
+
naturalHeight: it.height,
|
|
8604
|
+
url: it.url
|
|
8605
|
+
}
|
|
8606
|
+
};
|
|
8607
|
+
})
|
|
8608
|
+
);
|
|
8609
|
+
excalidrawAPI.updateScene({
|
|
8610
|
+
elements: [...currentElements, ...els]
|
|
8611
|
+
});
|
|
8612
|
+
excalidrawAPI.scrollToContent(els, {
|
|
8613
|
+
animate: currentElements.length === 0 ? false : true
|
|
8614
|
+
});
|
|
8427
8615
|
}
|
|
8428
8616
|
});
|
|
8429
8617
|
}, [excalidrawAPI, setApi]);
|
package/dist/index.umd.cjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("react/jsx-runtime"), require("jotai"), require("@8btc/excalidraw"), require("react")) : typeof define === "function" && define.amd ? define(["exports", "react/jsx-runtime", "jotai", "@8btc/excalidraw", "react"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.XCanvas = {}, global.jsxRuntime, global.Jotai, global.Excalidraw, global.React));
|
|
3
3
|
})(this, (function(exports2, jsxRuntime, jotai, excalidraw, react) {
|
|
4
4
|
"use strict";
|
|
5
|
-
const version = "0.0.14-beta.
|
|
5
|
+
const version = "0.0.14-beta.19";
|
|
6
6
|
const packageJson = {
|
|
7
7
|
version
|
|
8
8
|
};
|
|
@@ -7863,8 +7863,8 @@
|
|
|
7863
7863
|
throw new Error("Failed to parse SVG");
|
|
7864
7864
|
}
|
|
7865
7865
|
const viewBox = svgElement.getAttribute("viewBox");
|
|
7866
|
-
let width = parseFloat(svgElement.getAttribute("width") || "0");
|
|
7867
|
-
let height = parseFloat(svgElement.getAttribute("height") || "0");
|
|
7866
|
+
let width = parseFloat(svgElement.getAttribute("width") || "0") / 0.2;
|
|
7867
|
+
let height = parseFloat(svgElement.getAttribute("height") || "0") / 0.2;
|
|
7868
7868
|
if (!width || !height) {
|
|
7869
7869
|
if (viewBox) {
|
|
7870
7870
|
const [, , vbWidth, vbHeight] = viewBox.split(/\s+/).map(Number);
|
|
@@ -7936,16 +7936,19 @@
|
|
|
7936
7936
|
const py = Math.max(maxElY, minY) + gap;
|
|
7937
7937
|
let accWidth = 0;
|
|
7938
7938
|
let startY = py;
|
|
7939
|
+
let maxRowHeight = 0;
|
|
7939
7940
|
const ret = newElements.map((it, index) => {
|
|
7940
|
-
if (accWidth + it.width > maxX) {
|
|
7941
|
+
if (accWidth > 0 && px + accWidth + it.width > maxX) {
|
|
7941
7942
|
accWidth = 0;
|
|
7942
|
-
startY +=
|
|
7943
|
+
startY += maxRowHeight + gap;
|
|
7944
|
+
maxRowHeight = 0;
|
|
7943
7945
|
}
|
|
7944
7946
|
const place = {
|
|
7945
|
-
x:
|
|
7947
|
+
x: px + accWidth,
|
|
7946
7948
|
y: startY
|
|
7947
7949
|
};
|
|
7948
7950
|
accWidth += it.width + gap;
|
|
7951
|
+
maxRowHeight = Math.max(maxRowHeight, it.height);
|
|
7949
7952
|
return place;
|
|
7950
7953
|
});
|
|
7951
7954
|
return ret;
|
|
@@ -7970,6 +7973,51 @@
|
|
|
7970
7973
|
video.load();
|
|
7971
7974
|
});
|
|
7972
7975
|
}
|
|
7976
|
+
function getSvgDimensions(svgUrl) {
|
|
7977
|
+
return new Promise(
|
|
7978
|
+
async (resolve, reject) => {
|
|
7979
|
+
try {
|
|
7980
|
+
let svgText;
|
|
7981
|
+
if (svgUrl.startsWith("data:")) {
|
|
7982
|
+
const base64Data = svgUrl.split(",")[1];
|
|
7983
|
+
svgText = atob(base64Data);
|
|
7984
|
+
} else {
|
|
7985
|
+
const response = await fetch(svgUrl);
|
|
7986
|
+
if (!response.ok) {
|
|
7987
|
+
throw new Error(`Failed to fetch SVG: ${response.statusText}`);
|
|
7988
|
+
}
|
|
7989
|
+
svgText = await response.text();
|
|
7990
|
+
}
|
|
7991
|
+
const parser = new DOMParser();
|
|
7992
|
+
const svgDoc = parser.parseFromString(svgText, "image/svg+xml");
|
|
7993
|
+
const svgElement = svgDoc.documentElement;
|
|
7994
|
+
if (svgElement.querySelector("parsererror")) {
|
|
7995
|
+
throw new Error("Failed to parse SVG");
|
|
7996
|
+
}
|
|
7997
|
+
const viewBox = svgElement.getAttribute("viewBox");
|
|
7998
|
+
let width = parseFloat(svgElement.getAttribute("width") || "0");
|
|
7999
|
+
let height = parseFloat(svgElement.getAttribute("height") || "0");
|
|
8000
|
+
if (!width || !height) {
|
|
8001
|
+
const widthAttr = svgElement.getAttribute("width");
|
|
8002
|
+
const heightAttr = svgElement.getAttribute("height");
|
|
8003
|
+
if (widthAttr) width = parseFloat(widthAttr);
|
|
8004
|
+
if (heightAttr) height = parseFloat(heightAttr);
|
|
8005
|
+
}
|
|
8006
|
+
if ((!width || !height) && viewBox) {
|
|
8007
|
+
const [, , vbWidth, vbHeight] = viewBox.split(/\s+/).map(Number);
|
|
8008
|
+
width = width || vbWidth;
|
|
8009
|
+
height = height || vbHeight;
|
|
8010
|
+
}
|
|
8011
|
+
if (!width) width = 300;
|
|
8012
|
+
if (!height) height = 150;
|
|
8013
|
+
resolve({ width: width / 0.2, height: height / 0.2 });
|
|
8014
|
+
} catch (error) {
|
|
8015
|
+
console.error(`failed to get SVG dimensions: ${svgUrl}`, error);
|
|
8016
|
+
reject(error);
|
|
8017
|
+
}
|
|
8018
|
+
}
|
|
8019
|
+
);
|
|
8020
|
+
}
|
|
7973
8021
|
function ApiFactory() {
|
|
7974
8022
|
const excalidrawAPI = jotai.useAtomValue(excalidrawApiAtom);
|
|
7975
8023
|
const setApi = jotai.useSetAtom(apiAtom);
|
|
@@ -7979,18 +8027,32 @@
|
|
|
7979
8027
|
}
|
|
7980
8028
|
const insertImgs = async (urls) => {
|
|
7981
8029
|
if (urls.length === 0) return;
|
|
7982
|
-
const
|
|
7983
|
-
|
|
7984
|
-
|
|
7985
|
-
|
|
7986
|
-
|
|
7987
|
-
|
|
7988
|
-
|
|
8030
|
+
const isSvg = (url) => {
|
|
8031
|
+
return url.toLowerCase().endsWith(".svg") || url.includes("data:image/svg+xml");
|
|
8032
|
+
};
|
|
8033
|
+
const currentElements = excalidrawAPI.getSceneElements();
|
|
8034
|
+
const allRets = await Promise.all(
|
|
8035
|
+
urls.map(async (url) => {
|
|
8036
|
+
if (isSvg(url)) {
|
|
8037
|
+
const dimension = await getSvgDimensions(url);
|
|
8038
|
+
return {
|
|
8039
|
+
...dimension,
|
|
8040
|
+
url,
|
|
8041
|
+
isSvg: true
|
|
8042
|
+
};
|
|
8043
|
+
} else {
|
|
8044
|
+
const temp = await fetchImageAsDataUrl(url);
|
|
8045
|
+
return {
|
|
8046
|
+
...temp,
|
|
8047
|
+
url,
|
|
8048
|
+
isSvg: false
|
|
8049
|
+
};
|
|
8050
|
+
}
|
|
7989
8051
|
})
|
|
7990
8052
|
);
|
|
7991
8053
|
const files = excalidrawAPI.getFiles();
|
|
7992
|
-
|
|
7993
|
-
if (!files[it.url]) {
|
|
8054
|
+
allRets.forEach((it) => {
|
|
8055
|
+
if (!it.isSvg && !files[it.url]) {
|
|
7994
8056
|
excalidrawAPI.addFiles([
|
|
7995
8057
|
{
|
|
7996
8058
|
mimeType: "image/png",
|
|
@@ -8004,31 +8066,70 @@
|
|
|
8004
8066
|
const places = findPlaceForNewElements(
|
|
8005
8067
|
excalidrawAPI.getAppState(),
|
|
8006
8068
|
excalidrawAPI.getSceneElements(),
|
|
8007
|
-
|
|
8069
|
+
allRets
|
|
8008
8070
|
);
|
|
8009
|
-
const
|
|
8010
|
-
|
|
8011
|
-
|
|
8012
|
-
|
|
8013
|
-
|
|
8014
|
-
|
|
8015
|
-
|
|
8016
|
-
|
|
8017
|
-
|
|
8018
|
-
|
|
8019
|
-
|
|
8020
|
-
|
|
8021
|
-
|
|
8022
|
-
|
|
8071
|
+
const allNewElements = excalidraw.convertToExcalidrawElements(
|
|
8072
|
+
allRets.map((it, index) => {
|
|
8073
|
+
if (it.isSvg) {
|
|
8074
|
+
return {
|
|
8075
|
+
type: "embeddable",
|
|
8076
|
+
width: it.width,
|
|
8077
|
+
height: it.height,
|
|
8078
|
+
x: places[index].x,
|
|
8079
|
+
y: places[index].y,
|
|
8080
|
+
strokeColor: "#ffffff00",
|
|
8081
|
+
backgroundColor: "transparent",
|
|
8082
|
+
fillStyle: "solid",
|
|
8083
|
+
strokeWidth: 2,
|
|
8084
|
+
strokeStyle: "solid",
|
|
8085
|
+
roundness: null,
|
|
8086
|
+
roughness: 0,
|
|
8087
|
+
opacity: 100,
|
|
8088
|
+
id: `svg-${Date.now()}-${index}`,
|
|
8089
|
+
link: it.url ?? "https://www.google.com",
|
|
8090
|
+
angle: 0,
|
|
8091
|
+
seed: Math.random(),
|
|
8092
|
+
version: 1,
|
|
8093
|
+
versionNonce: Math.random(),
|
|
8094
|
+
locked: false,
|
|
8095
|
+
isDeleted: false,
|
|
8096
|
+
groupIds: [],
|
|
8097
|
+
boundElements: [],
|
|
8098
|
+
updated: Date.now(),
|
|
8099
|
+
frameId: null,
|
|
8100
|
+
index: null,
|
|
8101
|
+
customData: {
|
|
8102
|
+
custom: true,
|
|
8103
|
+
component: "svg",
|
|
8104
|
+
naturalWidth: it.width,
|
|
8105
|
+
naturalHeight: it.height,
|
|
8106
|
+
url: it.url
|
|
8107
|
+
}
|
|
8108
|
+
};
|
|
8109
|
+
} else {
|
|
8110
|
+
return {
|
|
8111
|
+
type: "image",
|
|
8112
|
+
width: it.width,
|
|
8113
|
+
height: it.height,
|
|
8114
|
+
x: places[index].x,
|
|
8115
|
+
y: places[index].y,
|
|
8116
|
+
fileId: it.url,
|
|
8117
|
+
customData: {
|
|
8118
|
+
naturalWidth: it.width,
|
|
8119
|
+
naturalHeight: it.height
|
|
8120
|
+
}
|
|
8121
|
+
};
|
|
8122
|
+
}
|
|
8023
8123
|
})
|
|
8024
8124
|
);
|
|
8025
|
-
|
|
8026
|
-
|
|
8027
|
-
|
|
8028
|
-
|
|
8029
|
-
|
|
8030
|
-
|
|
8031
|
-
|
|
8125
|
+
if (allNewElements.length > 0) {
|
|
8126
|
+
excalidrawAPI.updateScene({
|
|
8127
|
+
elements: [...currentElements, ...allNewElements]
|
|
8128
|
+
});
|
|
8129
|
+
excalidrawAPI.scrollToContent(allNewElements, {
|
|
8130
|
+
animate: currentElements.length === 0 ? false : true
|
|
8131
|
+
});
|
|
8132
|
+
}
|
|
8032
8133
|
};
|
|
8033
8134
|
const getSelectedElements = (elements, appState) => {
|
|
8034
8135
|
const selectedIds = appState.selectedElementIds;
|
|
@@ -8059,6 +8160,18 @@
|
|
|
8059
8160
|
intersecitonEls.push(el);
|
|
8060
8161
|
}
|
|
8061
8162
|
});
|
|
8163
|
+
const svgEls = els.filter(
|
|
8164
|
+
(el) => el.type === "embeddable" && el.customData?.component === "svg"
|
|
8165
|
+
);
|
|
8166
|
+
svgEls.forEach((el) => {
|
|
8167
|
+
const svgUrl = el.customData?.url;
|
|
8168
|
+
if (svgUrl) {
|
|
8169
|
+
idSet.add(svgUrl);
|
|
8170
|
+
if (urls.includes(svgUrl)) {
|
|
8171
|
+
intersecitonEls.push(el);
|
|
8172
|
+
}
|
|
8173
|
+
}
|
|
8174
|
+
});
|
|
8062
8175
|
const newUrls = urls.filter((url) => !idSet.has(url));
|
|
8063
8176
|
if (newUrls.length !== 0) {
|
|
8064
8177
|
await insertImgs(newUrls);
|
|
@@ -8424,6 +8537,81 @@
|
|
|
8424
8537
|
excalidrawAPI.scrollToContent(els, {
|
|
8425
8538
|
animate: currentElements.length === 0 ? false : true
|
|
8426
8539
|
});
|
|
8540
|
+
},
|
|
8541
|
+
insertSvg: async (url) => {
|
|
8542
|
+
const currentElements = excalidrawAPI.getSceneElements();
|
|
8543
|
+
const dimension = await getSvgDimensions(url);
|
|
8544
|
+
const rets = [
|
|
8545
|
+
{
|
|
8546
|
+
...dimension,
|
|
8547
|
+
url
|
|
8548
|
+
}
|
|
8549
|
+
];
|
|
8550
|
+
const existElements = currentElements.filter(
|
|
8551
|
+
(el) => el.type === "embeddable" && el.customData?.component === "svg" && el.customData?.url === url
|
|
8552
|
+
);
|
|
8553
|
+
if (existElements.length > 0) {
|
|
8554
|
+
excalidrawAPI.scrollToContent(existElements, {
|
|
8555
|
+
animate: true
|
|
8556
|
+
});
|
|
8557
|
+
return;
|
|
8558
|
+
}
|
|
8559
|
+
const places = findPlaceForNewElements(
|
|
8560
|
+
excalidrawAPI.getAppState(),
|
|
8561
|
+
excalidrawAPI.getSceneElements(),
|
|
8562
|
+
rets
|
|
8563
|
+
);
|
|
8564
|
+
const els = excalidraw.convertToExcalidrawElements(
|
|
8565
|
+
rets.map((it, index) => {
|
|
8566
|
+
return {
|
|
8567
|
+
type: "embeddable",
|
|
8568
|
+
width: it.width,
|
|
8569
|
+
height: it.height,
|
|
8570
|
+
x: places[index].x,
|
|
8571
|
+
y: places[index].y,
|
|
8572
|
+
// 添加必需的基本样式属性
|
|
8573
|
+
strokeColor: "#ffffff00",
|
|
8574
|
+
backgroundColor: "transparent",
|
|
8575
|
+
fillStyle: "solid",
|
|
8576
|
+
strokeWidth: 2,
|
|
8577
|
+
strokeStyle: "solid",
|
|
8578
|
+
roundness: null,
|
|
8579
|
+
roughness: 0,
|
|
8580
|
+
opacity: 100,
|
|
8581
|
+
id: `svg-${Date.now()}`,
|
|
8582
|
+
link: it.url ?? "https://www.google.com",
|
|
8583
|
+
// 添加必需的变换属性
|
|
8584
|
+
angle: 0,
|
|
8585
|
+
seed: Math.random(),
|
|
8586
|
+
version: 1,
|
|
8587
|
+
versionNonce: Math.random(),
|
|
8588
|
+
// 添加必需的状态属性
|
|
8589
|
+
locked: false,
|
|
8590
|
+
isDeleted: false,
|
|
8591
|
+
groupIds: [],
|
|
8592
|
+
// 添加绑定框属性
|
|
8593
|
+
boundElements: [],
|
|
8594
|
+
updated: Date.now(),
|
|
8595
|
+
// 添加必需的索引和帧ID属性
|
|
8596
|
+
frameId: null,
|
|
8597
|
+
index: null,
|
|
8598
|
+
// 添加缺失的index属性
|
|
8599
|
+
customData: {
|
|
8600
|
+
custom: true,
|
|
8601
|
+
component: "svg",
|
|
8602
|
+
naturalWidth: it.width,
|
|
8603
|
+
naturalHeight: it.height,
|
|
8604
|
+
url: it.url
|
|
8605
|
+
}
|
|
8606
|
+
};
|
|
8607
|
+
})
|
|
8608
|
+
);
|
|
8609
|
+
excalidrawAPI.updateScene({
|
|
8610
|
+
elements: [...currentElements, ...els]
|
|
8611
|
+
});
|
|
8612
|
+
excalidrawAPI.scrollToContent(els, {
|
|
8613
|
+
animate: currentElements.length === 0 ? false : true
|
|
8614
|
+
});
|
|
8427
8615
|
}
|
|
8428
8616
|
});
|
|
8429
8617
|
}, [excalidrawAPI, setApi]);
|