@calimero-network/mero-react 4.3.0 → 4.3.1

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 CHANGED
@@ -292,7 +292,7 @@ Helpers: `defaultMeroTheme` (the full default palette), `resolveMeroTheme(partia
292
292
 
293
293
  | Prop | Type | Default | Notes |
294
294
  |------|------|---------|-------|
295
- | `connectionType` | `ConnectionType \| CustomConnectionConfig` | `RemoteAndLocal` | Which options the embedded LoginModal shows. `Custom` skips the modal. |
295
+ | `connectionType` | `ConnectionType \| CustomConnectionConfig` | `Remote` | `Custom` connects straight to the given URL, skipping the modal. Every other value opens the LoginModal (node discovery + manual URL entry). |
296
296
  | `theme` | `MeroTheme` | — | Partial token override. |
297
297
  | `logoOnly` | `boolean` | `false` | Render only the Calimero logo (square button). The label is still announced via `aria-label`. |
298
298
  | `label` | `string \| { connect?, connected?, reconnecting? }` | — | Override default labels. Bare string targets the disconnected state. |
@@ -305,10 +305,10 @@ AppMode.SingleContext // 'single-context'
305
305
  AppMode.MultiContext // 'multi-context'
306
306
  AppMode.Admin // 'admin'
307
307
 
308
- ConnectionType.RemoteAndLocal // 'remote-and-local'
309
- ConnectionType.Remote // 'remote'
310
- ConnectionType.Local // 'local'
311
- ConnectionType.Custom // 'custom'
308
+ ConnectionType.Remote // 'remote' — opens the LoginModal (discovery + manual URL)
309
+ ConnectionType.Custom // 'custom' — skip the modal, connect to a fixed URL
310
+ ConnectionType.RemoteAndLocal // deprecated, same as Remote
311
+ ConnectionType.Local // deprecated, same as Remote
312
312
  ```
313
313
 
314
314
  ## Types
package/dist/index.cjs CHANGED
@@ -588,7 +588,6 @@ function themeToCssVars(theme) {
588
588
  function cssVar(theme, key) {
589
589
  return `var(${MERO_CSS_VARS[key]}, ${theme[key]})`;
590
590
  }
591
- var DEFAULT_LOCAL_NODE_URL = "http://node1.127.0.0.1.nip.io";
592
591
  var CUSTOM_SELECTION = "__custom__";
593
592
  function isValidUrl(urlString) {
594
593
  if (!urlString || urlString.trim() === "") {
@@ -706,29 +705,6 @@ function buildStyles(t) {
706
705
  fontSize: "0.875rem",
707
706
  textAlign: "center"
708
707
  },
709
- radioGroup: {
710
- display: "flex",
711
- gap: "0.75rem",
712
- marginBottom: "1rem",
713
- justifyContent: "center"
714
- },
715
- radioLabel: {
716
- display: "flex",
717
- alignItems: "center",
718
- gap: "0.5rem",
719
- color: text,
720
- cursor: "pointer",
721
- padding: "0.5rem 1rem",
722
- borderRadius: radius,
723
- border: `1px solid ${border}`,
724
- backgroundColor: bgSecondary,
725
- transition: "all 0.15s ease"
726
- },
727
- radioLabelActive: {
728
- border: `1px solid ${accent}`,
729
- backgroundColor: accentGlow,
730
- color: text
731
- },
732
708
  radioList: {
733
709
  display: "flex",
734
710
  flexDirection: "column",
@@ -791,29 +767,6 @@ function buildStyles(t) {
791
767
  marginBottom: "1rem",
792
768
  boxSizing: "border-box"
793
769
  },
794
- localInfo: {
795
- color: textSecondary,
796
- fontSize: "0.875rem",
797
- textAlign: "center",
798
- padding: "0.75rem",
799
- backgroundColor: bgSecondary,
800
- borderRadius: radius,
801
- marginBottom: "1rem",
802
- border: `1px solid ${border}`
803
- },
804
- localInfoCode: {
805
- color: accent
806
- },
807
- noNodeInfo: {
808
- color: textSecondary,
809
- fontSize: "0.875rem",
810
- textAlign: "center",
811
- padding: "0.75rem",
812
- backgroundColor: bgSecondary,
813
- borderRadius: radius,
814
- marginBottom: "1rem",
815
- border: `1px solid ${border}`
816
- },
817
770
  toolbar: {
818
771
  display: "flex",
819
772
  justifyContent: "center",
@@ -884,12 +837,10 @@ function buildStyles(t) {
884
837
  function LoginModal({
885
838
  onConnect,
886
839
  onClose,
887
- connectionType,
888
840
  isOpen,
889
841
  theme,
890
842
  localNodePorts = DEFAULT_LOCAL_NODE_PORTS
891
843
  }) {
892
- const [nodeType, setNodeType] = react.useState("local");
893
844
  const [selected, setSelected] = react.useState(CUSTOM_SELECTION);
894
845
  const [discovered, setDiscovered] = react.useState([]);
895
846
  const [discovering, setDiscovering] = react.useState(false);
@@ -902,27 +853,16 @@ function LoginModal({
902
853
  () => theme ? themeToCssVars(resolved) : void 0,
903
854
  [theme, resolved]
904
855
  );
905
- const shouldShowLocal = connectionType === "remote-and-local" /* RemoteAndLocal */ || connectionType === "local" /* Local */;
906
- const shouldShowRemote = connectionType === "remote-and-local" /* RemoteAndLocal */ || connectionType === "remote" /* Remote */;
907
- const shouldShowRadioGroup = shouldShowLocal && shouldShowRemote;
908
856
  react.useEffect(() => {
909
857
  const savedUrl = localStorage.getItem("mero:node_url");
910
858
  if (savedUrl) {
911
859
  setCustomUrl(savedUrl);
912
860
  }
913
861
  }, []);
914
- react.useEffect(() => {
915
- if (connectionType === "local" /* Local */) {
916
- setNodeType("local");
917
- } else if (connectionType === "remote" /* Remote */) {
918
- setNodeType("remote");
919
- }
920
- }, [connectionType]);
921
862
  const [scanNonce, setScanNonce] = react.useState(0);
922
863
  const portsKey = react.useMemo(() => localNodePorts.join(","), [localNodePorts]);
923
- const remoteActive = isOpen && shouldShowRemote && nodeType === "remote";
924
864
  react.useEffect(() => {
925
- if (!remoteActive) {
865
+ if (!isOpen) {
926
866
  return;
927
867
  }
928
868
  const controller = new AbortController();
@@ -943,16 +883,16 @@ function LoginModal({
943
883
  active = false;
944
884
  controller.abort();
945
885
  };
946
- }, [remoteActive, portsKey, scanNonce]);
886
+ }, [isOpen, portsKey, scanNonce]);
947
887
  const isCustom = selected === CUSTOM_SELECTION;
948
888
  const hasDiscovered = discovered.length > 0;
949
- const canConnect = !loading && (nodeType === "local" ? true : discovering ? false : isCustom ? isValidUrl(customUrl) : true);
889
+ const canConnect = !loading && !discovering && (isCustom ? isValidUrl(customUrl) : true);
950
890
  const canConnectRef = react.useRef(canConnect);
951
891
  canConnectRef.current = canConnect;
952
892
  const handleConnect = react.useCallback(async () => {
953
- const targetUrl = nodeType === "local" ? DEFAULT_LOCAL_NODE_URL : selected === CUSTOM_SELECTION ? customUrl : selected;
954
- const usingDiscovered = nodeType === "remote" && selected !== CUSTOM_SELECTION;
955
- if (nodeType === "remote" && !usingDiscovered && !isValidUrl(targetUrl)) {
893
+ const usingDiscovered = selected !== CUSTOM_SELECTION;
894
+ const targetUrl = usingDiscovered ? selected : customUrl;
895
+ if (!usingDiscovered && !isValidUrl(targetUrl)) {
956
896
  return;
957
897
  }
958
898
  const normalizedUrl = targetUrl.replace(/\/+$/, "");
@@ -977,7 +917,7 @@ function LoginModal({
977
917
  setError("Failed to connect. Please check the URL and try again.");
978
918
  setLoading(false);
979
919
  }
980
- }, [nodeType, selected, customUrl, onConnect]);
920
+ }, [selected, customUrl, onConnect]);
981
921
  if (!isOpen) {
982
922
  return null;
983
923
  }
@@ -1027,7 +967,7 @@ function LoginModal({
1027
967
  )
1028
968
  );
1029
969
  };
1030
- const renderRemoteView = () => {
970
+ const renderNodeSection = () => {
1031
971
  if (discovering) {
1032
972
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.discovering, "data-testid": "node-discovering", children: [
1033
973
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.spinnerSmall }),
@@ -1035,7 +975,7 @@ function LoginModal({
1035
975
  ] });
1036
976
  }
1037
977
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1038
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: styles.info, children: hasDiscovered ? "Select a discovered node, or enter a node URL manually." : "No local node found. Enter a node URL to continue." }),
978
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: styles.info, children: hasDiscovered ? "Select your Calimero node to continue." : "No local node found. Enter a node URL to continue." }),
1039
979
  hasDiscovered && /* @__PURE__ */ jsxRuntime.jsxs(
1040
980
  "div",
1041
981
  {
@@ -1095,64 +1035,8 @@ function LoginModal({
1095
1035
  /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Connecting to node..." }),
1096
1036
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.spinner })
1097
1037
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1098
- shouldShowRadioGroup && /* @__PURE__ */ jsxRuntime.jsx("p", { style: styles.info, children: "Select your Calimero node type to continue." }),
1099
1038
  error && /* @__PURE__ */ jsxRuntime.jsx("p", { style: styles.error, children: error }),
1100
- shouldShowRadioGroup && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.radioGroup, children: [
1101
- /* @__PURE__ */ jsxRuntime.jsxs(
1102
- "label",
1103
- {
1104
- "data-testid": "node-type-local",
1105
- style: {
1106
- ...styles.radioLabel,
1107
- ...nodeType === "local" ? styles.radioLabelActive : {}
1108
- },
1109
- onClick: () => setNodeType("local"),
1110
- children: [
1111
- /* @__PURE__ */ jsxRuntime.jsx(
1112
- "input",
1113
- {
1114
- type: "radio",
1115
- value: "local",
1116
- checked: nodeType === "local",
1117
- onChange: () => setNodeType("local"),
1118
- style: { display: "none" }
1119
- }
1120
- ),
1121
- "Local"
1122
- ]
1123
- }
1124
- ),
1125
- /* @__PURE__ */ jsxRuntime.jsxs(
1126
- "label",
1127
- {
1128
- "data-testid": "node-type-remote",
1129
- style: {
1130
- ...styles.radioLabel,
1131
- ...nodeType === "remote" ? styles.radioLabelActive : {}
1132
- },
1133
- onClick: () => setNodeType("remote"),
1134
- children: [
1135
- /* @__PURE__ */ jsxRuntime.jsx(
1136
- "input",
1137
- {
1138
- type: "radio",
1139
- value: "remote",
1140
- checked: nodeType === "remote",
1141
- onChange: () => setNodeType("remote"),
1142
- style: { display: "none" }
1143
- }
1144
- ),
1145
- "Remote"
1146
- ]
1147
- }
1148
- )
1149
- ] }),
1150
- nodeType === "local" && shouldShowLocal && /* @__PURE__ */ jsxRuntime.jsxs("p", { style: styles.localInfo, children: [
1151
- "Using default local node: ",
1152
- /* @__PURE__ */ jsxRuntime.jsx("br", {}),
1153
- /* @__PURE__ */ jsxRuntime.jsx("code", { style: styles.localInfoCode, children: DEFAULT_LOCAL_NODE_URL })
1154
- ] }),
1155
- nodeType === "remote" && shouldShowRemote && renderRemoteView(),
1039
+ renderNodeSection(),
1156
1040
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.buttonGroup, children: /* @__PURE__ */ jsxRuntime.jsx(
1157
1041
  "button",
1158
1042
  {
@@ -1172,7 +1056,7 @@ function LoginModal({
1172
1056
  return reactDom.createPortal(modalContent, document.body);
1173
1057
  }
1174
1058
  function ConnectButton({
1175
- connectionType = "remote-and-local" /* RemoteAndLocal */,
1059
+ connectionType = "remote" /* Remote */,
1176
1060
  className,
1177
1061
  style,
1178
1062
  theme,
@@ -1323,7 +1207,6 @@ function ConnectButton({
1323
1207
  isOpen: isModalOpen,
1324
1208
  onConnect: handleModalConnect,
1325
1209
  onClose: () => setIsModalOpen(false),
1326
- connectionType: typeof connectionType === "object" ? "remote-and-local" /* RemoteAndLocal */ : connectionType,
1327
1210
  theme: resolvedTheme ?? theme
1328
1211
  }
1329
1212
  )