@moveindustries/wallet-adapter-move-design 1.0.2 → 1.2.0

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
@@ -1,6 +1,6 @@
1
1
  # @moveindustries/wallet-adapter-move-design
2
2
 
3
- Movement Design System wallet modal for the Movement Wallet Adapter. Provides a styled `WalletModal` component for connecting wallets in Movement dApps.
3
+ Movement Design System wallet components for the Movement Wallet Adapter. Provides a styled `WalletModal` for connecting wallets and a `WalletSelector` drop-in button component for Movement dApps.
4
4
 
5
5
  ## Installation
6
6
 
@@ -26,11 +26,11 @@ Add the following import to your main CSS file (e.g., `globals.css`), **after**
26
26
  @import "@moveindustries/wallet-adapter-move-design/styles";
27
27
  ```
28
28
 
29
- This `@source` directive tells Tailwind to scan the package's source files for class names. Without it, the modal will render without styles.
29
+ This `@source` directive tells Tailwind to scan the package's source files for class names. Without it, the components will render without styles.
30
30
 
31
31
  ### Fonts
32
32
 
33
- The wallet modal uses the following fonts. Host them in your `public/fonts/` directory and add the corresponding `@font-face` declarations to your CSS:
33
+ The wallet components use the following fonts. Host them in your `public/fonts/` directory and add the corresponding `@font-face` declarations to your CSS:
34
34
 
35
35
  - **TWK Everett Mono** (Regular 400, Medium 500, Bold 700)
36
36
  - **Neue Haas Unica Pro** (Regular 400)
@@ -69,7 +69,40 @@ The wallet modal uses the following fonts. Host them in your `public/fonts/` dir
69
69
  }
70
70
  ```
71
71
 
72
- ## Usage
72
+ ## Components
73
+
74
+ ### WalletSelector
75
+
76
+ A complete, drop-in wallet button that handles the full connect/disconnect flow. Renders a gradient "CONNECT" button when disconnected, and a pill-shaped address display with dropdown when connected.
77
+
78
+ ```tsx
79
+ import { WalletSelector } from "@moveindustries/wallet-adapter-move-design";
80
+
81
+ function Header() {
82
+ return (
83
+ <nav>
84
+ <WalletSelector />
85
+ </nav>
86
+ );
87
+ }
88
+ ```
89
+
90
+ #### Props
91
+
92
+ | Prop | Type | Description |
93
+ |------|------|-------------|
94
+ | `className` | `string` | Optional class name applied to the outermost element |
95
+
96
+ The `WalletSelector` includes:
97
+ - Skeleton loading state before hydration
98
+ - Gradient connect button with hover effects
99
+ - Connected state with wallet icon, truncated address, and Move network badge
100
+ - Dropdown with copy address and disconnect actions
101
+ - Automatic wallet modal integration
102
+
103
+ ### WalletModal
104
+
105
+ A lower-level modal component for wallet selection. Use this if you want to build your own connect button but use the standard wallet picker UI.
73
106
 
74
107
  ```tsx
75
108
  import { WalletModal } from "@moveindustries/wallet-adapter-move-design";
@@ -89,7 +122,7 @@ function WalletConnectButton() {
89
122
  }
90
123
  ```
91
124
 
92
- ### Props
125
+ #### Props
93
126
 
94
127
  | Prop | Type | Description |
95
128
  |------|------|-------------|
package/dist/index.d.mts CHANGED
@@ -6,4 +6,9 @@ interface ConnectWalletDialogProps extends WalletSortingOptions {
6
6
  }
7
7
  declare function WalletModal({ onClose, ...walletSortingOptions }: ConnectWalletDialogProps): react_jsx_runtime.JSX.Element | null;
8
8
 
9
- export { type ConnectWalletDialogProps, WalletModal };
9
+ interface WalletSelectorProps {
10
+ className?: string;
11
+ }
12
+ declare function WalletSelector({ className }: WalletSelectorProps): react_jsx_runtime.JSX.Element;
13
+
14
+ export { type ConnectWalletDialogProps, WalletModal, WalletSelector, type WalletSelectorProps };
package/dist/index.d.ts CHANGED
@@ -6,4 +6,9 @@ interface ConnectWalletDialogProps extends WalletSortingOptions {
6
6
  }
7
7
  declare function WalletModal({ onClose, ...walletSortingOptions }: ConnectWalletDialogProps): react_jsx_runtime.JSX.Element | null;
8
8
 
9
- export { type ConnectWalletDialogProps, WalletModal };
9
+ interface WalletSelectorProps {
10
+ className?: string;
11
+ }
12
+ declare function WalletSelector({ className }: WalletSelectorProps): react_jsx_runtime.JSX.Element;
13
+
14
+ export { type ConnectWalletDialogProps, WalletModal, WalletSelector, type WalletSelectorProps };
package/dist/index.js CHANGED
@@ -59,7 +59,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
59
59
  // src/index.tsx
60
60
  var index_exports = {};
61
61
  __export(index_exports, {
62
- WalletModal: () => WalletModal
62
+ WalletModal: () => WalletModal,
63
+ WalletSelector: () => WalletSelector
63
64
  });
64
65
  module.exports = __toCommonJS(index_exports);
65
66
 
@@ -696,7 +697,342 @@ function IconWalletCard({ wallet, onConnect }) {
696
697
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "pointer-events-none absolute bottom-[-0.5rem] left-1/2 -translate-x-1/2 text-xs whitespace-nowrap text-white/60 opacity-0 transition-opacity duration-200 ease-in-out hover:opacity-100", children: "Click to connect" })
697
698
  ] }) }) });
698
699
  }
700
+
701
+ // src/WalletSelector.tsx
702
+ var import_wallet_adapter_react2 = require("@moveindustries/wallet-adapter-react");
703
+ var import_react2 = require("react");
704
+ var import_jsx_runtime5 = require("react/jsx-runtime");
705
+ function WalletIcon({
706
+ width = 24,
707
+ height = 24,
708
+ fill = "#002CD6"
709
+ }) {
710
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
711
+ "svg",
712
+ {
713
+ width,
714
+ height,
715
+ viewBox: "0 0 24 24",
716
+ fill: "none",
717
+ xmlns: "http://www.w3.org/2000/svg",
718
+ children: [
719
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
720
+ "path",
721
+ {
722
+ d: "M17.4845 4C17.7146 4 17.9357 4.09412 18.0984 4.26242C18.2611 4.43072 18.3521 4.65942 18.3521 4.89744V7.58974H19.2197V13.2308H17.4845V9.38462H11.4113C10.721 9.38462 10.0586 9.66797 9.57051 10.1729C9.08239 10.6778 8.80845 11.3629 8.80845 12.0769C8.80845 12.791 9.08239 13.4761 9.57051 13.981C10.0586 14.4859 10.721 14.7692 11.4113 14.7692H16.5085V16.5641H11.4113C10.2608 16.5641 9.1572 16.0915 8.34366 15.25C7.53012 14.4085 7.07324 13.267 7.07324 12.0769C7.07324 10.8868 7.53012 9.74536 8.34366 8.90385C9.1572 8.06234 10.2608 7.58974 11.4113 7.58974H16.6169V5.79487H2.73521V18.359H12.5684V20.239H1.8831C1.653 20.239 1.4319 20.1449 1.26919 19.9766C1.10648 19.8083 1.01549 19.5796 1.01549 19.3415L1 4.89744C1 4.65942 1.09099 4.43072 1.2537 4.26242C1.4164 4.09412 1.6375 4 1.86761 4H17.4845Z",
723
+ fill
724
+ }
725
+ ),
726
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
727
+ "path",
728
+ {
729
+ d: "M19.2197 14.7692V16.5641H18.3521L18.3676 19.3415C18.3676 19.5796 18.2766 19.8083 18.1139 19.9766C17.9512 20.1449 17.7301 20.239 17.5 20.239H14.0141V18.359H17.5V14.7692H19.2197Z",
730
+ fill
731
+ }
732
+ ),
733
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
734
+ "path",
735
+ {
736
+ d: "M14.0141 12.9744H11.4113V11.1795H14.0141V12.9744Z",
737
+ fill
738
+ }
739
+ ),
740
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
741
+ "path",
742
+ {
743
+ d: "M19.2197 18.359H23V20.239H19.2197V24H17.5V20.239H14.0141V18.359H17.5V14.7692H19.2197V18.359Z",
744
+ fill
745
+ }
746
+ )
747
+ ]
748
+ }
749
+ );
750
+ }
751
+ function CaretDownIcon2({
752
+ size = 18,
753
+ className,
754
+ color = "currentColor"
755
+ }) {
756
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
757
+ "svg",
758
+ {
759
+ width: size,
760
+ height: size,
761
+ viewBox: "0 0 24 24",
762
+ fill: "none",
763
+ className,
764
+ xmlns: "http://www.w3.org/2000/svg",
765
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
766
+ "path",
767
+ {
768
+ d: "M6 9L12 15L18 9",
769
+ stroke: color,
770
+ strokeWidth: "2",
771
+ strokeLinecap: "round",
772
+ strokeLinejoin: "round"
773
+ }
774
+ )
775
+ }
776
+ );
777
+ }
778
+ function CopyIcon({
779
+ size = 14,
780
+ color = "currentColor"
781
+ }) {
782
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
783
+ "svg",
784
+ {
785
+ width: size,
786
+ height: size,
787
+ viewBox: "0 0 24 24",
788
+ fill: "none",
789
+ xmlns: "http://www.w3.org/2000/svg",
790
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
791
+ "path",
792
+ {
793
+ d: "M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z",
794
+ stroke: color,
795
+ strokeWidth: "2",
796
+ strokeLinecap: "round",
797
+ strokeLinejoin: "round"
798
+ }
799
+ )
800
+ }
801
+ );
802
+ }
803
+ function DisconnectIcon({ size = 16 }) {
804
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
805
+ "svg",
806
+ {
807
+ width: size,
808
+ height: size,
809
+ viewBox: "0 0 24 24",
810
+ fill: "none",
811
+ stroke: "currentColor",
812
+ xmlns: "http://www.w3.org/2000/svg",
813
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
814
+ "path",
815
+ {
816
+ d: "M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1",
817
+ strokeWidth: "2",
818
+ strokeLinecap: "round",
819
+ strokeLinejoin: "round"
820
+ }
821
+ )
822
+ }
823
+ );
824
+ }
825
+ function MoveIcon({
826
+ width = 10,
827
+ height = 10
828
+ }) {
829
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
830
+ "svg",
831
+ {
832
+ width,
833
+ height,
834
+ viewBox: "0 0 10 10",
835
+ fill: "none",
836
+ xmlns: "http://www.w3.org/2000/svg",
837
+ children: [
838
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "5", cy: "5", r: "5", fill: "#81FFBA" }),
839
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M3 5L5 3L7 5L5 7L3 5Z", fill: "black" })
840
+ ]
841
+ }
842
+ );
843
+ }
844
+ function reduceAddress(address) {
845
+ if (!address) return "\u2026";
846
+ try {
847
+ return `${address.slice(0, 5)}\u2026${address.slice(-4)}`;
848
+ } catch (e) {
849
+ return address;
850
+ }
851
+ }
852
+ function WalletOptionsDropdown({
853
+ address,
854
+ disconnect,
855
+ onClose,
856
+ triggerRef
857
+ }) {
858
+ const dropdownRef = (0, import_react2.useRef)(null);
859
+ (0, import_react2.useEffect)(() => {
860
+ const handleClickOutside = (event) => {
861
+ const target = event.target;
862
+ const isClickOutside = dropdownRef.current && !dropdownRef.current.contains(target);
863
+ const isClickOnTrigger = (triggerRef == null ? void 0 : triggerRef.current) && triggerRef.current.contains(target);
864
+ if (isClickOutside && !isClickOnTrigger) {
865
+ onClose();
866
+ }
867
+ };
868
+ const handleEscape = (event) => {
869
+ if (event.key === "Escape") onClose();
870
+ };
871
+ document.addEventListener("mousedown", handleClickOutside);
872
+ document.addEventListener("keydown", handleEscape);
873
+ return () => {
874
+ document.removeEventListener("mousedown", handleClickOutside);
875
+ document.removeEventListener("keydown", handleEscape);
876
+ };
877
+ }, [onClose, triggerRef]);
878
+ const copyAddress = () => {
879
+ navigator.clipboard.writeText(address);
880
+ };
881
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "z-9999 relative", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
882
+ "div",
883
+ {
884
+ ref: dropdownRef,
885
+ className: "absolute top-[calc(100%+8px)] right-0 w-48 rounded-lg border border-white/10 bg-black/95 backdrop-blur-xl overflow-visible",
886
+ children: [
887
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2 px-3 h-10 rounded text-[#81FFBA] font-['TWK_Everett_Mono',monospace] text-sm font-medium leading-[140%] hover:bg-white/5", children: [
888
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(WalletIcon, { width: 14, height: 14, fill: "#81FFBA" }),
889
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "flex-1", children: reduceAddress(address) }),
890
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
891
+ "button",
892
+ {
893
+ onClick: copyAddress,
894
+ className: "cursor-pointer border-none bg-transparent p-0",
895
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CopyIcon, { size: 14, color: "#81FFBA" })
896
+ }
897
+ )
898
+ ] }),
899
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
900
+ "div",
901
+ {
902
+ className: "group flex cursor-pointer items-center gap-2 rounded-b px-3 py-3.5 h-10 text-white/64 hover:bg-[#81FFBA] hover:text-[#00135C]",
903
+ onClick: disconnect,
904
+ children: [
905
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(DisconnectIcon, { size: 16 }),
906
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "disconnect" })
907
+ ]
908
+ }
909
+ )
910
+ ]
911
+ }
912
+ ) });
913
+ }
914
+ function WalletSelector({ className }) {
915
+ var _a;
916
+ const [isModalOpen, setIsModalOpen] = (0, import_react2.useState)(false);
917
+ const [showDropdown, setShowDropdown] = (0, import_react2.useState)(false);
918
+ const [isHovered, setIsHovered] = (0, import_react2.useState)(false);
919
+ const [mounted, setMounted] = (0, import_react2.useState)(false);
920
+ const { connected, disconnect, account, wallet } = (0, import_wallet_adapter_react2.useWallet)();
921
+ const triggerButtonRef = (0, import_react2.useRef)(null);
922
+ (0, import_react2.useEffect)(() => {
923
+ setMounted(true);
924
+ }, []);
925
+ if (!mounted) {
926
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
927
+ "div",
928
+ {
929
+ className: cn(
930
+ "flex h-10 w-40 min-w-24 rounded-full bg-white/8 md:h-12",
931
+ className
932
+ ),
933
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "pointer-events-none inset-0 -z-hide h-full w-full animate-pulse overflow-hidden rounded-[inherit] bg-linear-to-r from-white/5 via-white/10 to-white/5 bg-[length:200%_100%]" })
934
+ }
935
+ );
936
+ }
937
+ if (!connected) {
938
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
939
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
940
+ "button",
941
+ {
942
+ className: cn(
943
+ "group relative flex h-10 w-40 min-w-24 cursor-pointer items-center justify-center gap-1 overflow-hidden rounded-full border-none p-0 text-[#002CD6] transition-all duration-200 group-hover:text-black md:h-12",
944
+ className
945
+ ),
946
+ style: {
947
+ background: "linear-gradient(130deg, #81FFBA 33.64%, #00FFF9 79.2%)"
948
+ },
949
+ onClick: () => setIsModalOpen(true),
950
+ children: [
951
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "absolute inset-0 rounded-full bg-white opacity-0 transition-opacity duration-200 group-hover:opacity-100" }),
952
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "relative z-10 flex items-center justify-center gap-1", children: [
953
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(WalletIcon, { width: 20, height: 20, fill: "currentColor" }),
954
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "hidden items-center font-['TWK_Everett_Mono',monospace] text-base leading-[1.3rem] font-medium md:flex", children: "CONNECT" })
955
+ ] })
956
+ ]
957
+ }
958
+ ),
959
+ isModalOpen && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(WalletModal, { onClose: () => setIsModalOpen(false) })
960
+ ] });
961
+ }
962
+ const address = ((_a = account == null ? void 0 : account.address) == null ? void 0 : _a.toString()) || "";
963
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: cn("relative inline-block", className), children: [
964
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
965
+ "button",
966
+ {
967
+ ref: triggerButtonRef,
968
+ className: "group flex h-10 w-24 cursor-pointer items-center justify-center gap-1 rounded-full border-none bg-white/8 pl-0 pr-0 text-white/64 transition-all duration-200 hover:bg-white hover:text-black md:h-12 md:w-50 md:pl-4 md:pr-4",
969
+ onClick: () => setShowDropdown(!showDropdown),
970
+ onMouseEnter: () => setIsHovered(true),
971
+ onMouseLeave: () => setIsHovered(false),
972
+ children: [
973
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "relative group-hover:[&_svg]:text-black", children: [
974
+ (wallet == null ? void 0 : wallet.icon) ? (
975
+ // eslint-disable-next-line @next/next/no-img-element
976
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
977
+ "img",
978
+ {
979
+ src: wallet.icon,
980
+ alt: "Wallet",
981
+ width: 20,
982
+ height: 20,
983
+ className: "transition-all duration-200"
984
+ }
985
+ )
986
+ ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
987
+ "svg",
988
+ {
989
+ width: "20",
990
+ height: "20",
991
+ viewBox: "0 0 24 24",
992
+ fill: "none",
993
+ className: "text-white",
994
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
995
+ "path",
996
+ {
997
+ d: "M21 18V19C21 20.1 20.1 21 19 21H5C3.89 21 3 20.1 3 19V5C3 3.9 3.89 3 5 3H19C20.1 3 21 3.9 21 5V6H12C10.89 6 10 6.9 10 8V16C10 17.1 10.89 18 12 18H21ZM12 16H22V8H12V16ZM16 13.5C15.17 13.5 14.5 12.83 14.5 12C14.5 11.17 15.17 10.5 16 10.5C16.83 10.5 17.5 11.17 17.5 12C17.5 12.83 16.83 13.5 16 13.5Z",
998
+ fill: "currentColor"
999
+ }
1000
+ )
1001
+ }
1002
+ ),
1003
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "absolute -right-1 -bottom-1 rounded-full bg-black transition-all duration-200", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MoveIcon, { width: 10, height: 10 }) })
1004
+ ] }),
1005
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "hidden max-w-[140px] items-baseline gap-1 font-['Neue_Haas_Unica_Pro',sans-serif] text-sm leading-[1.225rem] font-medium md:inline-flex", children: (account == null ? void 0 : account.mnsName) ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1006
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "truncate", children: account.mnsName }),
1007
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "shrink-0 -translate-y-[1px] rounded-sm bg-[#81FFBA]/15 px-1 py-0.5 font-['TWK_Everett_Mono',monospace] text-[9px] font-bold leading-none tracking-wider text-[#81FFBA]", children: ".move" })
1008
+ ] }) : reduceAddress(address) }),
1009
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1010
+ CaretDownIcon2,
1011
+ {
1012
+ size: 18,
1013
+ className: `transition-transform duration-200 ${showDropdown ? "rotate-180" : ""}`,
1014
+ color: isHovered ? "black" : "rgba(255, 255, 255, 0.64)"
1015
+ }
1016
+ )
1017
+ ]
1018
+ }
1019
+ ),
1020
+ showDropdown && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1021
+ WalletOptionsDropdown,
1022
+ {
1023
+ address,
1024
+ onClose: () => setShowDropdown(false),
1025
+ disconnect: () => {
1026
+ disconnect();
1027
+ setShowDropdown(false);
1028
+ },
1029
+ triggerRef: triggerButtonRef
1030
+ }
1031
+ )
1032
+ ] });
1033
+ }
699
1034
  // Annotate the CommonJS export names for ESM import in node:
700
1035
  0 && (module.exports = {
701
- WalletModal
1036
+ WalletModal,
1037
+ WalletSelector
702
1038
  });
package/dist/index.mjs CHANGED
@@ -669,6 +669,341 @@ function IconWalletCard({ wallet, onConnect }) {
669
669
  /* @__PURE__ */ jsx4("span", { className: "pointer-events-none absolute bottom-[-0.5rem] left-1/2 -translate-x-1/2 text-xs whitespace-nowrap text-white/60 opacity-0 transition-opacity duration-200 ease-in-out hover:opacity-100", children: "Click to connect" })
670
670
  ] }) }) });
671
671
  }
672
+
673
+ // src/WalletSelector.tsx
674
+ import { useWallet as useWallet2 } from "@moveindustries/wallet-adapter-react";
675
+ import { useEffect as useEffect3, useRef, useState as useState3 } from "react";
676
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
677
+ function WalletIcon({
678
+ width = 24,
679
+ height = 24,
680
+ fill = "#002CD6"
681
+ }) {
682
+ return /* @__PURE__ */ jsxs5(
683
+ "svg",
684
+ {
685
+ width,
686
+ height,
687
+ viewBox: "0 0 24 24",
688
+ fill: "none",
689
+ xmlns: "http://www.w3.org/2000/svg",
690
+ children: [
691
+ /* @__PURE__ */ jsx5(
692
+ "path",
693
+ {
694
+ d: "M17.4845 4C17.7146 4 17.9357 4.09412 18.0984 4.26242C18.2611 4.43072 18.3521 4.65942 18.3521 4.89744V7.58974H19.2197V13.2308H17.4845V9.38462H11.4113C10.721 9.38462 10.0586 9.66797 9.57051 10.1729C9.08239 10.6778 8.80845 11.3629 8.80845 12.0769C8.80845 12.791 9.08239 13.4761 9.57051 13.981C10.0586 14.4859 10.721 14.7692 11.4113 14.7692H16.5085V16.5641H11.4113C10.2608 16.5641 9.1572 16.0915 8.34366 15.25C7.53012 14.4085 7.07324 13.267 7.07324 12.0769C7.07324 10.8868 7.53012 9.74536 8.34366 8.90385C9.1572 8.06234 10.2608 7.58974 11.4113 7.58974H16.6169V5.79487H2.73521V18.359H12.5684V20.239H1.8831C1.653 20.239 1.4319 20.1449 1.26919 19.9766C1.10648 19.8083 1.01549 19.5796 1.01549 19.3415L1 4.89744C1 4.65942 1.09099 4.43072 1.2537 4.26242C1.4164 4.09412 1.6375 4 1.86761 4H17.4845Z",
695
+ fill
696
+ }
697
+ ),
698
+ /* @__PURE__ */ jsx5(
699
+ "path",
700
+ {
701
+ d: "M19.2197 14.7692V16.5641H18.3521L18.3676 19.3415C18.3676 19.5796 18.2766 19.8083 18.1139 19.9766C17.9512 20.1449 17.7301 20.239 17.5 20.239H14.0141V18.359H17.5V14.7692H19.2197Z",
702
+ fill
703
+ }
704
+ ),
705
+ /* @__PURE__ */ jsx5(
706
+ "path",
707
+ {
708
+ d: "M14.0141 12.9744H11.4113V11.1795H14.0141V12.9744Z",
709
+ fill
710
+ }
711
+ ),
712
+ /* @__PURE__ */ jsx5(
713
+ "path",
714
+ {
715
+ d: "M19.2197 18.359H23V20.239H19.2197V24H17.5V20.239H14.0141V18.359H17.5V14.7692H19.2197V18.359Z",
716
+ fill
717
+ }
718
+ )
719
+ ]
720
+ }
721
+ );
722
+ }
723
+ function CaretDownIcon2({
724
+ size = 18,
725
+ className,
726
+ color = "currentColor"
727
+ }) {
728
+ return /* @__PURE__ */ jsx5(
729
+ "svg",
730
+ {
731
+ width: size,
732
+ height: size,
733
+ viewBox: "0 0 24 24",
734
+ fill: "none",
735
+ className,
736
+ xmlns: "http://www.w3.org/2000/svg",
737
+ children: /* @__PURE__ */ jsx5(
738
+ "path",
739
+ {
740
+ d: "M6 9L12 15L18 9",
741
+ stroke: color,
742
+ strokeWidth: "2",
743
+ strokeLinecap: "round",
744
+ strokeLinejoin: "round"
745
+ }
746
+ )
747
+ }
748
+ );
749
+ }
750
+ function CopyIcon({
751
+ size = 14,
752
+ color = "currentColor"
753
+ }) {
754
+ return /* @__PURE__ */ jsx5(
755
+ "svg",
756
+ {
757
+ width: size,
758
+ height: size,
759
+ viewBox: "0 0 24 24",
760
+ fill: "none",
761
+ xmlns: "http://www.w3.org/2000/svg",
762
+ children: /* @__PURE__ */ jsx5(
763
+ "path",
764
+ {
765
+ d: "M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z",
766
+ stroke: color,
767
+ strokeWidth: "2",
768
+ strokeLinecap: "round",
769
+ strokeLinejoin: "round"
770
+ }
771
+ )
772
+ }
773
+ );
774
+ }
775
+ function DisconnectIcon({ size = 16 }) {
776
+ return /* @__PURE__ */ jsx5(
777
+ "svg",
778
+ {
779
+ width: size,
780
+ height: size,
781
+ viewBox: "0 0 24 24",
782
+ fill: "none",
783
+ stroke: "currentColor",
784
+ xmlns: "http://www.w3.org/2000/svg",
785
+ children: /* @__PURE__ */ jsx5(
786
+ "path",
787
+ {
788
+ d: "M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1",
789
+ strokeWidth: "2",
790
+ strokeLinecap: "round",
791
+ strokeLinejoin: "round"
792
+ }
793
+ )
794
+ }
795
+ );
796
+ }
797
+ function MoveIcon({
798
+ width = 10,
799
+ height = 10
800
+ }) {
801
+ return /* @__PURE__ */ jsxs5(
802
+ "svg",
803
+ {
804
+ width,
805
+ height,
806
+ viewBox: "0 0 10 10",
807
+ fill: "none",
808
+ xmlns: "http://www.w3.org/2000/svg",
809
+ children: [
810
+ /* @__PURE__ */ jsx5("circle", { cx: "5", cy: "5", r: "5", fill: "#81FFBA" }),
811
+ /* @__PURE__ */ jsx5("path", { d: "M3 5L5 3L7 5L5 7L3 5Z", fill: "black" })
812
+ ]
813
+ }
814
+ );
815
+ }
816
+ function reduceAddress(address) {
817
+ if (!address) return "\u2026";
818
+ try {
819
+ return `${address.slice(0, 5)}\u2026${address.slice(-4)}`;
820
+ } catch (e) {
821
+ return address;
822
+ }
823
+ }
824
+ function WalletOptionsDropdown({
825
+ address,
826
+ disconnect,
827
+ onClose,
828
+ triggerRef
829
+ }) {
830
+ const dropdownRef = useRef(null);
831
+ useEffect3(() => {
832
+ const handleClickOutside = (event) => {
833
+ const target = event.target;
834
+ const isClickOutside = dropdownRef.current && !dropdownRef.current.contains(target);
835
+ const isClickOnTrigger = (triggerRef == null ? void 0 : triggerRef.current) && triggerRef.current.contains(target);
836
+ if (isClickOutside && !isClickOnTrigger) {
837
+ onClose();
838
+ }
839
+ };
840
+ const handleEscape = (event) => {
841
+ if (event.key === "Escape") onClose();
842
+ };
843
+ document.addEventListener("mousedown", handleClickOutside);
844
+ document.addEventListener("keydown", handleEscape);
845
+ return () => {
846
+ document.removeEventListener("mousedown", handleClickOutside);
847
+ document.removeEventListener("keydown", handleEscape);
848
+ };
849
+ }, [onClose, triggerRef]);
850
+ const copyAddress = () => {
851
+ navigator.clipboard.writeText(address);
852
+ };
853
+ return /* @__PURE__ */ jsx5("div", { className: "z-9999 relative", children: /* @__PURE__ */ jsxs5(
854
+ "div",
855
+ {
856
+ ref: dropdownRef,
857
+ className: "absolute top-[calc(100%+8px)] right-0 w-48 rounded-lg border border-white/10 bg-black/95 backdrop-blur-xl overflow-visible",
858
+ children: [
859
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2 px-3 h-10 rounded text-[#81FFBA] font-['TWK_Everett_Mono',monospace] text-sm font-medium leading-[140%] hover:bg-white/5", children: [
860
+ /* @__PURE__ */ jsx5(WalletIcon, { width: 14, height: 14, fill: "#81FFBA" }),
861
+ /* @__PURE__ */ jsx5("span", { className: "flex-1", children: reduceAddress(address) }),
862
+ /* @__PURE__ */ jsx5(
863
+ "button",
864
+ {
865
+ onClick: copyAddress,
866
+ className: "cursor-pointer border-none bg-transparent p-0",
867
+ children: /* @__PURE__ */ jsx5(CopyIcon, { size: 14, color: "#81FFBA" })
868
+ }
869
+ )
870
+ ] }),
871
+ /* @__PURE__ */ jsxs5(
872
+ "div",
873
+ {
874
+ className: "group flex cursor-pointer items-center gap-2 rounded-b px-3 py-3.5 h-10 text-white/64 hover:bg-[#81FFBA] hover:text-[#00135C]",
875
+ onClick: disconnect,
876
+ children: [
877
+ /* @__PURE__ */ jsx5(DisconnectIcon, { size: 16 }),
878
+ /* @__PURE__ */ jsx5("span", { children: "disconnect" })
879
+ ]
880
+ }
881
+ )
882
+ ]
883
+ }
884
+ ) });
885
+ }
886
+ function WalletSelector({ className }) {
887
+ var _a;
888
+ const [isModalOpen, setIsModalOpen] = useState3(false);
889
+ const [showDropdown, setShowDropdown] = useState3(false);
890
+ const [isHovered, setIsHovered] = useState3(false);
891
+ const [mounted, setMounted] = useState3(false);
892
+ const { connected, disconnect, account, wallet } = useWallet2();
893
+ const triggerButtonRef = useRef(null);
894
+ useEffect3(() => {
895
+ setMounted(true);
896
+ }, []);
897
+ if (!mounted) {
898
+ return /* @__PURE__ */ jsx5(
899
+ "div",
900
+ {
901
+ className: cn(
902
+ "flex h-10 w-40 min-w-24 rounded-full bg-white/8 md:h-12",
903
+ className
904
+ ),
905
+ children: /* @__PURE__ */ jsx5("div", { className: "pointer-events-none inset-0 -z-hide h-full w-full animate-pulse overflow-hidden rounded-[inherit] bg-linear-to-r from-white/5 via-white/10 to-white/5 bg-[length:200%_100%]" })
906
+ }
907
+ );
908
+ }
909
+ if (!connected) {
910
+ return /* @__PURE__ */ jsxs5(Fragment2, { children: [
911
+ /* @__PURE__ */ jsxs5(
912
+ "button",
913
+ {
914
+ className: cn(
915
+ "group relative flex h-10 w-40 min-w-24 cursor-pointer items-center justify-center gap-1 overflow-hidden rounded-full border-none p-0 text-[#002CD6] transition-all duration-200 group-hover:text-black md:h-12",
916
+ className
917
+ ),
918
+ style: {
919
+ background: "linear-gradient(130deg, #81FFBA 33.64%, #00FFF9 79.2%)"
920
+ },
921
+ onClick: () => setIsModalOpen(true),
922
+ children: [
923
+ /* @__PURE__ */ jsx5("div", { className: "absolute inset-0 rounded-full bg-white opacity-0 transition-opacity duration-200 group-hover:opacity-100" }),
924
+ /* @__PURE__ */ jsxs5("div", { className: "relative z-10 flex items-center justify-center gap-1", children: [
925
+ /* @__PURE__ */ jsx5(WalletIcon, { width: 20, height: 20, fill: "currentColor" }),
926
+ /* @__PURE__ */ jsx5("span", { className: "hidden items-center font-['TWK_Everett_Mono',monospace] text-base leading-[1.3rem] font-medium md:flex", children: "CONNECT" })
927
+ ] })
928
+ ]
929
+ }
930
+ ),
931
+ isModalOpen && /* @__PURE__ */ jsx5(WalletModal, { onClose: () => setIsModalOpen(false) })
932
+ ] });
933
+ }
934
+ const address = ((_a = account == null ? void 0 : account.address) == null ? void 0 : _a.toString()) || "";
935
+ return /* @__PURE__ */ jsxs5("div", { className: cn("relative inline-block", className), children: [
936
+ /* @__PURE__ */ jsxs5(
937
+ "button",
938
+ {
939
+ ref: triggerButtonRef,
940
+ className: "group flex h-10 w-24 cursor-pointer items-center justify-center gap-1 rounded-full border-none bg-white/8 pl-0 pr-0 text-white/64 transition-all duration-200 hover:bg-white hover:text-black md:h-12 md:w-50 md:pl-4 md:pr-4",
941
+ onClick: () => setShowDropdown(!showDropdown),
942
+ onMouseEnter: () => setIsHovered(true),
943
+ onMouseLeave: () => setIsHovered(false),
944
+ children: [
945
+ /* @__PURE__ */ jsxs5("div", { className: "relative group-hover:[&_svg]:text-black", children: [
946
+ (wallet == null ? void 0 : wallet.icon) ? (
947
+ // eslint-disable-next-line @next/next/no-img-element
948
+ /* @__PURE__ */ jsx5(
949
+ "img",
950
+ {
951
+ src: wallet.icon,
952
+ alt: "Wallet",
953
+ width: 20,
954
+ height: 20,
955
+ className: "transition-all duration-200"
956
+ }
957
+ )
958
+ ) : /* @__PURE__ */ jsx5(
959
+ "svg",
960
+ {
961
+ width: "20",
962
+ height: "20",
963
+ viewBox: "0 0 24 24",
964
+ fill: "none",
965
+ className: "text-white",
966
+ children: /* @__PURE__ */ jsx5(
967
+ "path",
968
+ {
969
+ d: "M21 18V19C21 20.1 20.1 21 19 21H5C3.89 21 3 20.1 3 19V5C3 3.9 3.89 3 5 3H19C20.1 3 21 3.9 21 5V6H12C10.89 6 10 6.9 10 8V16C10 17.1 10.89 18 12 18H21ZM12 16H22V8H12V16ZM16 13.5C15.17 13.5 14.5 12.83 14.5 12C14.5 11.17 15.17 10.5 16 10.5C16.83 10.5 17.5 11.17 17.5 12C17.5 12.83 16.83 13.5 16 13.5Z",
970
+ fill: "currentColor"
971
+ }
972
+ )
973
+ }
974
+ ),
975
+ /* @__PURE__ */ jsx5("div", { className: "absolute -right-1 -bottom-1 rounded-full bg-black transition-all duration-200", children: /* @__PURE__ */ jsx5(MoveIcon, { width: 10, height: 10 }) })
976
+ ] }),
977
+ /* @__PURE__ */ jsx5("span", { className: "hidden max-w-[140px] items-baseline gap-1 font-['Neue_Haas_Unica_Pro',sans-serif] text-sm leading-[1.225rem] font-medium md:inline-flex", children: (account == null ? void 0 : account.mnsName) ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
978
+ /* @__PURE__ */ jsx5("span", { className: "truncate", children: account.mnsName }),
979
+ /* @__PURE__ */ jsx5("span", { className: "shrink-0 -translate-y-[1px] rounded-sm bg-[#81FFBA]/15 px-1 py-0.5 font-['TWK_Everett_Mono',monospace] text-[9px] font-bold leading-none tracking-wider text-[#81FFBA]", children: ".move" })
980
+ ] }) : reduceAddress(address) }),
981
+ /* @__PURE__ */ jsx5(
982
+ CaretDownIcon2,
983
+ {
984
+ size: 18,
985
+ className: `transition-transform duration-200 ${showDropdown ? "rotate-180" : ""}`,
986
+ color: isHovered ? "black" : "rgba(255, 255, 255, 0.64)"
987
+ }
988
+ )
989
+ ]
990
+ }
991
+ ),
992
+ showDropdown && /* @__PURE__ */ jsx5(
993
+ WalletOptionsDropdown,
994
+ {
995
+ address,
996
+ onClose: () => setShowDropdown(false),
997
+ disconnect: () => {
998
+ disconnect();
999
+ setShowDropdown(false);
1000
+ },
1001
+ triggerRef: triggerButtonRef
1002
+ }
1003
+ )
1004
+ ] });
1005
+ }
672
1006
  export {
673
- WalletModal
1007
+ WalletModal,
1008
+ WalletSelector
674
1009
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moveindustries/wallet-adapter-move-design",
3
- "version": "1.0.2",
3
+ "version": "1.2.0",
4
4
  "description": "Movement Wallet Adapter Move Design System - WalletModal component",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -55,7 +55,7 @@
55
55
  "clsx": "^2.1.1",
56
56
  "tailwind-merge": "^3.0.1",
57
57
  "class-variance-authority": "^0.7.1",
58
- "@moveindustries/wallet-adapter-react": "7.2.3"
58
+ "@moveindustries/wallet-adapter-react": "7.3.0"
59
59
  },
60
60
  "peerDependencies": {
61
61
  "react": "^18.0.0 || ^19.0.0",
@@ -0,0 +1,347 @@
1
+ "use client";
2
+
3
+ import { useWallet } from "@moveindustries/wallet-adapter-react";
4
+ import { useEffect, useRef, useState } from "react";
5
+ import { WalletModal } from "./WalletModal";
6
+ import { cn } from "./utils";
7
+
8
+ function WalletIcon({
9
+ width = 24,
10
+ height = 24,
11
+ fill = "#002CD6",
12
+ }: {
13
+ width?: number;
14
+ height?: number;
15
+ fill?: string;
16
+ }) {
17
+ return (
18
+ <svg
19
+ width={width}
20
+ height={height}
21
+ viewBox="0 0 24 24"
22
+ fill="none"
23
+ xmlns="http://www.w3.org/2000/svg"
24
+ >
25
+ <path
26
+ d="M17.4845 4C17.7146 4 17.9357 4.09412 18.0984 4.26242C18.2611 4.43072 18.3521 4.65942 18.3521 4.89744V7.58974H19.2197V13.2308H17.4845V9.38462H11.4113C10.721 9.38462 10.0586 9.66797 9.57051 10.1729C9.08239 10.6778 8.80845 11.3629 8.80845 12.0769C8.80845 12.791 9.08239 13.4761 9.57051 13.981C10.0586 14.4859 10.721 14.7692 11.4113 14.7692H16.5085V16.5641H11.4113C10.2608 16.5641 9.1572 16.0915 8.34366 15.25C7.53012 14.4085 7.07324 13.267 7.07324 12.0769C7.07324 10.8868 7.53012 9.74536 8.34366 8.90385C9.1572 8.06234 10.2608 7.58974 11.4113 7.58974H16.6169V5.79487H2.73521V18.359H12.5684V20.239H1.8831C1.653 20.239 1.4319 20.1449 1.26919 19.9766C1.10648 19.8083 1.01549 19.5796 1.01549 19.3415L1 4.89744C1 4.65942 1.09099 4.43072 1.2537 4.26242C1.4164 4.09412 1.6375 4 1.86761 4H17.4845Z"
27
+ fill={fill}
28
+ />
29
+ <path
30
+ d="M19.2197 14.7692V16.5641H18.3521L18.3676 19.3415C18.3676 19.5796 18.2766 19.8083 18.1139 19.9766C17.9512 20.1449 17.7301 20.239 17.5 20.239H14.0141V18.359H17.5V14.7692H19.2197Z"
31
+ fill={fill}
32
+ />
33
+ <path
34
+ d="M14.0141 12.9744H11.4113V11.1795H14.0141V12.9744Z"
35
+ fill={fill}
36
+ />
37
+ <path
38
+ d="M19.2197 18.359H23V20.239H19.2197V24H17.5V20.239H14.0141V18.359H17.5V14.7692H19.2197V18.359Z"
39
+ fill={fill}
40
+ />
41
+ </svg>
42
+ );
43
+ }
44
+
45
+ function CaretDownIcon({
46
+ size = 18,
47
+ className,
48
+ color = "currentColor",
49
+ }: {
50
+ size?: number;
51
+ className?: string;
52
+ color?: string;
53
+ }) {
54
+ return (
55
+ <svg
56
+ width={size}
57
+ height={size}
58
+ viewBox="0 0 24 24"
59
+ fill="none"
60
+ className={className}
61
+ xmlns="http://www.w3.org/2000/svg"
62
+ >
63
+ <path
64
+ d="M6 9L12 15L18 9"
65
+ stroke={color}
66
+ strokeWidth="2"
67
+ strokeLinecap="round"
68
+ strokeLinejoin="round"
69
+ />
70
+ </svg>
71
+ );
72
+ }
73
+
74
+ function CopyIcon({
75
+ size = 14,
76
+ color = "currentColor",
77
+ }: {
78
+ size?: number;
79
+ color?: string;
80
+ }) {
81
+ return (
82
+ <svg
83
+ width={size}
84
+ height={size}
85
+ viewBox="0 0 24 24"
86
+ fill="none"
87
+ xmlns="http://www.w3.org/2000/svg"
88
+ >
89
+ <path
90
+ d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
91
+ stroke={color}
92
+ strokeWidth="2"
93
+ strokeLinecap="round"
94
+ strokeLinejoin="round"
95
+ />
96
+ </svg>
97
+ );
98
+ }
99
+
100
+ function DisconnectIcon({ size = 16 }: { size?: number }) {
101
+ return (
102
+ <svg
103
+ width={size}
104
+ height={size}
105
+ viewBox="0 0 24 24"
106
+ fill="none"
107
+ stroke="currentColor"
108
+ xmlns="http://www.w3.org/2000/svg"
109
+ >
110
+ <path
111
+ d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"
112
+ strokeWidth="2"
113
+ strokeLinecap="round"
114
+ strokeLinejoin="round"
115
+ />
116
+ </svg>
117
+ );
118
+ }
119
+
120
+ function MoveIcon({
121
+ width = 10,
122
+ height = 10,
123
+ }: {
124
+ width?: number;
125
+ height?: number;
126
+ }) {
127
+ return (
128
+ <svg
129
+ width={width}
130
+ height={height}
131
+ viewBox="0 0 10 10"
132
+ fill="none"
133
+ xmlns="http://www.w3.org/2000/svg"
134
+ >
135
+ <circle cx="5" cy="5" r="5" fill="#81FFBA" />
136
+ <path d="M3 5L5 3L7 5L5 7L3 5Z" fill="black" />
137
+ </svg>
138
+ );
139
+ }
140
+
141
+ function reduceAddress(address: string | undefined) {
142
+ if (!address) return "\u2026";
143
+ try {
144
+ return `${address.slice(0, 5)}\u2026${address.slice(-4)}`;
145
+ } catch {
146
+ return address;
147
+ }
148
+ }
149
+
150
+ function WalletOptionsDropdown({
151
+ address,
152
+ disconnect,
153
+ onClose,
154
+ triggerRef,
155
+ }: {
156
+ address: string;
157
+ disconnect: () => void;
158
+ onClose: () => void;
159
+ triggerRef?: React.RefObject<HTMLButtonElement | null>;
160
+ }) {
161
+ const dropdownRef = useRef<HTMLDivElement>(null);
162
+
163
+ useEffect(() => {
164
+ const handleClickOutside = (event: MouseEvent) => {
165
+ const target = event.target as Node;
166
+ const isClickOutside =
167
+ dropdownRef.current && !dropdownRef.current.contains(target);
168
+ const isClickOnTrigger =
169
+ triggerRef?.current && triggerRef.current.contains(target);
170
+ if (isClickOutside && !isClickOnTrigger) {
171
+ onClose();
172
+ }
173
+ };
174
+
175
+ const handleEscape = (event: KeyboardEvent) => {
176
+ if (event.key === "Escape") onClose();
177
+ };
178
+
179
+ document.addEventListener("mousedown", handleClickOutside);
180
+ document.addEventListener("keydown", handleEscape);
181
+ return () => {
182
+ document.removeEventListener("mousedown", handleClickOutside);
183
+ document.removeEventListener("keydown", handleEscape);
184
+ };
185
+ }, [onClose, triggerRef]);
186
+
187
+ const copyAddress = () => {
188
+ navigator.clipboard.writeText(address);
189
+ };
190
+
191
+ return (
192
+ <div className="z-9999 relative">
193
+ <div
194
+ ref={dropdownRef}
195
+ className="absolute top-[calc(100%+8px)] right-0 w-48 rounded-lg border border-white/10 bg-black/95 backdrop-blur-xl overflow-visible"
196
+ >
197
+ <div className="flex items-center gap-2 px-3 h-10 rounded text-[#81FFBA] font-['TWK_Everett_Mono',monospace] text-sm font-medium leading-[140%] hover:bg-white/5">
198
+ <WalletIcon width={14} height={14} fill="#81FFBA" />
199
+ <span className="flex-1">{reduceAddress(address)}</span>
200
+ <button
201
+ onClick={copyAddress}
202
+ className="cursor-pointer border-none bg-transparent p-0"
203
+ >
204
+ <CopyIcon size={14} color="#81FFBA" />
205
+ </button>
206
+ </div>
207
+ <div
208
+ className="group flex cursor-pointer items-center gap-2 rounded-b px-3 py-3.5 h-10 text-white/64 hover:bg-[#81FFBA] hover:text-[#00135C]"
209
+ onClick={disconnect}
210
+ >
211
+ <DisconnectIcon size={16} />
212
+ <span>disconnect</span>
213
+ </div>
214
+ </div>
215
+ </div>
216
+ );
217
+ }
218
+
219
+ export interface WalletSelectorProps {
220
+ className?: string;
221
+ }
222
+
223
+ export function WalletSelector({ className }: WalletSelectorProps) {
224
+ const [isModalOpen, setIsModalOpen] = useState(false);
225
+ const [showDropdown, setShowDropdown] = useState(false);
226
+ const [isHovered, setIsHovered] = useState(false);
227
+ const [mounted, setMounted] = useState(false);
228
+ const { connected, disconnect, account, wallet } = useWallet();
229
+ const triggerButtonRef = useRef<HTMLButtonElement>(null);
230
+
231
+ useEffect(() => {
232
+ setMounted(true);
233
+ }, []);
234
+
235
+ if (!mounted) {
236
+ return (
237
+ <div
238
+ className={cn(
239
+ "flex h-10 w-40 min-w-24 rounded-full bg-white/8 md:h-12",
240
+ className
241
+ )}
242
+ >
243
+ <div className="pointer-events-none inset-0 -z-hide h-full w-full animate-pulse overflow-hidden rounded-[inherit] bg-linear-to-r from-white/5 via-white/10 to-white/5 bg-[length:200%_100%]" />
244
+ </div>
245
+ );
246
+ }
247
+
248
+ if (!connected) {
249
+ return (
250
+ <>
251
+ <button
252
+ className={cn(
253
+ "group relative flex h-10 w-40 min-w-24 cursor-pointer items-center justify-center gap-1 overflow-hidden rounded-full border-none p-0 text-[#002CD6] transition-all duration-200 group-hover:text-black md:h-12",
254
+ className
255
+ )}
256
+ style={{
257
+ background:
258
+ "linear-gradient(130deg, #81FFBA 33.64%, #00FFF9 79.2%)",
259
+ }}
260
+ onClick={() => setIsModalOpen(true)}
261
+ >
262
+ <div className="absolute inset-0 rounded-full bg-white opacity-0 transition-opacity duration-200 group-hover:opacity-100" />
263
+ <div className="relative z-10 flex items-center justify-center gap-1">
264
+ <WalletIcon width={20} height={20} fill="currentColor" />
265
+ <span className="hidden items-center font-['TWK_Everett_Mono',monospace] text-base leading-[1.3rem] font-medium md:flex">
266
+ CONNECT
267
+ </span>
268
+ </div>
269
+ </button>
270
+ {isModalOpen && (
271
+ <WalletModal onClose={() => setIsModalOpen(false)} />
272
+ )}
273
+ </>
274
+ );
275
+ }
276
+
277
+ const address = account?.address?.toString() || "";
278
+
279
+ return (
280
+ <div className={cn("relative inline-block", className)}>
281
+ <button
282
+ ref={triggerButtonRef}
283
+ className="group flex h-10 w-24 cursor-pointer items-center justify-center gap-1 rounded-full border-none bg-white/8 pl-0 pr-0 text-white/64 transition-all duration-200 hover:bg-white hover:text-black md:h-12 md:w-50 md:pl-4 md:pr-4"
284
+ onClick={() => setShowDropdown(!showDropdown)}
285
+ onMouseEnter={() => setIsHovered(true)}
286
+ onMouseLeave={() => setIsHovered(false)}
287
+ >
288
+ <div className="relative group-hover:[&_svg]:text-black">
289
+ {wallet?.icon ? (
290
+ // eslint-disable-next-line @next/next/no-img-element
291
+ <img
292
+ src={wallet.icon}
293
+ alt="Wallet"
294
+ width={20}
295
+ height={20}
296
+ className="transition-all duration-200"
297
+ />
298
+ ) : (
299
+ <svg
300
+ width="20"
301
+ height="20"
302
+ viewBox="0 0 24 24"
303
+ fill="none"
304
+ className="text-white"
305
+ >
306
+ <path
307
+ d="M21 18V19C21 20.1 20.1 21 19 21H5C3.89 21 3 20.1 3 19V5C3 3.9 3.89 3 5 3H19C20.1 3 21 3.9 21 5V6H12C10.89 6 10 6.9 10 8V16C10 17.1 10.89 18 12 18H21ZM12 16H22V8H12V16ZM16 13.5C15.17 13.5 14.5 12.83 14.5 12C14.5 11.17 15.17 10.5 16 10.5C16.83 10.5 17.5 11.17 17.5 12C17.5 12.83 16.83 13.5 16 13.5Z"
308
+ fill="currentColor"
309
+ />
310
+ </svg>
311
+ )}
312
+ <div className="absolute -right-1 -bottom-1 rounded-full bg-black transition-all duration-200">
313
+ <MoveIcon width={10} height={10} />
314
+ </div>
315
+ </div>
316
+
317
+ <span className="hidden max-w-[140px] items-baseline gap-1 font-['Neue_Haas_Unica_Pro',sans-serif] text-sm leading-[1.225rem] font-medium md:inline-flex">
318
+ {account?.mnsName ? (
319
+ <>
320
+ <span className="truncate">{account.mnsName}</span>
321
+ <span className="shrink-0 -translate-y-[1px] rounded-sm bg-[#81FFBA]/15 px-1 py-0.5 font-['TWK_Everett_Mono',monospace] text-[9px] font-bold leading-none tracking-wider text-[#81FFBA]">.move</span>
322
+ </>
323
+ ) : (
324
+ reduceAddress(address)
325
+ )}
326
+ </span>
327
+
328
+ <CaretDownIcon
329
+ size={18}
330
+ className={`transition-transform duration-200 ${showDropdown ? "rotate-180" : ""}`}
331
+ color={isHovered ? "black" : "rgba(255, 255, 255, 0.64)"}
332
+ />
333
+ </button>
334
+ {showDropdown && (
335
+ <WalletOptionsDropdown
336
+ address={address}
337
+ onClose={() => setShowDropdown(false)}
338
+ disconnect={() => {
339
+ disconnect();
340
+ setShowDropdown(false);
341
+ }}
342
+ triggerRef={triggerButtonRef}
343
+ />
344
+ )}
345
+ </div>
346
+ );
347
+ }
package/src/index.tsx CHANGED
@@ -1,2 +1,4 @@
1
1
  export { WalletModal } from "./WalletModal";
2
2
  export type { ConnectWalletDialogProps } from "./WalletModal";
3
+ export { WalletSelector } from "./WalletSelector";
4
+ export type { WalletSelectorProps } from "./WalletSelector";