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.cjs CHANGED
@@ -3655,7 +3655,7 @@ function ColorPalette({ value, onChange }) {
3655
3655
  "button",
3656
3656
  {
3657
3657
  type: "button",
3658
- "aria-label": "Cor personalizada",
3658
+ "aria-label": "Custom color",
3659
3659
  "aria-pressed": showCustom || !inPalette,
3660
3660
  onClick: () => setShowCustom((v) => !v),
3661
3661
  onPointerDown: (e) => {
@@ -3716,14 +3716,14 @@ function ColorPalette({ value, onChange }) {
3716
3716
  }
3717
3717
  function DashTabs({ value, onChange }) {
3718
3718
  const tabs = [
3719
- { id: "solid", label: "Cont\xEDnuo" },
3720
- { id: "dashed", label: "Tracejado" }
3719
+ { id: "solid", label: "Solid" },
3720
+ { id: "dashed", label: "Dashed" }
3721
3721
  ];
3722
3722
  return /* @__PURE__ */ jsxRuntime.jsx(
3723
3723
  "div",
3724
3724
  {
3725
3725
  role: "radiogroup",
3726
- "aria-label": "Estilo do tra\xE7o",
3726
+ "aria-label": "Stroke style",
3727
3727
  style: {
3728
3728
  display: "flex",
3729
3729
  background: "rgba(24,24,27,0.06)",
@@ -3806,7 +3806,7 @@ function FontSizeTabs({ value, onChange }) {
3806
3806
  "div",
3807
3807
  {
3808
3808
  role: "radiogroup",
3809
- "aria-label": "Tamanho do texto",
3809
+ "aria-label": "Text size",
3810
3810
  style: {
3811
3811
  display: "flex",
3812
3812
  background: "rgba(24,24,27,0.06)",
@@ -3870,7 +3870,7 @@ function viewModelFromActiveTool(activeToolStyle) {
3870
3870
  ...isText ? { textFontSize } : {}
3871
3871
  };
3872
3872
  return {
3873
- label: activeToolStyle.label ?? "Estilo da ferramenta",
3873
+ label: activeToolStyle.label ?? "Tool style",
3874
3874
  count: 0,
3875
3875
  hex,
3876
3876
  strokeWidth,
@@ -3929,7 +3929,7 @@ function viewModelFromSelection(stylable) {
3929
3929
  ...showFontSize ? { textFontSize } : {}
3930
3930
  };
3931
3931
  return {
3932
- label: "Estilo da sele\xE7\xE3o",
3932
+ label: "Selection style",
3933
3933
  count: stylable.length,
3934
3934
  hex,
3935
3935
  strokeWidth,
@@ -3992,18 +3992,18 @@ function VectorSelectionInspector({
3992
3992
  children: [
3993
3993
  vm.count > 1 ? /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { margin: 0, fontSize: 11, color: "#71717a" }, children: [
3994
3994
  vm.count,
3995
- " objetos selecionados"
3995
+ " objects selected"
3996
3996
  ] }) : null,
3997
- /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "group", "aria-label": "Cor", style: sectionLabelStyle, children: [
3997
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "group", "aria-label": "Color", style: sectionLabelStyle, children: [
3998
3998
  /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
3999
- "Cor",
4000
- vm.mixedStroke ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: mixedHintStyle, children: " (valores misturados)" }) : null
3999
+ "Color",
4000
+ vm.mixedStroke ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: mixedHintStyle, children: " (mixed values)" }) : null
4001
4001
  ] }),
4002
4002
  /* @__PURE__ */ jsxRuntime.jsx(ColorPalette, { value: vm.hex, onChange: (h) => apply({ stroke: h }) })
4003
4003
  ] }),
4004
4004
  vm.showStrokeWidth ? /* @__PURE__ */ jsxRuntime.jsxs("label", { style: sectionLabelStyle, children: [
4005
- "Grossura",
4006
- vm.mixedWidth ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: mixedHintStyle, children: " (misturado)" }) : null,
4005
+ "Thickness",
4006
+ vm.mixedWidth ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: mixedHintStyle, children: " (mixed)" }) : null,
4007
4007
  /* @__PURE__ */ jsxRuntime.jsx(
4008
4008
  "input",
4009
4009
  {
@@ -4017,10 +4017,10 @@ function VectorSelectionInspector({
4017
4017
  ] }) : null,
4018
4018
  vm.showDash ? (
4019
4019
  // biome-ignore lint/a11y/useSemanticElements: fieldset would impose default browser styling that breaks the inspector layout
4020
- /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "group", "aria-label": "Tra\xE7o", style: sectionLabelStyle, children: [
4020
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "group", "aria-label": "Stroke", style: sectionLabelStyle, children: [
4021
4021
  /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
4022
- "Tra\xE7o",
4023
- vm.mixedDash ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: mixedHintStyle, children: " (misturado)" }) : null
4022
+ "Stroke",
4023
+ vm.mixedDash ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: mixedHintStyle, children: " (mixed)" }) : null
4024
4024
  ] }),
4025
4025
  /* @__PURE__ */ jsxRuntime.jsx(
4026
4026
  DashTabs,
@@ -4033,10 +4033,10 @@ function VectorSelectionInspector({
4033
4033
  ) : null,
4034
4034
  vm.showFontSize ? (
4035
4035
  // biome-ignore lint/a11y/useSemanticElements: fieldset would impose default browser styling that breaks the inspector layout
4036
- /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "group", "aria-label": "Tamanho", style: sectionLabelStyle, children: [
4036
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "group", "aria-label": "Size", style: sectionLabelStyle, children: [
4037
4037
  /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
4038
- "Tamanho",
4039
- vm.mixedFontSize ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: mixedHintStyle, children: " (misturado)" }) : null
4038
+ "Size",
4039
+ vm.mixedFontSize ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: mixedHintStyle, children: " (mixed)" }) : null
4040
4040
  ] }),
4041
4041
  /* @__PURE__ */ jsxRuntime.jsx(
4042
4042
  FontSizeTabs,
@@ -4048,8 +4048,8 @@ function VectorSelectionInspector({
4048
4048
  ] })
4049
4049
  ) : null,
4050
4050
  vm.showMarkerOpacity ? /* @__PURE__ */ jsxRuntime.jsxs("label", { style: sectionLabelStyle, children: [
4051
- "Opacidade",
4052
- vm.mixedOpacity ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: mixedHintStyle, children: " (misturado)" }) : null,
4051
+ "Opacity",
4052
+ vm.mixedOpacity ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: mixedHintStyle, children: " (mixed)" }) : null,
4053
4053
  /* @__PURE__ */ jsxRuntime.jsx(
4054
4054
  "input",
4055
4055
  {
@@ -8050,6 +8050,7 @@ var VectorViewport = react.forwardRef(
8050
8050
  const wrapperRef = react.useRef(null);
8051
8051
  const interactionRootRef = react.useRef(null);
8052
8052
  const sceneContainerRef = react.useRef(null);
8053
+ const lastPointerScreenRef = react.useRef(null);
8053
8054
  const activeInteractionPointerIdRef = react.useRef(null);
8054
8055
  const activeInteractionPointerTargetRef = react.useRef(null);
8055
8056
  const activeInteractionTouchIdRef = react.useRef(null);
@@ -8776,6 +8777,24 @@ var VectorViewport = react.forwardRef(
8776
8777
  root.removeEventListener("pointerleave", leave);
8777
8778
  };
8778
8779
  }, [onWorldPointerMove]);
8780
+ react.useEffect(() => {
8781
+ const root = wrapperRef.current;
8782
+ if (!root) return;
8783
+ const record = (e) => {
8784
+ lastPointerScreenRef.current = { x: e.clientX, y: e.clientY };
8785
+ };
8786
+ const clear = () => {
8787
+ lastPointerScreenRef.current = null;
8788
+ };
8789
+ root.addEventListener("pointermove", record, { passive: true });
8790
+ root.addEventListener("pointerdown", record, { passive: true });
8791
+ root.addEventListener("pointerleave", clear);
8792
+ return () => {
8793
+ root.removeEventListener("pointermove", record);
8794
+ root.removeEventListener("pointerdown", record);
8795
+ root.removeEventListener("pointerleave", clear);
8796
+ };
8797
+ }, []);
8779
8798
  react.useEffect(() => {
8780
8799
  if (directRemoteStrokePreviewRef.current && placementPreview === null) {
8781
8800
  return;
@@ -9495,6 +9514,50 @@ var VectorViewport = react.forwardRef(
9495
9514
  },
9496
9515
  [screenToWorld, placeImageFilesAtWorld]
9497
9516
  );
9517
+ const handleWrapperPaste = react.useCallback(
9518
+ async (e) => {
9519
+ if (!interactiveRef.current || !onItemsChangeRef.current) return;
9520
+ const t = e.target;
9521
+ if (t instanceof HTMLTextAreaElement || t instanceof HTMLInputElement || t instanceof HTMLElement && t.isContentEditable) {
9522
+ return;
9523
+ }
9524
+ if (editingTextIdRef.current) return;
9525
+ const dt = e.clipboardData;
9526
+ if (!dt) return;
9527
+ const files = [];
9528
+ for (const file of Array.from(dt.files)) {
9529
+ if (file.type.startsWith("image/")) files.push(file);
9530
+ }
9531
+ if (files.length === 0) {
9532
+ for (const item of Array.from(dt.items)) {
9533
+ if (item.kind === "file" && item.type.startsWith("image/")) {
9534
+ const file = item.getAsFile();
9535
+ if (file) files.push(file);
9536
+ }
9537
+ }
9538
+ }
9539
+ if (files.length === 0) return;
9540
+ e.preventDefault();
9541
+ const last = lastPointerScreenRef.current;
9542
+ let target;
9543
+ if (last) {
9544
+ target = screenToWorld(last.x, last.y);
9545
+ } else {
9546
+ const el = sceneContainerRef.current;
9547
+ const cam = cameraRef.current;
9548
+ if (el && cam) {
9549
+ const rect = el.getBoundingClientRect();
9550
+ target = cam.screenToWorld(rect.width / 2, rect.height / 2);
9551
+ } else {
9552
+ target = { worldX: 0, worldY: 0 };
9553
+ }
9554
+ }
9555
+ await placeImageFilesAtWorld(files, target.worldX, target.worldY, {
9556
+ stackBelowExistingItems: false
9557
+ });
9558
+ },
9559
+ [screenToWorld, placeImageFilesAtWorld]
9560
+ );
9498
9561
  const handleOverlayDoubleClick = react.useCallback(
9499
9562
  (e) => {
9500
9563
  const cam = cameraRef.current;
@@ -10764,6 +10827,7 @@ var VectorViewport = react.forwardRef(
10764
10827
  "aria-label": ariaLabel,
10765
10828
  "aria-describedby": liveId,
10766
10829
  onKeyDown,
10830
+ onPaste: handleWrapperPaste,
10767
10831
  onDragOver: handleWrapperDragOver,
10768
10832
  onDrop: handleWrapperDrop,
10769
10833
  children: [