@8btc/xcanvas 0.0.14-beta.1 → 0.0.14-beta.11

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 CHANGED
@@ -13,6 +13,7 @@ export declare interface XCanvasAPI {
13
13
  insertImgs: (url: string[]) => Promise<void>;
14
14
  /** 追加多张图片到画布末尾, 如果画布中存在对应图片(url相同),则不做任何操作 */
15
15
  appendImgs: (urls: string[]) => Promise<void>;
16
+ appendImgsForMaze: (urls: string[]) => Promise<void>;
16
17
  /** 保存canvas数据 */
17
18
  save: () => any;
18
19
  /** 从数据中恢复canvas画布 */
@@ -32,10 +33,14 @@ export declare interface XCanvasAPI {
32
33
  reset: () => void;
33
34
  /**
34
35
  * 导出当前选区为图片(dataUrl), 特殊情况:当前选区为单张外部传入图片,则直接返回外部图片url
36
+ *
37
+ * @param tryToSplit 是否尝试拆分选区内的图片元素(当选区内全是图片时生效)
38
+ * 如果选区内的元素都是图片,则返回选区内图片url数组
39
+ * 如果选区内有非图片元素,则返回选区区域的dataUrl
35
40
  * @returns dataUrl | url
36
41
  * @throws 如果没有选区,调用这个函数会抛出错误
37
42
  */
38
- exportSelection: () => Promise<string>;
43
+ exportSelection: (tryToSplit?: boolean) => Promise<string | string[]>;
39
44
  /**
40
45
  * 插入embeddableElement元素
41
46
  *
@@ -66,6 +71,10 @@ export declare interface XCanvasAPI {
66
71
  * 裁切图片
67
72
  */
68
73
  cropImage: () => void;
74
+ /**
75
+ * 删除选区里的全部元素
76
+ */
77
+ deleteSelection: () => void;
69
78
  }
70
79
 
71
80
  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.1";
5
+ const version = "0.0.14-beta.11";
6
6
  const packageJson = {
7
7
  version
8
8
  };
@@ -40,7 +40,7 @@ function CustomExcalidraw(props) {
40
40
  function fetchImageAsDataUrl(src) {
41
41
  return new Promise((resolve, reject) => {
42
42
  const img = new Image();
43
- img.crossOrigin = "Anonymous";
43
+ img.crossOrigin = "anonymous";
44
44
  img.onload = () => {
45
45
  const canvas = document.createElement("canvas");
46
46
  canvas.width = img.naturalWidth;
@@ -118,15 +118,18 @@ function ApiFactory() {
118
118
  };
119
119
  })
120
120
  );
121
+ const files = excalidrawAPI.getFiles();
121
122
  rets.forEach((it) => {
122
- excalidrawAPI.addFiles([
123
- {
124
- mimeType: "image/png",
125
- id: it.url,
126
- dataURL: it.dataUrl,
127
- created: Date.now()
128
- }
129
- ]);
123
+ if (!files[it.url]) {
124
+ excalidrawAPI.addFiles([
125
+ {
126
+ mimeType: "image/png",
127
+ id: it.url,
128
+ dataURL: it.dataUrl,
129
+ created: Date.now()
130
+ }
131
+ ]);
132
+ }
130
133
  });
131
134
  const places = findPlaceForNewElements(
132
135
  excalidrawAPI.getAppState(),
@@ -172,9 +175,47 @@ function ApiFactory() {
172
175
  },
173
176
  insertImgs,
174
177
  appendImgs: async (urls) => {
175
- const files = excalidrawAPI.getFiles();
176
- const newUrls = urls.filter((it) => !files[it]);
177
- await insertImgs(newUrls);
178
+ const els = excalidrawAPI.getSceneElements();
179
+ const imgEls = els.filter((el) => el.type === "image");
180
+ const idSet = /* @__PURE__ */ new Set();
181
+ const intersecitonEls = [];
182
+ imgEls.forEach((el) => {
183
+ idSet.add(el.fileId);
184
+ if (urls.includes(el.fileId)) {
185
+ intersecitonEls.push(el);
186
+ }
187
+ });
188
+ const newUrls = urls.filter((url) => !idSet.has(url));
189
+ if (newUrls.length !== 0) {
190
+ await insertImgs(newUrls);
191
+ } else {
192
+ excalidrawAPI.scrollToContent(intersecitonEls, {
193
+ animate: els.length === 0 ? false : true
194
+ });
195
+ }
196
+ },
197
+ // 插入的图片后缀会自动带上时间戳后缀,用于解决maze跨域问题
198
+ // `${it}?t=${Date.now()}`;
199
+ appendImgsForMaze: async (urls) => {
200
+ const els = excalidrawAPI.getSceneElements();
201
+ const imgEls = els.filter((el) => el.type === "image");
202
+ const idSet = /* @__PURE__ */ new Set();
203
+ const intersecitonEls = [];
204
+ imgEls.forEach((el) => {
205
+ let elFileId = el.fileId.split("?t=")[0];
206
+ idSet.add(elFileId);
207
+ if (urls.includes(elFileId)) {
208
+ intersecitonEls.push(el);
209
+ }
210
+ });
211
+ const newUrls = urls.filter((url) => !idSet.has(url)).map((url) => `${url}?t=${Date.now()}`);
212
+ if (newUrls.length !== 0) {
213
+ await insertImgs(newUrls);
214
+ } else {
215
+ excalidrawAPI.scrollToContent(intersecitonEls, {
216
+ animate: els.length === 0 ? false : true
217
+ });
218
+ }
178
219
  },
179
220
  save: () => {
180
221
  const appState = excalidrawAPI.getAppState();
@@ -286,8 +327,7 @@ function ApiFactory() {
286
327
  }
287
328
  });
288
329
  },
289
- exportSelection: async () => {
290
- if (!excalidrawAPI) return;
330
+ exportSelection: async (tryToSplit) => {
291
331
  const appState = excalidrawAPI.getAppState();
292
332
  const selectedElements = getSelectedElements(
293
333
  excalidrawAPI.getSceneElements(),
@@ -297,13 +337,20 @@ function ApiFactory() {
297
337
  throw "没有选中任何元素";
298
338
  }
299
339
  const files = excalidrawAPI.getFiles();
300
- if (selectedElements.length === 1 && selectedElements[0].type === "image" && selectedElements[0].fileId) {
301
- console.log(files[selectedElements[0].fileId].dataURL);
340
+ if (selectedElements.length === 1 && selectedElements[0].type === "image" && selectedElements[0].fileId && !selectedElements[0].crop) {
302
341
  if (selectedElements[0].fileId.startsWith("http")) {
303
342
  return selectedElements[0].fileId;
304
343
  }
305
344
  return files[selectedElements[0].fileId].dataURL;
306
345
  }
346
+ if (tryToSplit && selectedElements.every((el) => el.type === "image")) {
347
+ return selectedElements.map((el) => {
348
+ if (el.fileId?.startsWith("http") && !el.crop) {
349
+ return selectedElements[0].fileId;
350
+ }
351
+ return files[el.fileId].dataURL;
352
+ });
353
+ }
307
354
  const canvas = await exportToCanvas({
308
355
  elements: selectedElements,
309
356
  appState: {
@@ -351,18 +398,18 @@ function ApiFactory() {
351
398
  y: minY,
352
399
  width: h,
353
400
  height: h,
354
- ...customPos,
355
- id: `loading-${Date.now()}`,
356
- link: data.link ?? "https://www.google.com",
357
401
  // 添加必需的基本样式属性
358
- strokeColor: "#ffffff",
402
+ strokeColor: "#ffffff00",
359
403
  backgroundColor: "transparent",
360
404
  fillStyle: "solid",
361
- strokeWidth: 1,
405
+ strokeWidth: 2,
362
406
  strokeStyle: "solid",
363
407
  roundness: null,
364
- roughness: 1,
408
+ roughness: 0,
365
409
  opacity: 100,
410
+ ...customPos,
411
+ id: `loading-${Date.now()}`,
412
+ link: data.link ?? "https://www.google.com",
366
413
  // 添加必需的变换属性
367
414
  angle: 0,
368
415
  seed: Math.random(),
@@ -414,6 +461,21 @@ function ApiFactory() {
414
461
  croppingElementId: selectedElements[0].id
415
462
  }
416
463
  });
464
+ },
465
+ deleteSelection: () => {
466
+ const appState = excalidrawAPI.getAppState();
467
+ const selectedIds = appState.selectedElementIds;
468
+ const currentElements = excalidrawAPI.getSceneElements();
469
+ const newElements = currentElements.filter((el) => {
470
+ return !selectedIds[el.id];
471
+ });
472
+ excalidrawAPI.updateScene({
473
+ elements: [...newElements],
474
+ appState: {
475
+ ...appState,
476
+ selectedElementIds: {}
477
+ }
478
+ });
417
479
  }
418
480
  });
419
481
  }, [excalidrawAPI, setApi]);
@@ -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.1";
5
+ const version = "0.0.14-beta.11";
6
6
  const packageJson = {
7
7
  version
8
8
  };
@@ -40,7 +40,7 @@
40
40
  function fetchImageAsDataUrl(src) {
41
41
  return new Promise((resolve, reject) => {
42
42
  const img = new Image();
43
- img.crossOrigin = "Anonymous";
43
+ img.crossOrigin = "anonymous";
44
44
  img.onload = () => {
45
45
  const canvas = document.createElement("canvas");
46
46
  canvas.width = img.naturalWidth;
@@ -118,15 +118,18 @@
118
118
  };
119
119
  })
120
120
  );
121
+ const files = excalidrawAPI.getFiles();
121
122
  rets.forEach((it) => {
122
- excalidrawAPI.addFiles([
123
- {
124
- mimeType: "image/png",
125
- id: it.url,
126
- dataURL: it.dataUrl,
127
- created: Date.now()
128
- }
129
- ]);
123
+ if (!files[it.url]) {
124
+ excalidrawAPI.addFiles([
125
+ {
126
+ mimeType: "image/png",
127
+ id: it.url,
128
+ dataURL: it.dataUrl,
129
+ created: Date.now()
130
+ }
131
+ ]);
132
+ }
130
133
  });
131
134
  const places = findPlaceForNewElements(
132
135
  excalidrawAPI.getAppState(),
@@ -172,9 +175,47 @@
172
175
  },
173
176
  insertImgs,
174
177
  appendImgs: async (urls) => {
175
- const files = excalidrawAPI.getFiles();
176
- const newUrls = urls.filter((it) => !files[it]);
177
- await insertImgs(newUrls);
178
+ const els = excalidrawAPI.getSceneElements();
179
+ const imgEls = els.filter((el) => el.type === "image");
180
+ const idSet = /* @__PURE__ */ new Set();
181
+ const intersecitonEls = [];
182
+ imgEls.forEach((el) => {
183
+ idSet.add(el.fileId);
184
+ if (urls.includes(el.fileId)) {
185
+ intersecitonEls.push(el);
186
+ }
187
+ });
188
+ const newUrls = urls.filter((url) => !idSet.has(url));
189
+ if (newUrls.length !== 0) {
190
+ await insertImgs(newUrls);
191
+ } else {
192
+ excalidrawAPI.scrollToContent(intersecitonEls, {
193
+ animate: els.length === 0 ? false : true
194
+ });
195
+ }
196
+ },
197
+ // 插入的图片后缀会自动带上时间戳后缀,用于解决maze跨域问题
198
+ // `${it}?t=${Date.now()}`;
199
+ appendImgsForMaze: async (urls) => {
200
+ const els = excalidrawAPI.getSceneElements();
201
+ const imgEls = els.filter((el) => el.type === "image");
202
+ const idSet = /* @__PURE__ */ new Set();
203
+ const intersecitonEls = [];
204
+ imgEls.forEach((el) => {
205
+ let elFileId = el.fileId.split("?t=")[0];
206
+ idSet.add(elFileId);
207
+ if (urls.includes(elFileId)) {
208
+ intersecitonEls.push(el);
209
+ }
210
+ });
211
+ const newUrls = urls.filter((url) => !idSet.has(url)).map((url) => `${url}?t=${Date.now()}`);
212
+ if (newUrls.length !== 0) {
213
+ await insertImgs(newUrls);
214
+ } else {
215
+ excalidrawAPI.scrollToContent(intersecitonEls, {
216
+ animate: els.length === 0 ? false : true
217
+ });
218
+ }
178
219
  },
179
220
  save: () => {
180
221
  const appState = excalidrawAPI.getAppState();
@@ -286,8 +327,7 @@
286
327
  }
287
328
  });
288
329
  },
289
- exportSelection: async () => {
290
- if (!excalidrawAPI) return;
330
+ exportSelection: async (tryToSplit) => {
291
331
  const appState = excalidrawAPI.getAppState();
292
332
  const selectedElements = getSelectedElements(
293
333
  excalidrawAPI.getSceneElements(),
@@ -297,13 +337,20 @@
297
337
  throw "没有选中任何元素";
298
338
  }
299
339
  const files = excalidrawAPI.getFiles();
300
- if (selectedElements.length === 1 && selectedElements[0].type === "image" && selectedElements[0].fileId) {
301
- console.log(files[selectedElements[0].fileId].dataURL);
340
+ if (selectedElements.length === 1 && selectedElements[0].type === "image" && selectedElements[0].fileId && !selectedElements[0].crop) {
302
341
  if (selectedElements[0].fileId.startsWith("http")) {
303
342
  return selectedElements[0].fileId;
304
343
  }
305
344
  return files[selectedElements[0].fileId].dataURL;
306
345
  }
346
+ if (tryToSplit && selectedElements.every((el) => el.type === "image")) {
347
+ return selectedElements.map((el) => {
348
+ if (el.fileId?.startsWith("http") && !el.crop) {
349
+ return selectedElements[0].fileId;
350
+ }
351
+ return files[el.fileId].dataURL;
352
+ });
353
+ }
307
354
  const canvas = await excalidraw.exportToCanvas({
308
355
  elements: selectedElements,
309
356
  appState: {
@@ -351,18 +398,18 @@
351
398
  y: minY,
352
399
  width: h,
353
400
  height: h,
354
- ...customPos,
355
- id: `loading-${Date.now()}`,
356
- link: data.link ?? "https://www.google.com",
357
401
  // 添加必需的基本样式属性
358
- strokeColor: "#ffffff",
402
+ strokeColor: "#ffffff00",
359
403
  backgroundColor: "transparent",
360
404
  fillStyle: "solid",
361
- strokeWidth: 1,
405
+ strokeWidth: 2,
362
406
  strokeStyle: "solid",
363
407
  roundness: null,
364
- roughness: 1,
408
+ roughness: 0,
365
409
  opacity: 100,
410
+ ...customPos,
411
+ id: `loading-${Date.now()}`,
412
+ link: data.link ?? "https://www.google.com",
366
413
  // 添加必需的变换属性
367
414
  angle: 0,
368
415
  seed: Math.random(),
@@ -414,6 +461,21 @@
414
461
  croppingElementId: selectedElements[0].id
415
462
  }
416
463
  });
464
+ },
465
+ deleteSelection: () => {
466
+ const appState = excalidrawAPI.getAppState();
467
+ const selectedIds = appState.selectedElementIds;
468
+ const currentElements = excalidrawAPI.getSceneElements();
469
+ const newElements = currentElements.filter((el) => {
470
+ return !selectedIds[el.id];
471
+ });
472
+ excalidrawAPI.updateScene({
473
+ elements: [...newElements],
474
+ appState: {
475
+ ...appState,
476
+ selectedElementIds: {}
477
+ }
478
+ });
417
479
  }
418
480
  });
419
481
  }, [excalidrawAPI, setApi]);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@8btc/xcanvas",
3
3
  "private": false,
4
- "version": "0.0.14-beta.1",
4
+ "version": "0.0.14-beta.11",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {