canvu-react 0.4.12 → 0.4.14

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/react.js CHANGED
@@ -3648,7 +3648,7 @@ function ColorPalette({ value, onChange }) {
3648
3648
  "button",
3649
3649
  {
3650
3650
  type: "button",
3651
- "aria-label": "Cor personalizada",
3651
+ "aria-label": "Custom color",
3652
3652
  "aria-pressed": showCustom || !inPalette,
3653
3653
  onClick: () => setShowCustom((v) => !v),
3654
3654
  onPointerDown: (e) => {
@@ -3709,14 +3709,14 @@ function ColorPalette({ value, onChange }) {
3709
3709
  }
3710
3710
  function DashTabs({ value, onChange }) {
3711
3711
  const tabs = [
3712
- { id: "solid", label: "Cont\xEDnuo" },
3713
- { id: "dashed", label: "Tracejado" }
3712
+ { id: "solid", label: "Solid" },
3713
+ { id: "dashed", label: "Dashed" }
3714
3714
  ];
3715
3715
  return /* @__PURE__ */ jsx(
3716
3716
  "div",
3717
3717
  {
3718
3718
  role: "radiogroup",
3719
- "aria-label": "Estilo do tra\xE7o",
3719
+ "aria-label": "Stroke style",
3720
3720
  style: {
3721
3721
  display: "flex",
3722
3722
  background: "rgba(24,24,27,0.06)",
@@ -3799,7 +3799,7 @@ function FontSizeTabs({ value, onChange }) {
3799
3799
  "div",
3800
3800
  {
3801
3801
  role: "radiogroup",
3802
- "aria-label": "Tamanho do texto",
3802
+ "aria-label": "Text size",
3803
3803
  style: {
3804
3804
  display: "flex",
3805
3805
  background: "rgba(24,24,27,0.06)",
@@ -3863,7 +3863,7 @@ function viewModelFromActiveTool(activeToolStyle) {
3863
3863
  ...isText ? { textFontSize } : {}
3864
3864
  };
3865
3865
  return {
3866
- label: activeToolStyle.label ?? "Estilo da ferramenta",
3866
+ label: activeToolStyle.label ?? "Tool style",
3867
3867
  count: 0,
3868
3868
  hex,
3869
3869
  strokeWidth,
@@ -3922,7 +3922,7 @@ function viewModelFromSelection(stylable) {
3922
3922
  ...showFontSize ? { textFontSize } : {}
3923
3923
  };
3924
3924
  return {
3925
- label: "Estilo da sele\xE7\xE3o",
3925
+ label: "Selection style",
3926
3926
  count: stylable.length,
3927
3927
  hex,
3928
3928
  strokeWidth,
@@ -3985,18 +3985,18 @@ function VectorSelectionInspector({
3985
3985
  children: [
3986
3986
  vm.count > 1 ? /* @__PURE__ */ jsxs("p", { style: { margin: 0, fontSize: 11, color: "#71717a" }, children: [
3987
3987
  vm.count,
3988
- " objetos selecionados"
3988
+ " objects selected"
3989
3989
  ] }) : null,
3990
- /* @__PURE__ */ jsxs("div", { role: "group", "aria-label": "Cor", style: sectionLabelStyle, children: [
3990
+ /* @__PURE__ */ jsxs("div", { role: "group", "aria-label": "Color", style: sectionLabelStyle, children: [
3991
3991
  /* @__PURE__ */ jsxs("span", { children: [
3992
- "Cor",
3993
- vm.mixedStroke ? /* @__PURE__ */ jsx("span", { style: mixedHintStyle, children: " (valores misturados)" }) : null
3992
+ "Color",
3993
+ vm.mixedStroke ? /* @__PURE__ */ jsx("span", { style: mixedHintStyle, children: " (mixed values)" }) : null
3994
3994
  ] }),
3995
3995
  /* @__PURE__ */ jsx(ColorPalette, { value: vm.hex, onChange: (h) => apply({ stroke: h }) })
3996
3996
  ] }),
3997
3997
  vm.showStrokeWidth ? /* @__PURE__ */ jsxs("label", { style: sectionLabelStyle, children: [
3998
- "Grossura",
3999
- vm.mixedWidth ? /* @__PURE__ */ jsx("span", { style: mixedHintStyle, children: " (misturado)" }) : null,
3998
+ "Thickness",
3999
+ vm.mixedWidth ? /* @__PURE__ */ jsx("span", { style: mixedHintStyle, children: " (mixed)" }) : null,
4000
4000
  /* @__PURE__ */ jsx(
4001
4001
  "input",
4002
4002
  {
@@ -4010,10 +4010,10 @@ function VectorSelectionInspector({
4010
4010
  ] }) : null,
4011
4011
  vm.showDash ? (
4012
4012
  // biome-ignore lint/a11y/useSemanticElements: fieldset would impose default browser styling that breaks the inspector layout
4013
- /* @__PURE__ */ jsxs("div", { role: "group", "aria-label": "Tra\xE7o", style: sectionLabelStyle, children: [
4013
+ /* @__PURE__ */ jsxs("div", { role: "group", "aria-label": "Stroke", style: sectionLabelStyle, children: [
4014
4014
  /* @__PURE__ */ jsxs("span", { children: [
4015
- "Tra\xE7o",
4016
- vm.mixedDash ? /* @__PURE__ */ jsx("span", { style: mixedHintStyle, children: " (misturado)" }) : null
4015
+ "Stroke",
4016
+ vm.mixedDash ? /* @__PURE__ */ jsx("span", { style: mixedHintStyle, children: " (mixed)" }) : null
4017
4017
  ] }),
4018
4018
  /* @__PURE__ */ jsx(
4019
4019
  DashTabs,
@@ -4026,10 +4026,10 @@ function VectorSelectionInspector({
4026
4026
  ) : null,
4027
4027
  vm.showFontSize ? (
4028
4028
  // biome-ignore lint/a11y/useSemanticElements: fieldset would impose default browser styling that breaks the inspector layout
4029
- /* @__PURE__ */ jsxs("div", { role: "group", "aria-label": "Tamanho", style: sectionLabelStyle, children: [
4029
+ /* @__PURE__ */ jsxs("div", { role: "group", "aria-label": "Size", style: sectionLabelStyle, children: [
4030
4030
  /* @__PURE__ */ jsxs("span", { children: [
4031
- "Tamanho",
4032
- vm.mixedFontSize ? /* @__PURE__ */ jsx("span", { style: mixedHintStyle, children: " (misturado)" }) : null
4031
+ "Size",
4032
+ vm.mixedFontSize ? /* @__PURE__ */ jsx("span", { style: mixedHintStyle, children: " (mixed)" }) : null
4033
4033
  ] }),
4034
4034
  /* @__PURE__ */ jsx(
4035
4035
  FontSizeTabs,
@@ -4041,8 +4041,8 @@ function VectorSelectionInspector({
4041
4041
  ] })
4042
4042
  ) : null,
4043
4043
  vm.showMarkerOpacity ? /* @__PURE__ */ jsxs("label", { style: sectionLabelStyle, children: [
4044
- "Opacidade",
4045
- vm.mixedOpacity ? /* @__PURE__ */ jsx("span", { style: mixedHintStyle, children: " (misturado)" }) : null,
4044
+ "Opacity",
4045
+ vm.mixedOpacity ? /* @__PURE__ */ jsx("span", { style: mixedHintStyle, children: " (mixed)" }) : null,
4046
4046
  /* @__PURE__ */ jsx(
4047
4047
  "input",
4048
4048
  {
@@ -8043,6 +8043,7 @@ var VectorViewport = forwardRef(
8043
8043
  const wrapperRef = useRef(null);
8044
8044
  const interactionRootRef = useRef(null);
8045
8045
  const sceneContainerRef = useRef(null);
8046
+ const lastPointerScreenRef = useRef(null);
8046
8047
  const activeInteractionPointerIdRef = useRef(null);
8047
8048
  const activeInteractionPointerTargetRef = useRef(null);
8048
8049
  const activeInteractionTouchIdRef = useRef(null);
@@ -8769,6 +8770,24 @@ var VectorViewport = forwardRef(
8769
8770
  root.removeEventListener("pointerleave", leave);
8770
8771
  };
8771
8772
  }, [onWorldPointerMove]);
8773
+ useEffect(() => {
8774
+ const root = wrapperRef.current;
8775
+ if (!root) return;
8776
+ const record = (e) => {
8777
+ lastPointerScreenRef.current = { x: e.clientX, y: e.clientY };
8778
+ };
8779
+ const clear = () => {
8780
+ lastPointerScreenRef.current = null;
8781
+ };
8782
+ root.addEventListener("pointermove", record, { passive: true });
8783
+ root.addEventListener("pointerdown", record, { passive: true });
8784
+ root.addEventListener("pointerleave", clear);
8785
+ return () => {
8786
+ root.removeEventListener("pointermove", record);
8787
+ root.removeEventListener("pointerdown", record);
8788
+ root.removeEventListener("pointerleave", clear);
8789
+ };
8790
+ }, []);
8772
8791
  useEffect(() => {
8773
8792
  if (directRemoteStrokePreviewRef.current && placementPreview === null) {
8774
8793
  return;
@@ -9488,6 +9507,50 @@ var VectorViewport = forwardRef(
9488
9507
  },
9489
9508
  [screenToWorld, placeImageFilesAtWorld]
9490
9509
  );
9510
+ const handleWrapperPaste = useCallback(
9511
+ async (e) => {
9512
+ if (!interactiveRef.current || !onItemsChangeRef.current) return;
9513
+ const t = e.target;
9514
+ if (t instanceof HTMLTextAreaElement || t instanceof HTMLInputElement || t instanceof HTMLElement && t.isContentEditable) {
9515
+ return;
9516
+ }
9517
+ if (editingTextIdRef.current) return;
9518
+ const dt = e.clipboardData;
9519
+ if (!dt) return;
9520
+ const files = [];
9521
+ for (const file of Array.from(dt.files)) {
9522
+ if (file.type.startsWith("image/")) files.push(file);
9523
+ }
9524
+ if (files.length === 0) {
9525
+ for (const item of Array.from(dt.items)) {
9526
+ if (item.kind === "file" && item.type.startsWith("image/")) {
9527
+ const file = item.getAsFile();
9528
+ if (file) files.push(file);
9529
+ }
9530
+ }
9531
+ }
9532
+ if (files.length === 0) return;
9533
+ e.preventDefault();
9534
+ const last = lastPointerScreenRef.current;
9535
+ let target;
9536
+ if (last) {
9537
+ target = screenToWorld(last.x, last.y);
9538
+ } else {
9539
+ const el = sceneContainerRef.current;
9540
+ const cam = cameraRef.current;
9541
+ if (el && cam) {
9542
+ const rect = el.getBoundingClientRect();
9543
+ target = cam.screenToWorld(rect.width / 2, rect.height / 2);
9544
+ } else {
9545
+ target = { worldX: 0, worldY: 0 };
9546
+ }
9547
+ }
9548
+ await placeImageFilesAtWorld(files, target.worldX, target.worldY, {
9549
+ stackBelowExistingItems: false
9550
+ });
9551
+ },
9552
+ [screenToWorld, placeImageFilesAtWorld]
9553
+ );
9491
9554
  const handleOverlayDoubleClick = useCallback(
9492
9555
  (e) => {
9493
9556
  const cam = cameraRef.current;
@@ -10757,6 +10820,7 @@ var VectorViewport = forwardRef(
10757
10820
  "aria-label": ariaLabel,
10758
10821
  "aria-describedby": liveId,
10759
10822
  onKeyDown,
10823
+ onPaste: handleWrapperPaste,
10760
10824
  onDragOver: handleWrapperDragOver,
10761
10825
  onDrop: handleWrapperDrop,
10762
10826
  children: [