canvu-react 0.3.14 → 0.3.17
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/README.md +5 -0
- package/dist/react.cjs +21 -50
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1 -0
- package/dist/react.d.ts +1 -0
- package/dist/react.js +21 -50
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -308,6 +308,11 @@ canvu can now stream PDF pages into the canvas progressively through
|
|
|
308
308
|
`onItemsReady(...)`, skip PDF thumbnails during ingest, and use a lower initial
|
|
309
309
|
raster scale by default to improve time-to-first-render.
|
|
310
310
|
|
|
311
|
+
The native file tool now accepts multiple images and PDFs from the picker and
|
|
312
|
+
stacks every imported asset vertically. When the board already has uploaded
|
|
313
|
+
images or PDFs, new imports are inserted immediately after the existing stack
|
|
314
|
+
instead of overlapping previous content.
|
|
315
|
+
|
|
311
316
|
This helper is the same ingestion layer used internally by the native file
|
|
312
317
|
tool, so external imports do not need to reimplement PDF rasterization, local
|
|
313
318
|
blob persistence, or `pluginData` attachment.
|
package/dist/react.cjs
CHANGED
|
@@ -1365,12 +1365,12 @@ async function uploadAssetIfNeeded(assetStore, file, kind) {
|
|
|
1365
1365
|
async function ingestAssetFilesToSceneItems(options) {
|
|
1366
1366
|
const {
|
|
1367
1367
|
files,
|
|
1368
|
+
existingItems = [],
|
|
1368
1369
|
worldCenter,
|
|
1369
1370
|
assetStore,
|
|
1370
1371
|
imageStore = new IndexedDbImageStore(),
|
|
1371
1372
|
createId = createShapeId,
|
|
1372
1373
|
gapWorld = 16,
|
|
1373
|
-
stepWorld = 48,
|
|
1374
1374
|
pdfScale = 1.15,
|
|
1375
1375
|
pdfPageConcurrency = 2,
|
|
1376
1376
|
decorateItem,
|
|
@@ -1379,10 +1379,7 @@ async function ingestAssetFilesToSceneItems(options) {
|
|
|
1379
1379
|
} = options;
|
|
1380
1380
|
const items = [];
|
|
1381
1381
|
const errors = [];
|
|
1382
|
-
let
|
|
1383
|
-
let occupiedBottomY = null;
|
|
1384
|
-
let imageYOffsetAdjustment = 0;
|
|
1385
|
-
let hasImagePlacementBase = false;
|
|
1382
|
+
let occupiedBottomY = existingItems.length > 0 ? Math.max(...existingItems.map((item) => item.bounds.y + item.bounds.height)) : null;
|
|
1386
1383
|
for (const file of files) {
|
|
1387
1384
|
const kind = getAssetKindForFile(file);
|
|
1388
1385
|
if (!kind) {
|
|
@@ -1405,10 +1402,10 @@ async function ingestAssetFilesToSceneItems(options) {
|
|
|
1405
1402
|
const uploadResult2 = await uploadResultPromise;
|
|
1406
1403
|
const fullUrl2 = await createBlobUrlFromStore(imageStore, page.blobId);
|
|
1407
1404
|
const naturalTopY2 = worldCenter.y - page.height / 2;
|
|
1408
|
-
const
|
|
1405
|
+
const stackedTopY2 = occupiedBottomY == null ? naturalTopY2 : occupiedBottomY + gapWorld;
|
|
1409
1406
|
const bounds2 = {
|
|
1410
1407
|
x: worldCenter.x - page.width / 2,
|
|
1411
|
-
y: Math.max(naturalTopY2,
|
|
1408
|
+
y: Math.max(naturalTopY2, stackedTopY2),
|
|
1412
1409
|
width: page.width,
|
|
1413
1410
|
height: page.height
|
|
1414
1411
|
};
|
|
@@ -1446,9 +1443,6 @@ async function ingestAssetFilesToSceneItems(options) {
|
|
|
1446
1443
|
onItemsReady?.([item2], { file, kind });
|
|
1447
1444
|
}
|
|
1448
1445
|
});
|
|
1449
|
-
hasImagePlacementBase = false;
|
|
1450
|
-
imagePlacementIndex = 0;
|
|
1451
|
-
imageYOffsetAdjustment = 0;
|
|
1452
1446
|
continue;
|
|
1453
1447
|
}
|
|
1454
1448
|
const [uploadResult, storedImage] = await Promise.all([
|
|
@@ -1459,17 +1453,11 @@ async function ingestAssetFilesToSceneItems(options) {
|
|
|
1459
1453
|
const fullUrl = await createBlobUrlFromStore(imageStore, blobId);
|
|
1460
1454
|
const thumbBlob = await imageStore.getThumbnail(thumbnailBlobId);
|
|
1461
1455
|
const thumbnailHref = thumbBlob ? URL.createObjectURL(thumbBlob) : null;
|
|
1462
|
-
const
|
|
1463
|
-
const
|
|
1464
|
-
const naturalTopY = worldCenter.y - height / 2 + oy;
|
|
1465
|
-
if (!hasImagePlacementBase) {
|
|
1466
|
-
const minimumTopY = occupiedBottomY == null ? naturalTopY : occupiedBottomY + gapWorld;
|
|
1467
|
-
imageYOffsetAdjustment = Math.max(0, minimumTopY - naturalTopY);
|
|
1468
|
-
hasImagePlacementBase = true;
|
|
1469
|
-
}
|
|
1456
|
+
const naturalTopY = worldCenter.y - height / 2;
|
|
1457
|
+
const stackedTopY = occupiedBottomY == null ? naturalTopY : occupiedBottomY + gapWorld;
|
|
1470
1458
|
const bounds = {
|
|
1471
|
-
x: worldCenter.x - width / 2
|
|
1472
|
-
y: naturalTopY
|
|
1459
|
+
x: worldCenter.x - width / 2,
|
|
1460
|
+
y: Math.max(naturalTopY, stackedTopY),
|
|
1473
1461
|
width,
|
|
1474
1462
|
height
|
|
1475
1463
|
};
|
|
@@ -1499,7 +1487,6 @@ async function ingestAssetFilesToSceneItems(options) {
|
|
|
1499
1487
|
);
|
|
1500
1488
|
items.push(item);
|
|
1501
1489
|
onItemsReady?.([item], { file, kind });
|
|
1502
|
-
imagePlacementIndex++;
|
|
1503
1490
|
occupiedBottomY = occupiedBottomY == null ? bounds.y + height : Math.max(occupiedBottomY, bounds.y + height);
|
|
1504
1491
|
} catch (error) {
|
|
1505
1492
|
const fileError = {
|
|
@@ -1686,11 +1673,7 @@ function VectorSelectionInspector({
|
|
|
1686
1673
|
...activeToolStyle.strokeOpacity != null ? { strokeOpacity: activeToolStyle.strokeOpacity } : {}
|
|
1687
1674
|
})
|
|
1688
1675
|
}
|
|
1689
|
-
)
|
|
1690
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontWeight: 500, color: "#71717a" }, children: [
|
|
1691
|
-
strokeWidth2,
|
|
1692
|
-
"px"
|
|
1693
|
-
] })
|
|
1676
|
+
)
|
|
1694
1677
|
] }),
|
|
1695
1678
|
showMarkerOpacity2 && /* @__PURE__ */ jsxRuntime.jsxs("label", { style: labelStyle, children: [
|
|
1696
1679
|
"Opacidade (marcador)",
|
|
@@ -1796,11 +1779,7 @@ function VectorSelectionInspector({
|
|
|
1796
1779
|
strokeWidth: Number(e.target.value)
|
|
1797
1780
|
})
|
|
1798
1781
|
}
|
|
1799
|
-
)
|
|
1800
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontWeight: 500, color: "#71717a" }, children: [
|
|
1801
|
-
strokeWidth,
|
|
1802
|
-
"px"
|
|
1803
|
-
] })
|
|
1782
|
+
)
|
|
1804
1783
|
] }),
|
|
1805
1784
|
showMarkerOpacity && firstMarker && /* @__PURE__ */ jsxRuntime.jsxs("label", { style: labelStyle, children: [
|
|
1806
1785
|
"Opacidade (marcador)",
|
|
@@ -6010,7 +5989,7 @@ body[data-canvu-pen-active="true"] [data-slot="shape-context-menu"] * {
|
|
|
6010
5989
|
}
|
|
6011
5990
|
`;
|
|
6012
5991
|
function debugApplePencilPointer(phase, detail) {
|
|
6013
|
-
|
|
5992
|
+
return;
|
|
6014
5993
|
}
|
|
6015
5994
|
var MARKER_TOOL_STYLE = {
|
|
6016
5995
|
stroke: "#fde047",
|
|
@@ -6610,7 +6589,6 @@ var VectorViewport = react.forwardRef(
|
|
|
6610
6589
|
const captureInteractionPointer = react.useCallback(
|
|
6611
6590
|
(target, pointerId, options) => {
|
|
6612
6591
|
const captureNativePointer = options?.captureNativePointer ?? true;
|
|
6613
|
-
debugApplePencilPointer("capture", { pointerId, captureNativePointer });
|
|
6614
6592
|
activeInteractionPointerIdRef.current = pointerId;
|
|
6615
6593
|
activeInteractionPointerTargetRef.current = captureNativePointer ? target : null;
|
|
6616
6594
|
if (captureNativePointer) {
|
|
@@ -6622,7 +6600,6 @@ var VectorViewport = react.forwardRef(
|
|
|
6622
6600
|
const releaseInteractionPointer = react.useCallback(() => {
|
|
6623
6601
|
const pointerId = activeInteractionPointerIdRef.current;
|
|
6624
6602
|
const target = activeInteractionPointerTargetRef.current;
|
|
6625
|
-
debugApplePencilPointer("release", { pointerId });
|
|
6626
6603
|
if (pointerId != null && target) {
|
|
6627
6604
|
try {
|
|
6628
6605
|
if (target.hasPointerCapture(pointerId)) {
|
|
@@ -6655,8 +6632,6 @@ var VectorViewport = react.forwardRef(
|
|
|
6655
6632
|
const itemId = strokeState.itemId;
|
|
6656
6633
|
const style = { ...strokeStyleRef.current };
|
|
6657
6634
|
debugApplePencilPointer("finalize-stroke", {
|
|
6658
|
-
pointerType,
|
|
6659
|
-
tool,
|
|
6660
6635
|
points: pts.length
|
|
6661
6636
|
});
|
|
6662
6637
|
dragStateRef.current = { kind: "idle" };
|
|
@@ -7406,6 +7381,7 @@ var VectorViewport = react.forwardRef(
|
|
|
7406
7381
|
}
|
|
7407
7382
|
const result = await ingestAssetFilesToSceneItems({
|
|
7408
7383
|
files,
|
|
7384
|
+
existingItems: itemsRef.current,
|
|
7409
7385
|
worldCenter: {
|
|
7410
7386
|
x: worldX,
|
|
7411
7387
|
y: worldY
|
|
@@ -7433,12 +7409,12 @@ var VectorViewport = react.forwardRef(
|
|
|
7433
7409
|
);
|
|
7434
7410
|
const handleImageFileChange = react.useCallback(
|
|
7435
7411
|
async (e) => {
|
|
7436
|
-
const
|
|
7412
|
+
const files = e.target.files ? Array.from(e.target.files) : [];
|
|
7437
7413
|
e.target.value = "";
|
|
7438
7414
|
const pending = pendingImagePlacementRef.current;
|
|
7439
7415
|
pendingImagePlacementRef.current = null;
|
|
7440
|
-
if (
|
|
7441
|
-
await placeImageFilesAtWorld(
|
|
7416
|
+
if (files.length === 0 || !pending) return;
|
|
7417
|
+
await placeImageFilesAtWorld(files, pending.worldX, pending.worldY);
|
|
7442
7418
|
},
|
|
7443
7419
|
[placeImageFilesAtWorld]
|
|
7444
7420
|
);
|
|
@@ -7534,14 +7510,12 @@ var VectorViewport = react.forwardRef(
|
|
|
7534
7510
|
pointerType: e.pointerType,
|
|
7535
7511
|
pointerId: e.pointerId,
|
|
7536
7512
|
pressure: e.pointerType === "pen" ? e.pressure : void 0,
|
|
7537
|
-
activePointerId,
|
|
7538
7513
|
dragKind: currentDragState.kind
|
|
7539
7514
|
});
|
|
7540
7515
|
}
|
|
7541
7516
|
if (e.pointerType === "pen" && currentDragState.kind === "stroke" && currentDragState.pointerType === "pen") {
|
|
7542
7517
|
debugApplePencilPointer("pen-reentry", {
|
|
7543
7518
|
pointerId: e.pointerId,
|
|
7544
|
-
previousPointerId: activePointerId,
|
|
7545
7519
|
previousPoints: currentDragState.points.length
|
|
7546
7520
|
});
|
|
7547
7521
|
finalizeStrokeDragState(currentDragState);
|
|
@@ -7706,10 +7680,10 @@ var VectorViewport = react.forwardRef(
|
|
|
7706
7680
|
return;
|
|
7707
7681
|
}
|
|
7708
7682
|
if (!e.shiftKey && cur.length > 0) {
|
|
7709
|
-
const
|
|
7710
|
-
(it) => it != null && !it.locked
|
|
7683
|
+
const selectedUnlockedItems = cur.map((id) => resolved.find((it) => it.id === id)).filter(
|
|
7684
|
+
(it) => it != null && !it.locked
|
|
7711
7685
|
);
|
|
7712
|
-
if (
|
|
7686
|
+
if (selectedUnlockedItems.some(
|
|
7713
7687
|
(it) => pointInSelectedItemBounds(it, worldX, worldY)
|
|
7714
7688
|
)) {
|
|
7715
7689
|
const snapshots = {};
|
|
@@ -7887,9 +7861,7 @@ var VectorViewport = react.forwardRef(
|
|
|
7887
7861
|
debugApplePencilPointer("native-pointerdown", {
|
|
7888
7862
|
pointerType: e.pointerType,
|
|
7889
7863
|
pointerId: e.pointerId,
|
|
7890
|
-
pressure: e.pressure
|
|
7891
|
-
tool
|
|
7892
|
-
});
|
|
7864
|
+
pressure: e.pressure});
|
|
7893
7865
|
e.preventDefault();
|
|
7894
7866
|
e.stopImmediatePropagation();
|
|
7895
7867
|
};
|
|
@@ -7989,9 +7961,7 @@ var VectorViewport = react.forwardRef(
|
|
|
7989
7961
|
debugApplePencilPointer("touchstart-stroke", {
|
|
7990
7962
|
touchId: touch.identifier,
|
|
7991
7963
|
touchType: touchKind(touch),
|
|
7992
|
-
force: touchPressure(touch)
|
|
7993
|
-
tool
|
|
7994
|
-
});
|
|
7964
|
+
force: touchPressure(touch)});
|
|
7995
7965
|
stopTouchEvent(ev);
|
|
7996
7966
|
};
|
|
7997
7967
|
const onTouchMove = (ev) => {
|
|
@@ -8713,6 +8683,7 @@ var VectorViewport = react.forwardRef(
|
|
|
8713
8683
|
{
|
|
8714
8684
|
ref: imageInputRef,
|
|
8715
8685
|
type: "file",
|
|
8686
|
+
multiple: true,
|
|
8716
8687
|
accept: "image/*,application/pdf",
|
|
8717
8688
|
style: { display: "none" },
|
|
8718
8689
|
"aria-label": "Select image or PDF to place on canvas",
|