canvu-react 0.4.36 → 0.4.37

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/native.cjs CHANGED
@@ -1469,6 +1469,12 @@ function linkInitial(value) {
1469
1469
  const first = value.trim().charAt(0).toUpperCase();
1470
1470
  return first || "L";
1471
1471
  }
1472
+ function normalizeNativeLinkHref(value) {
1473
+ const trimmed = value.trim();
1474
+ if (!trimmed) return null;
1475
+ if (/^[a-z][a-z0-9+.-]*:/i.test(trimmed)) return trimmed;
1476
+ return `https://${trimmed}`;
1477
+ }
1472
1478
  function buildNativeLinkCardDisplay(link) {
1473
1479
  const hostname = linkHostname(link.href);
1474
1480
  const title = link.title?.trim() || hostname || "Link";
@@ -4657,6 +4663,13 @@ function resizeItemByHandle(item, start, handle, currentWorld) {
4657
4663
  }
4658
4664
  return { ...item, x: nb.x, y: nb.y, bounds: nb };
4659
4665
  }
4666
+ var DEFAULT_NATIVE_LINK_TOOL_DIALOG_LABELS = {
4667
+ title: "Add link",
4668
+ description: "Paste the link you want to add to the board.",
4669
+ inputPlaceholder: "https://example.com",
4670
+ cancelLabel: "Cancel",
4671
+ addLabel: "Add"
4672
+ };
4660
4673
  var MIN_PLACE_SIZE = 8;
4661
4674
  var MIN_ARROW_DRAG_PX = 8;
4662
4675
  var TAP_PX = 20;
@@ -4740,6 +4753,7 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
4740
4753
  onItemsChange,
4741
4754
  onToolChangeRequest,
4742
4755
  onLinkToolRequest,
4756
+ linkToolDialogLabels,
4743
4757
  onWorldPointerDown,
4744
4758
  onWorldPointerMove,
4745
4759
  onWorldPointerLeave,
@@ -4794,6 +4808,8 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
4794
4808
  );
4795
4809
  const [eraserTrail, setEraserTrail] = react.useState([]);
4796
4810
  const [laserTrail, setLaserTrail] = react.useState([]);
4811
+ const [pendingNativeLinkRequest, setPendingNativeLinkRequest] = react.useState(null);
4812
+ const [nativeLinkInputValue, setNativeLinkInputValue] = react.useState("");
4797
4813
  const laserClearTimerRef = react.useRef(null);
4798
4814
  const strokeStyleRef = react.useRef({ ...DEFAULT_STROKE_STYLE });
4799
4815
  const markerStrokeStyleRef = react.useRef({ ...MARKER_TOOL_STYLE });
@@ -4871,6 +4887,21 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
4871
4887
  onToolChangeRequestRef.current?.("select");
4872
4888
  }
4873
4889
  }, []);
4890
+ const requestSelectToolAfterNativeLinkUse = react.useCallback(() => {
4891
+ onToolChangeRequestRef.current?.("select");
4892
+ }, []);
4893
+ const closeNativeLinkDialog = react.useCallback(() => {
4894
+ setPendingNativeLinkRequest(null);
4895
+ setNativeLinkInputValue("");
4896
+ }, []);
4897
+ const submitNativeLinkDialog = react.useCallback(() => {
4898
+ const href = normalizeNativeLinkHref(nativeLinkInputValue);
4899
+ if (!href || !pendingNativeLinkRequest) return;
4900
+ const inserted = pendingNativeLinkRequest.insertLink({ href });
4901
+ if (inserted) {
4902
+ closeNativeLinkDialog();
4903
+ }
4904
+ }, [closeNativeLinkDialog, nativeLinkInputValue, pendingNativeLinkRequest]);
4874
4905
  if (!cameraRef.current) {
4875
4906
  cameraRef.current = new Camera2D({ minZoom: 0.05, maxZoom: 32 });
4876
4907
  }
@@ -5448,7 +5479,7 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
5448
5479
  const item = createLinkItem(id, options.bounds ?? suggestedBounds, link);
5449
5480
  currentChange([...itemsRef.current, item]);
5450
5481
  onSelectionChangeRef.current?.([id]);
5451
- requestSelectToolAfterUse();
5482
+ requestSelectToolAfterNativeLinkUse();
5452
5483
  return item;
5453
5484
  };
5454
5485
  const requestLink = onLinkToolRequestRef.current;
@@ -5464,17 +5495,16 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
5464
5495
  });
5465
5496
  return;
5466
5497
  }
5467
- const handleWorldPointerDown = onWorldPointerDownRef.current;
5468
- if (handleWorldPointerDown) {
5469
- handleWorldPointerDown({
5470
- toolId: "link",
5471
- worldX: st.startWorld.x,
5472
- worldY: st.startWorld.y,
5473
- screenX: st.startScreen.x,
5474
- screenY: st.startScreen.y
5475
- });
5476
- requestSelectToolAfterUse();
5477
- }
5498
+ setNativeLinkInputValue("");
5499
+ setPendingNativeLinkRequest({
5500
+ toolId: "link",
5501
+ worldX: st.startWorld.x,
5502
+ worldY: st.startWorld.y,
5503
+ screenX: st.startScreen.x,
5504
+ screenY: st.startScreen.y,
5505
+ suggestedBounds,
5506
+ insertLink
5507
+ });
5478
5508
  return;
5479
5509
  }
5480
5510
  if (!change) return;
@@ -5524,6 +5554,7 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
5524
5554
  dragStateRef.current = { kind: "idle" };
5525
5555
  },
5526
5556
  [
5557
+ requestSelectToolAfterNativeLinkUse,
5527
5558
  requestSelectToolAfterUse,
5528
5559
  screenToWorld,
5529
5560
  setRealtimePlacementPreview,
@@ -5655,7 +5686,13 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
5655
5686
  [requestRender, size]
5656
5687
  );
5657
5688
  const activeStyleToolId = toolId === "draw" || toolId === "marker" ? toolId : null;
5658
- return /* @__PURE__ */ jsxRuntime.jsx(
5689
+ const nativeLinkDialogLabels = {
5690
+ ...DEFAULT_NATIVE_LINK_TOOL_DIALOG_LABELS,
5691
+ ...linkToolDialogLabels ?? {}
5692
+ };
5693
+ const normalizedNativeLinkHref = normalizeNativeLinkHref(nativeLinkInputValue);
5694
+ const nativeLinkCanSubmit = pendingNativeLinkRequest !== null && normalizedNativeLinkHref !== null && onItemsChange != null;
5695
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5659
5696
  reactNative.View,
5660
5697
  {
5661
5698
  style: { flex: 1, overflow: "hidden" },
@@ -5669,80 +5706,225 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
5669
5706
  notifyWorldPointerLeave();
5670
5707
  },
5671
5708
  ...panResponder.panHandlers,
5672
- children: size.width > 0 && size.height > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5709
+ children: [
5710
+ size.width > 0 && size.height > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5711
+ /* @__PURE__ */ jsxRuntime.jsx(
5712
+ NativeSceneRenderer,
5713
+ {
5714
+ items: sceneItems,
5715
+ camera,
5716
+ width: size.width,
5717
+ height: size.height
5718
+ }
5719
+ ),
5720
+ interactive && /* @__PURE__ */ jsxRuntime.jsx(
5721
+ NativeInteractionOverlay,
5722
+ {
5723
+ camera,
5724
+ width: size.width,
5725
+ height: size.height,
5726
+ selectedItems,
5727
+ showResizeHandles,
5728
+ placementPreview,
5729
+ laserTrail,
5730
+ eraserTrail,
5731
+ eraserPreviewItems: items.filter(
5732
+ (it) => eraserPreviewIds.includes(it.id)
5733
+ ),
5734
+ previewStrokeStyle: strokeStyleState,
5735
+ remotePresence
5736
+ }
5737
+ ),
5738
+ interactive && showStyleInspector && activeStyleToolId ? /* @__PURE__ */ jsxRuntime.jsx(
5739
+ reactNative.View,
5740
+ {
5741
+ pointerEvents: "box-none",
5742
+ style: styleInspectorPlacement === "top-left" ? {
5743
+ position: "absolute",
5744
+ left: 16,
5745
+ top: 104,
5746
+ alignItems: "flex-start"
5747
+ } : {
5748
+ position: "absolute",
5749
+ left: 16,
5750
+ right: 16,
5751
+ bottom: 84,
5752
+ alignItems: "center"
5753
+ },
5754
+ children: /* @__PURE__ */ jsxRuntime.jsx(
5755
+ NativeVectorStyleInspector,
5756
+ {
5757
+ toolId: activeStyleToolId,
5758
+ value: strokeStyleState,
5759
+ onChange: patchCurrentStrokeStyle
5760
+ }
5761
+ )
5762
+ }
5763
+ ) : null,
5764
+ toolbar && /* @__PURE__ */ jsxRuntime.jsx(
5765
+ reactNative.View,
5766
+ {
5767
+ style: {
5768
+ position: "absolute",
5769
+ bottom: 16,
5770
+ left: 16,
5771
+ right: 16,
5772
+ flexDirection: "row",
5773
+ justifyContent: "center",
5774
+ alignItems: "center"
5775
+ },
5776
+ pointerEvents: "box-none",
5777
+ children: toolbar
5778
+ }
5779
+ )
5780
+ ] }),
5673
5781
  /* @__PURE__ */ jsxRuntime.jsx(
5674
- NativeSceneRenderer,
5782
+ reactNative.Modal,
5675
5783
  {
5676
- items: sceneItems,
5677
- camera,
5678
- width: size.width,
5679
- height: size.height
5680
- }
5681
- ),
5682
- interactive && /* @__PURE__ */ jsxRuntime.jsx(
5683
- NativeInteractionOverlay,
5684
- {
5685
- camera,
5686
- width: size.width,
5687
- height: size.height,
5688
- selectedItems,
5689
- showResizeHandles,
5690
- placementPreview,
5691
- laserTrail,
5692
- eraserTrail,
5693
- eraserPreviewItems: items.filter(
5694
- (it) => eraserPreviewIds.includes(it.id)
5695
- ),
5696
- previewStrokeStyle: strokeStyleState,
5697
- remotePresence
5698
- }
5699
- ),
5700
- interactive && showStyleInspector && activeStyleToolId ? /* @__PURE__ */ jsxRuntime.jsx(
5701
- reactNative.View,
5702
- {
5703
- pointerEvents: "box-none",
5704
- style: styleInspectorPlacement === "top-left" ? {
5705
- position: "absolute",
5706
- left: 16,
5707
- top: 104,
5708
- alignItems: "flex-start"
5709
- } : {
5710
- position: "absolute",
5711
- left: 16,
5712
- right: 16,
5713
- bottom: 84,
5714
- alignItems: "center"
5715
- },
5716
- children: /* @__PURE__ */ jsxRuntime.jsx(
5717
- NativeVectorStyleInspector,
5718
- {
5719
- toolId: activeStyleToolId,
5720
- value: strokeStyleState,
5721
- onChange: patchCurrentStrokeStyle
5722
- }
5723
- )
5724
- }
5725
- ) : null,
5726
- toolbar && /* @__PURE__ */ jsxRuntime.jsx(
5727
- reactNative.View,
5728
- {
5729
- style: {
5730
- position: "absolute",
5731
- bottom: 16,
5732
- left: 16,
5733
- right: 16,
5734
- flexDirection: "row",
5735
- justifyContent: "center",
5736
- alignItems: "center"
5737
- },
5738
- pointerEvents: "box-none",
5739
- children: toolbar
5784
+ animationType: "fade",
5785
+ transparent: true,
5786
+ visible: pendingNativeLinkRequest !== null,
5787
+ onRequestClose: closeNativeLinkDialog,
5788
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles3.nativeLinkDialogBackdrop, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles3.nativeLinkDialogCard, children: [
5789
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.nativeLinkDialogTitle, children: nativeLinkDialogLabels.title }),
5790
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.nativeLinkDialogDescription, children: nativeLinkDialogLabels.description }),
5791
+ /* @__PURE__ */ jsxRuntime.jsx(
5792
+ reactNative.TextInput,
5793
+ {
5794
+ accessibilityLabel: nativeLinkDialogLabels.title,
5795
+ autoCapitalize: "none",
5796
+ autoCorrect: false,
5797
+ keyboardType: "url",
5798
+ onChangeText: setNativeLinkInputValue,
5799
+ onSubmitEditing: submitNativeLinkDialog,
5800
+ placeholder: nativeLinkDialogLabels.inputPlaceholder,
5801
+ returnKeyType: "done",
5802
+ style: styles3.nativeLinkDialogInput,
5803
+ value: nativeLinkInputValue
5804
+ }
5805
+ ),
5806
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles3.nativeLinkDialogActions, children: [
5807
+ /* @__PURE__ */ jsxRuntime.jsx(
5808
+ reactNative.Pressable,
5809
+ {
5810
+ accessibilityRole: "button",
5811
+ onPress: closeNativeLinkDialog,
5812
+ style: ({ pressed }) => [
5813
+ styles3.nativeLinkDialogButton,
5814
+ pressed ? styles3.nativeLinkDialogButtonPressed : void 0
5815
+ ],
5816
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.nativeLinkDialogButtonText, children: nativeLinkDialogLabels.cancelLabel })
5817
+ }
5818
+ ),
5819
+ /* @__PURE__ */ jsxRuntime.jsx(
5820
+ reactNative.Pressable,
5821
+ {
5822
+ accessibilityRole: "button",
5823
+ accessibilityState: { disabled: !nativeLinkCanSubmit },
5824
+ disabled: !nativeLinkCanSubmit,
5825
+ onPress: submitNativeLinkDialog,
5826
+ style: ({ pressed }) => [
5827
+ styles3.nativeLinkDialogButton,
5828
+ styles3.nativeLinkDialogPrimaryButton,
5829
+ pressed && nativeLinkCanSubmit ? styles3.nativeLinkDialogPrimaryButtonPressed : void 0,
5830
+ !nativeLinkCanSubmit ? styles3.nativeLinkDialogDisabledButton : void 0
5831
+ ],
5832
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.nativeLinkDialogPrimaryButtonText, children: nativeLinkDialogLabels.addLabel })
5833
+ }
5834
+ )
5835
+ ] })
5836
+ ] }) })
5740
5837
  }
5741
5838
  )
5742
- ] })
5839
+ ]
5743
5840
  }
5744
5841
  );
5745
5842
  });
5843
+ var styles3 = reactNative.StyleSheet.create({
5844
+ nativeLinkDialogBackdrop: {
5845
+ flex: 1,
5846
+ alignItems: "center",
5847
+ justifyContent: "center",
5848
+ paddingHorizontal: 24,
5849
+ backgroundColor: "rgba(0, 0, 0, 0.45)"
5850
+ },
5851
+ nativeLinkDialogCard: {
5852
+ width: "100%",
5853
+ maxWidth: 480,
5854
+ padding: 24,
5855
+ borderRadius: 16,
5856
+ backgroundColor: "#ffffff",
5857
+ shadowColor: "#000000",
5858
+ shadowOpacity: 0.18,
5859
+ shadowRadius: 24,
5860
+ shadowOffset: { width: 0, height: 8 },
5861
+ elevation: 12
5862
+ },
5863
+ nativeLinkDialogTitle: {
5864
+ color: "#111827",
5865
+ fontSize: 24,
5866
+ fontWeight: "700",
5867
+ lineHeight: 30
5868
+ },
5869
+ nativeLinkDialogDescription: {
5870
+ marginTop: 12,
5871
+ color: "#6b7280",
5872
+ fontSize: 16,
5873
+ lineHeight: 22
5874
+ },
5875
+ nativeLinkDialogInput: {
5876
+ marginTop: 24,
5877
+ height: 52,
5878
+ paddingHorizontal: 14,
5879
+ borderRadius: 10,
5880
+ borderWidth: reactNative.StyleSheet.hairlineWidth,
5881
+ borderColor: "#d1d5db",
5882
+ color: "#111827",
5883
+ fontSize: 18,
5884
+ backgroundColor: "#ffffff"
5885
+ },
5886
+ nativeLinkDialogActions: {
5887
+ marginTop: 24,
5888
+ flexDirection: "row",
5889
+ justifyContent: "flex-end",
5890
+ gap: 12
5891
+ },
5892
+ nativeLinkDialogButton: {
5893
+ minWidth: 92,
5894
+ height: 48,
5895
+ alignItems: "center",
5896
+ justifyContent: "center",
5897
+ borderRadius: 10,
5898
+ borderWidth: reactNative.StyleSheet.hairlineWidth,
5899
+ borderColor: "#d1d5db",
5900
+ backgroundColor: "#ffffff",
5901
+ paddingHorizontal: 18
5902
+ },
5903
+ nativeLinkDialogButtonPressed: {
5904
+ backgroundColor: "#f3f4f6"
5905
+ },
5906
+ nativeLinkDialogButtonText: {
5907
+ color: "#111827",
5908
+ fontSize: 17,
5909
+ fontWeight: "600"
5910
+ },
5911
+ nativeLinkDialogPrimaryButton: {
5912
+ borderColor: "#18181b",
5913
+ backgroundColor: "#18181b"
5914
+ },
5915
+ nativeLinkDialogPrimaryButtonPressed: {
5916
+ backgroundColor: "#27272a"
5917
+ },
5918
+ nativeLinkDialogDisabledButton: {
5919
+ borderColor: "#9ca3af",
5920
+ backgroundColor: "#9ca3af"
5921
+ },
5922
+ nativeLinkDialogPrimaryButtonText: {
5923
+ color: "#ffffff",
5924
+ fontSize: 17,
5925
+ fontWeight: "700"
5926
+ }
5927
+ });
5746
5928
 
5747
5929
  exports.DEFAULT_LINK_CARD_SIZE = DEFAULT_LINK_CARD_SIZE;
5748
5930
  exports.DEFAULT_NATIVE_OVERFLOW_TOOL_IDS = DEFAULT_NATIVE_OVERFLOW_TOOL_IDS;