From b5dd97ff2ea3797a920277accce5d59890667c6b Mon Sep 17 00:00:00 2001 From: yasha-meursault Date: Mon, 17 Aug 2026 20:51:40 +0400 Subject: [PATCH 1/9] refactor: initial pass moving shared wallet UI and shadcn cores into ui-kit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move shadcn primitives, wallet list, ExtendedAddress, connect buttons, AddressIcon (jazzicon) and wallet icons into @layerswap/ui-kit as shared new-gen cores; widget keeps its chrome (drawers, save-to-book) and feeds data through a WalletListAdapters context mounted once at the root. Single-source cn/shortenString/truncateDecimals/getExplorerUrl in @layerswap/utils and AccountIdentity in wallet-core; delete widget-side copies, dead shadcn files and unused radix deps. Train adoption not included — this is the initial layerswapapp-side pass. --- packages/ui-kit/package.json | 9 +- packages/ui-kit/src/components/CopyButton.tsx | 2 +- .../ui-kit/src/components/ProviderPicker.tsx | 82 ++- packages/ui-kit/src/components/index.ts | 19 + .../src/components/shadcn/accordion.tsx | 8 +- .../ui-kit/src/components/shadcn/checkbox.tsx | 28 + .../ui-kit/src/components/shadcn/command.tsx | 127 ++++ .../src/components/shadcn/dialog.tsx | 14 +- .../src/components/shadcn/popover.tsx | 70 ++- .../src/components/shadcn/progressBar.tsx | 26 + .../src/components/shadcn/tooltip.tsx | 20 +- .../wallet-list}/AddressIcon/colors.mjs | 0 .../wallet-list}/AddressIcon/index.tsx | 7 +- .../wallet-list}/AddressIcon/jazzicon.mjs | 0 .../AddressIcon/mersenne-twister.mjs | 0 .../wallet-list}/AddressIcon/paper.mjs | 0 .../components/wallet-list/ConnectButton.tsx} | 17 +- .../wallet-list}/ConnectWalletButton.tsx | 14 +- .../wallet-list}/ConnectorIcons.tsx | 0 .../wallet-list}/ConnectorIconsAllChains.tsx | 0 .../wallet-list/ExtendedAddress.tsx | 271 ++++++++ .../components/wallet-list}/FilledCheck.tsx | 0 .../wallet-list}/KnownWalletIcon.tsx | 0 .../wallet-list}/WalletIconView.tsx | 5 +- .../components/wallet-list/WalletsIcons.tsx | 45 ++ .../components/wallet-list/WalletsList.tsx | 409 ++++++++++++ .../src/components/wallet-list/adapters.tsx | 63 ++ packages/ui-kit/src/lib/portal.ts | 24 + packages/utils/package.json | 4 +- .../src/components/utils => utils/src}/cn.ts | 0 .../lib/address => utils/src}/explorerUrl.ts | 0 packages/utils/src/index.ts | 4 + .../src/shortenString.ts} | 2 +- packages/utils/src/truncateDecimals.ts | 32 + packages/wallets/core/src/hooks/useWallet.ts | 177 ++++++ packages/wallets/core/src/index.ts | 2 + packages/wallets/core/src/types/index.ts | 1 + packages/wallets/core/src/types/wallet.ts | 10 + packages/widget/core/package.json | 11 - .../AddressBook/AddressBookStep.tsx | 8 +- .../AddressBook/NetworkLogoStack.tsx | 2 +- .../AddressBook/NetworkScopeSelector.tsx | 2 +- .../src/components/Buttons/exploreButton.tsx | 2 +- .../Common/AddressIcon/jazzicon/colors.js | 12 - .../Common/AddressIcon/jazzicon/index.js | 78 --- .../AddressIcon/jazzicon/mersenne-twister.js | 217 ------- .../Common/AddressIcon/jazzicon/paper.js | 16 - .../src/components/Common/ClickTooltip.tsx | 2 +- .../src/components/Common/QRCodeWallet.tsx | 4 +- .../widget/core/src/components/Icons/index.ts | 2 +- .../components/Input/Address/AddressNote.tsx | 4 +- .../Address/AddressPicker/AddressBook.tsx | 2 +- .../AddressPicker/AddressPickerItem.tsx | 2 +- .../Address/AddressPicker/AddressWithIcon.tsx | 279 +-------- .../AddressPicker/ConnectedWallets.tsx | 4 +- .../Input/Address/AddressPicker/index.tsx | 2 +- .../Input/Address/ContractAddressNote.tsx | 4 +- .../Input/Address/UrlAddressNote.tsx | 4 +- .../src/components/Input/Amount/Balance.tsx | 2 +- .../src/components/Input/Amount/MinMax.tsx | 5 +- .../components/Input/Amount/PriceImpact.tsx | 2 +- .../src/components/Input/Amount/index.tsx | 2 +- .../Input/DestinationWalletPicker.tsx | 4 +- .../src/components/Input/NumericInput.tsx | 2 +- .../components/Input/RoutePicker/Content.tsx | 4 +- .../Input/RoutePicker/FlatContent.tsx | 2 +- .../Input/RoutePicker/RouteSortingMenu.tsx | 4 +- .../Input/RoutePicker/RouteTokenSwitch.tsx | 2 +- .../RoutePicker/RouterPickerWalletConnect.tsx | 10 +- .../components/Input/RoutePicker/Routes.tsx | 2 +- .../Input/RoutePicker/Rows/CollapsibleRow.tsx | 2 +- .../Input/RoutePicker/TokenTitleDetails.tsx | 6 +- .../components/Input/SourceWalletPicker.tsx | 6 +- .../Pages/Campaigns/Details/Leaderboard.tsx | 6 +- .../Pages/Campaigns/Details/Rewards.tsx | 5 +- .../Pages/Deposit/DestinationTokenPicker.tsx | 2 +- .../Pages/Deposit/Options/MethodPicker.tsx | 4 +- .../src/components/Pages/Deposit/index.tsx | 2 +- .../DepositQuoteDetails.tsx | 4 +- .../DestinationWalletPicker.tsx | 2 +- .../Pages/Swap/Form/ExchangeForm.tsx | 4 +- .../Swap/Form/FeeDetails/GaslessBadge.tsx | 2 +- .../Pages/Swap/Form/FeeDetails/Rate.tsx | 2 +- .../Swap/Form/FeeDetails/ReceiveAmounts.tsx | 2 +- .../Swap/Form/FeeDetails/RefuelModal.tsx | 2 +- .../Pages/Swap/Form/FeeDetails/Slippage.tsx | 2 +- .../SwapQuote/DetailedEstimates.tsx | 7 +- .../Form/FeeDetails/SwapQuote/SummaryRow.tsx | 4 +- .../Swap/Form/FeeDetails/SwapQuote/index.tsx | 2 +- .../Pages/Swap/Form/FeeDetails/index.tsx | 2 +- .../SecondaryComponents/ReserveGasNote.tsx | 2 +- .../Pages/Swap/Withdraw/ManualWithdraw.tsx | 10 +- .../Swap/Withdraw/Processing/Processing.tsx | 4 +- .../Pages/Swap/Withdraw/Summary/Summary.tsx | 2 +- .../Withdraw/Wallet/RPCUnhealthyMessage.tsx | 2 +- .../Swap/Withdraw/WalletTransferContent.tsx | 2 +- .../Pages/Swap/Withdraw/Withdraw.tsx | 2 +- .../Pages/Swap/Withdraw/messages/Message.tsx | 2 +- .../Pages/SwapHistory/Filters/CheckboxRow.tsx | 4 +- .../SwapHistory/Filters/NetworksDropdown.tsx | 2 +- .../SwapHistory/Filters/SearchResult.tsx | 2 +- .../SwapHistory/Filters/WalletsDropdown.tsx | 4 +- .../components/Pages/SwapHistory/History.tsx | 4 +- .../Pages/SwapHistory/HistorySummary.tsx | 4 +- .../SwapHistory/SwapDetailsComponent.tsx | 9 +- .../Select/Command/commandSelect.tsx | 4 +- .../WalletComponents/ConnectedWallets.tsx | 46 +- .../Wallet/WalletComponents/WalletsList.tsx | 432 ++----------- .../WalletComponents/walletListAdapters.tsx | 43 ++ .../Wallet/WalletProviders/index.tsx | 9 +- .../core/src/components/shadcn/button.tsx | 67 -- .../core/src/components/shadcn/checkbox.tsx | 29 - .../core/src/components/shadcn/command.tsx | 164 ----- .../core/src/components/shadcn/index.ts | 8 - .../src/components/shadcn/progressBar.tsx | 27 - .../core/src/components/shadcn/select.tsx | 113 ---- .../widget/core/src/components/shadcn/tab.tsx | 55 -- .../src/components/utils/RoundDecimals.ts | 33 - .../src/components/utils/formatTokenAmount.ts | 2 +- .../widget/core/src/context/swapAccounts.tsx | 10 +- packages/widget/core/src/exports/internal.ts | 6 +- packages/widget/core/src/helpers/cn.ts | 4 - packages/widget/core/src/hooks/useWallet.ts | 161 +---- pnpm-lock.yaml | 585 +----------------- 124 files changed, 1611 insertions(+), 2500 deletions(-) rename packages/{widget/core => ui-kit}/src/components/shadcn/accordion.tsx (97%) create mode 100644 packages/ui-kit/src/components/shadcn/checkbox.tsx create mode 100644 packages/ui-kit/src/components/shadcn/command.tsx rename packages/{widget/core => ui-kit}/src/components/shadcn/dialog.tsx (94%) rename packages/{widget/core => ui-kit}/src/components/shadcn/popover.tsx (60%) create mode 100644 packages/ui-kit/src/components/shadcn/progressBar.tsx rename packages/{widget/core => ui-kit}/src/components/shadcn/tooltip.tsx (84%) rename packages/{widget/core/src/components/Common => ui-kit/src/components/wallet-list}/AddressIcon/colors.mjs (100%) rename packages/{widget/core/src/components/Common => ui-kit/src/components/wallet-list}/AddressIcon/index.tsx (90%) rename packages/{widget/core/src/components/Common => ui-kit/src/components/wallet-list}/AddressIcon/jazzicon.mjs (100%) rename packages/{widget/core/src/components/Common => ui-kit/src/components/wallet-list}/AddressIcon/mersenne-twister.mjs (100%) rename packages/{widget/core/src/components/Common => ui-kit/src/components/wallet-list}/AddressIcon/paper.mjs (100%) rename packages/{widget/core/src/components/Buttons/connectButton.tsx => ui-kit/src/components/wallet-list/ConnectButton.tsx} (53%) rename packages/{widget/core/src/components/Common => ui-kit/src/components/wallet-list}/ConnectWalletButton.tsx (87%) rename packages/{widget/core/src/components/Icons => ui-kit/src/components/wallet-list}/ConnectorIcons.tsx (100%) rename packages/{widget/core/src/components/Icons => ui-kit/src/components/wallet-list}/ConnectorIconsAllChains.tsx (100%) create mode 100644 packages/ui-kit/src/components/wallet-list/ExtendedAddress.tsx rename packages/{widget/core/src/components/Icons => ui-kit/src/components/wallet-list}/FilledCheck.tsx (100%) rename packages/{widget/core/src/components/Icons => ui-kit/src/components/wallet-list}/KnownWalletIcon.tsx (100%) rename packages/{widget/core/src/components/Wallet => ui-kit/src/components/wallet-list}/WalletIconView.tsx (88%) create mode 100644 packages/ui-kit/src/components/wallet-list/WalletsIcons.tsx create mode 100644 packages/ui-kit/src/components/wallet-list/WalletsList.tsx create mode 100644 packages/ui-kit/src/components/wallet-list/adapters.tsx create mode 100644 packages/ui-kit/src/lib/portal.ts rename packages/{widget/core/src/components/utils => utils/src}/cn.ts (100%) rename packages/{widget/core/src/lib/address => utils/src}/explorerUrl.ts (100%) rename packages/{widget/core/src/components/utils/ShortenString.tsx => utils/src/shortenString.ts} (89%) create mode 100644 packages/utils/src/truncateDecimals.ts create mode 100644 packages/wallets/core/src/hooks/useWallet.ts delete mode 100644 packages/widget/core/src/components/Common/AddressIcon/jazzicon/colors.js delete mode 100644 packages/widget/core/src/components/Common/AddressIcon/jazzicon/index.js delete mode 100644 packages/widget/core/src/components/Common/AddressIcon/jazzicon/mersenne-twister.js delete mode 100644 packages/widget/core/src/components/Common/AddressIcon/jazzicon/paper.js create mode 100644 packages/widget/core/src/components/Wallet/WalletComponents/walletListAdapters.tsx delete mode 100644 packages/widget/core/src/components/shadcn/button.tsx delete mode 100644 packages/widget/core/src/components/shadcn/checkbox.tsx delete mode 100644 packages/widget/core/src/components/shadcn/command.tsx delete mode 100644 packages/widget/core/src/components/shadcn/index.ts delete mode 100644 packages/widget/core/src/components/shadcn/progressBar.tsx delete mode 100644 packages/widget/core/src/components/shadcn/select.tsx delete mode 100644 packages/widget/core/src/components/shadcn/tab.tsx delete mode 100644 packages/widget/core/src/helpers/cn.ts diff --git a/packages/ui-kit/package.json b/packages/ui-kit/package.json index 7cf5047f85..67cb8a32e8 100644 --- a/packages/ui-kit/package.json +++ b/packages/ui-kit/package.json @@ -11,10 +11,6 @@ "types": "./dist/types/index.d.ts", "default": "./dist/esm/index.js" }, - "./types": { - "types": "./dist/types/types/index.d.ts", - "default": "./dist/esm/types/index.js" - }, "./components": { "types": "./dist/types/components/index.d.ts", "default": "./dist/esm/components/index.js" @@ -48,13 +44,12 @@ "@layerswap/utils": "workspace:^", "@layerswap/wallet-core": "workspace:^", "@layerswap/widget-types": "workspace:^", - "@radix-ui/react-checkbox": "^1.1.5", - "@radix-ui/react-popover": "^1.1.15", - "@radix-ui/react-tooltip": "^1.2.8", "clsx": "^2.1.1", + "cmdk": "^0.2.0", "copy-to-clipboard": "^3.3.3", "framer-motion": "^12.26.2", "lucide-react": "^0.379.0", + "radix-ui": "^1.4.3", "react-qrcode-logo": "4.1.0" }, "peerDependencies": { diff --git a/packages/ui-kit/src/components/CopyButton.tsx b/packages/ui-kit/src/components/CopyButton.tsx index 27235da5db..a9376e714d 100644 --- a/packages/ui-kit/src/components/CopyButton.tsx +++ b/packages/ui-kit/src/components/CopyButton.tsx @@ -1,4 +1,4 @@ -import * as Tooltip from "@radix-ui/react-tooltip" +import { Tooltip } from "radix-ui" import { Check } from "lucide-react" import { useEffect, useLayoutEffect, useState, type FC, type ReactNode, type RefObject } from "react" import clsx from "clsx" diff --git a/packages/ui-kit/src/components/ProviderPicker.tsx b/packages/ui-kit/src/components/ProviderPicker.tsx index 8ed75814d4..189e947e25 100644 --- a/packages/ui-kit/src/components/ProviderPicker.tsx +++ b/packages/ui-kit/src/components/ProviderPicker.tsx @@ -1,10 +1,9 @@ -import * as Checkbox from "@radix-ui/react-checkbox"; -import * as Popover from "@radix-ui/react-popover"; -import { Check } from "lucide-react"; import { FC, useMemo, useRef, useState } from "react"; import type { WalletConnectionProvider } from "@layerswap/wallet-core/types" import clsx from "clsx"; import ProviderFilterIcon from "./ProviderFilterIcon"; +import { Popover, PopoverTrigger, PopoverContent } from "./shadcn/popover"; +import { Checkbox } from "./shadcn/checkbox"; export const ProviderPicker: FC<{ providers: WalletConnectionProvider[], selectedProviderNames: string[], setSelectedProviderNames: (providerNames: string[]) => void }> = ({ providers, selectedProviderNames, setSelectedProviderNames }) => { const values = useMemo(() => providers.map(p => p.name).sort(), [providers]) @@ -27,8 +26,8 @@ export const ProviderPicker: FC<{ providers: WalletConnectionProvider[], selecte return (
- - + 0, })} @@ -39,46 +38,39 @@ export const ProviderPicker: FC<{ providers: WalletConnectionProvider[], selecte {selectedProviderNames.length}
)} - - - e.preventDefault()} - className="z-50 w-[130px]! max-w-72 origin-(--radix-popover-content-transform-origin) rounded-xl! bg-secondary-500! p-2 text-sm text-primary-text! shadow-sm ring-1 ring-primary-text/5 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:ring-primary-text/10 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 space-y-1" - style={{ width: '130px', minWidth: '130px', maxWidth: '130px' }} - > - { - values.map((item, index) => ( -
onSelect(item)} className="px-2 py-1 text-left flex items-center w-full gap-2.5 hover:bg-secondary-400 rounded-lg transition-colors duration-200 text-secondary-text cursor-pointer"> - e.stopPropagation()} - onCheckedChange={() => onSelect(item)} - className="peer h-3 w-3 shrink-0 rounded border border-secondary-text data-[state=checked]:border-primary-text focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary-text data-[state=checked]:text-secondary-800" - > - - - - - -
- )) - } - {selectedProviderNames.length > 0 && ( - - )} -
-
- + + + { + values.map((item, index) => ( +
onSelect(item)} className="px-2 py-1 text-left flex items-center w-full gap-2.5 hover:bg-secondary-400 rounded-lg transition-colors duration-200 text-secondary-text cursor-pointer"> + e.stopPropagation()} + onCheckedChange={() => onSelect(item)} + /> + +
+ )) + } + {selectedProviderNames.length > 0 && ( + + )} +
+ ) } diff --git a/packages/ui-kit/src/components/index.ts b/packages/ui-kit/src/components/index.ts index bac25ba4db..5ebbb61f1f 100644 --- a/packages/ui-kit/src/components/index.ts +++ b/packages/ui-kit/src/components/index.ts @@ -16,3 +16,22 @@ export { default as SearchIcon } from "./SearchIcon" export { default as StyledQRCode } from "./StyledQRCode" export type { StyledQRCodeProps } from "./StyledQRCode" export { default as WalletIcon } from "./WalletIcon" +export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider, TooltipArrow } from "./shadcn/tooltip" +export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor, PopoverHeader, PopoverTitle, PopoverDescription } from "./shadcn/popover" +export { Progress } from "./shadcn/progressBar" +export { Checkbox } from "./shadcn/checkbox" +export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger } from "./shadcn/dialog" +export { Command, CommandWrapper, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandShortcut, CommandSeparator } from "./shadcn/command" +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "./shadcn/accordion" +export { WalletListAdaptersProvider, useWalletListAdapters } from "./wallet-list/adapters" +export type { WalletListAdapters, WalletBalanceResult, AddressLabelResult, SaveAddressRequest, RenderSaveFormProps } from "./wallet-list/adapters" +export { default as WalletsList, WalletItem } from "./wallet-list/WalletsList" +export { WalletsIcons } from "./wallet-list/WalletsIcons" +export { ExtendedAddress } from "./wallet-list/ExtendedAddress" +export { default as WalletIconView } from "./wallet-list/WalletIconView" +export { default as AddressIcon } from "./wallet-list/AddressIcon" +export { default as ConnectButton } from "./wallet-list/ConnectButton" +export { default as ConnectWalletButton } from "./wallet-list/ConnectWalletButton" +export { KnownWalletIcon } from "./wallet-list/KnownWalletIcon" +export { ResolveConnectorIcon } from "./wallet-list/ConnectorIcons" +export { default as FilledCheck } from "./wallet-list/FilledCheck" diff --git a/packages/widget/core/src/components/shadcn/accordion.tsx b/packages/ui-kit/src/components/shadcn/accordion.tsx similarity index 97% rename from packages/widget/core/src/components/shadcn/accordion.tsx rename to packages/ui-kit/src/components/shadcn/accordion.tsx index 2a36611d98..3959409e6b 100644 --- a/packages/widget/core/src/components/shadcn/accordion.tsx +++ b/packages/ui-kit/src/components/shadcn/accordion.tsx @@ -1,6 +1,8 @@ +"use client" + import * as React from "react" import { motion, AnimatePresence } from "framer-motion" -import { classNames } from "../utils/classNames" +import { cn } from "@layerswap/utils" interface AccordionContextValue { value: string | string[] | undefined @@ -104,7 +106,7 @@ const AccordionTrigger = React.forwardRef< opacity: { duration: 0.1, delay: isOpen ? 0.05 : 0 } }} style={{ overflow: "hidden" }} - className={classNames("AccordionContent", className)} + className={cn("AccordionContent", className)} > {isOpen && ( diff --git a/packages/ui-kit/src/components/shadcn/checkbox.tsx b/packages/ui-kit/src/components/shadcn/checkbox.tsx new file mode 100644 index 0000000000..e5378871de --- /dev/null +++ b/packages/ui-kit/src/components/shadcn/checkbox.tsx @@ -0,0 +1,28 @@ +"use client" + +import * as React from "react" +import { Checkbox as CheckboxPrimitive } from "radix-ui" +import { Check } from "lucide-react" +import { cn } from "@layerswap/utils" + +function Checkbox({ className, ...props }: React.ComponentProps) { + return ( + + + + + + ) +} + +export { Checkbox } diff --git a/packages/ui-kit/src/components/shadcn/command.tsx b/packages/ui-kit/src/components/shadcn/command.tsx new file mode 100644 index 0000000000..d4532cb681 --- /dev/null +++ b/packages/ui-kit/src/components/shadcn/command.tsx @@ -0,0 +1,127 @@ +"use client" + +import * as React from "react" +import { Command as CommandPrimitive } from "cmdk" +import { cn } from "@layerswap/utils" + +function Command({ className, ...props }: React.ComponentProps) { + return ( + + ) +} + +function CommandWrapper({ className, children, ...props }: React.ComponentProps) { + return ( + + {children} + + ) +} + +function CommandInput({ className, children, ...props }: React.ComponentProps) { + return ( +
+
{children}
+ +
+ ) +} + +function CommandList({ className, ...props }: React.ComponentProps) { + return ( + + ) +} + +function CommandEmpty({ ...props }: React.ComponentProps) { + return ( + + ) +} + +function CommandGroup({ className, ...props }: React.ComponentProps) { + return ( +
{props.children}
+ ) +} + +function CommandSeparator({ className, ...props }: React.ComponentProps) { + return ( + + ) +} + +function CommandItem({ className, ...props }: React.ComponentProps) { + return ( + + ) +} + +const CommandShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ) +} +CommandShortcut.displayName = "CommandShortcut" + +export { + Command, + CommandWrapper, + CommandInput, + CommandList, + CommandEmpty, + CommandGroup, + CommandItem, + CommandShortcut, + CommandSeparator, +} diff --git a/packages/widget/core/src/components/shadcn/dialog.tsx b/packages/ui-kit/src/components/shadcn/dialog.tsx similarity index 94% rename from packages/widget/core/src/components/shadcn/dialog.tsx rename to packages/ui-kit/src/components/shadcn/dialog.tsx index d3033da8f2..ec5f4745ac 100644 --- a/packages/widget/core/src/components/shadcn/dialog.tsx +++ b/packages/ui-kit/src/components/shadcn/dialog.tsx @@ -3,7 +3,7 @@ import * as React from "react" import { XIcon } from "lucide-react" import { Dialog as DialogPrimitive } from "radix-ui" -import { classNames } from "../utils/classNames" +import { cn } from "@layerswap/utils" function Dialog({ ...props @@ -36,7 +36,7 @@ function DialogOverlay({ return ( ) { return (
) @@ -102,7 +102,7 @@ function DialogFooter({ return (
} -/** - * Reads `#widget` lazily after mount. - * - * Radix Portal defaults to `document.body`. The widget's Tailwind - * utilities are scoped to `.layerswap-styles` (postcss-prefixwrap), so a - * popover portaled to body picks up none of them — no z-index, no - * background — and looks like the trigger did nothing. Defaulting to - * `#widget` (always inside `.layerswap-styles`, also the target the - * wallet drawer uses) keeps every popover in-scope by default. Callers - * that need a different target can still pass `container` explicitly. - */ -function useWidgetContainer(): HTMLElement | null { - const [el, setEl] = React.useState(null) - // useLayoutEffect runs synchronously after DOM mutation, before paint, so - // the portal resolves to `#widget` before the popover is first painted — - // avoiding the body→#widget remount flash a passive effect would cause. - // This component never renders server-side, so the SSR warning is moot. - React.useLayoutEffect(() => { - setEl(document.getElementById('widget')) - }, []) - return el -} - function PopoverContent({ className, align = "center", @@ -56,7 +34,7 @@ function PopoverContent({ align={align} sideOffset={sideOffset} onOpenAutoFocus={(e) => e.preventDefault()} - className={clsx( + className={cn( "z-50 w-fit max-w-72 origin-(--radix-popover-content-transform-origin) rounded-3xl bg-secondary-600 p-2 text-sm text-secondary-text shadow-sm ring-1 ring-primary-text/5 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:ring-primary-text/10 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95", className )} @@ -73,4 +51,42 @@ function PopoverAnchor({ return } -export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor } +function PopoverHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function PopoverTitle({ className, ...props }: React.ComponentProps<"h2">) { + return ( +
+ ) +} + +function PopoverDescription({ className, ...props }: React.ComponentProps<"p">) { + return ( +

+ ) +} + +export { + Popover, + PopoverAnchor, + PopoverContent, + PopoverDescription, + PopoverHeader, + PopoverTitle, + PopoverTrigger, +} diff --git a/packages/ui-kit/src/components/shadcn/progressBar.tsx b/packages/ui-kit/src/components/shadcn/progressBar.tsx new file mode 100644 index 0000000000..3e1dc8e5c2 --- /dev/null +++ b/packages/ui-kit/src/components/shadcn/progressBar.tsx @@ -0,0 +1,26 @@ +"use client" + +import * as React from "react" +import { Progress as ProgressPrimitive } from "radix-ui" +import { cn } from "@layerswap/utils" + +function Progress({ className, value, ...props }: React.ComponentProps) { + return ( + + + + ) +} + +export { Progress } diff --git a/packages/widget/core/src/components/shadcn/tooltip.tsx b/packages/ui-kit/src/components/shadcn/tooltip.tsx similarity index 84% rename from packages/widget/core/src/components/shadcn/tooltip.tsx rename to packages/ui-kit/src/components/shadcn/tooltip.tsx index 871d8a2208..5c99a66281 100644 --- a/packages/widget/core/src/components/shadcn/tooltip.tsx +++ b/packages/ui-kit/src/components/shadcn/tooltip.tsx @@ -1,8 +1,9 @@ "use client" import * as React from "react" -import * as TooltipPrimitive from "@radix-ui/react-tooltip" -import { clsx } from "clsx" +import { Tooltip as TooltipPrimitive } from "radix-ui" +import { cn } from "@layerswap/utils" +import { useWidgetContainer } from "@/lib/portal" function TooltipProvider({ delayDuration = 0, @@ -81,7 +82,7 @@ function TooltipTrigger({ toggle?.() } } - return & { arrowClasses?: string, - showArrow?: boolean + showArrow?: boolean, + container?: HTMLElement | null } function TooltipContent({ @@ -100,16 +102,18 @@ function TooltipContent({ children, arrowClasses, showArrow = false, + container, ...props }: TooltipContentProps) { - const container = typeof document !== 'undefined' ? document.getElementById('widget') : null; + const widgetContainer = useWidgetContainer() + const resolvedContainer = container !== undefined ? container : widgetContainer return ( - +

) { - return + return } export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider, TooltipArrow } diff --git a/packages/widget/core/src/components/Common/AddressIcon/colors.mjs b/packages/ui-kit/src/components/wallet-list/AddressIcon/colors.mjs similarity index 100% rename from packages/widget/core/src/components/Common/AddressIcon/colors.mjs rename to packages/ui-kit/src/components/wallet-list/AddressIcon/colors.mjs diff --git a/packages/widget/core/src/components/Common/AddressIcon/index.tsx b/packages/ui-kit/src/components/wallet-list/AddressIcon/index.tsx similarity index 90% rename from packages/widget/core/src/components/Common/AddressIcon/index.tsx rename to packages/ui-kit/src/components/wallet-list/AddressIcon/index.tsx index c5fd89274f..06555eefe5 100644 --- a/packages/widget/core/src/components/Common/AddressIcon/index.tsx +++ b/packages/ui-kit/src/components/wallet-list/AddressIcon/index.tsx @@ -2,8 +2,8 @@ import Jazzicon from "./jazzicon.mjs"; import { FC, useEffect, useRef } from "react"; import { UserRound } from "lucide-react"; -import { cn } from "@/helpers/cn"; -import { useAddressName } from "@/stores/addressBookStore"; +import { cn } from "@layerswap/utils"; +import { useWalletListAdapters } from "../adapters"; type Props = { address: string; @@ -15,7 +15,8 @@ type Props = { const AddressIcon: FC = ({ address, size, className, network, providerName }) => { const ref = useRef(null) - const savedName = useAddressName(address, network, providerName) + const adapters = useWalletListAdapters() + const { name: savedName } = adapters.useAddressLabel(address, network, providerName) const saved = !!savedName const renderSize = size ?? 24 // Mirror the connected-wallet network badge (≈0.5 of the icon, poking out the corner). diff --git a/packages/widget/core/src/components/Common/AddressIcon/jazzicon.mjs b/packages/ui-kit/src/components/wallet-list/AddressIcon/jazzicon.mjs similarity index 100% rename from packages/widget/core/src/components/Common/AddressIcon/jazzicon.mjs rename to packages/ui-kit/src/components/wallet-list/AddressIcon/jazzicon.mjs diff --git a/packages/widget/core/src/components/Common/AddressIcon/mersenne-twister.mjs b/packages/ui-kit/src/components/wallet-list/AddressIcon/mersenne-twister.mjs similarity index 100% rename from packages/widget/core/src/components/Common/AddressIcon/mersenne-twister.mjs rename to packages/ui-kit/src/components/wallet-list/AddressIcon/mersenne-twister.mjs diff --git a/packages/widget/core/src/components/Common/AddressIcon/paper.mjs b/packages/ui-kit/src/components/wallet-list/AddressIcon/paper.mjs similarity index 100% rename from packages/widget/core/src/components/Common/AddressIcon/paper.mjs rename to packages/ui-kit/src/components/wallet-list/AddressIcon/paper.mjs diff --git a/packages/widget/core/src/components/Buttons/connectButton.tsx b/packages/ui-kit/src/components/wallet-list/ConnectButton.tsx similarity index 53% rename from packages/widget/core/src/components/Buttons/connectButton.tsx rename to packages/ui-kit/src/components/wallet-list/ConnectButton.tsx index 3298c486ee..1dbacc66c0 100644 --- a/packages/widget/core/src/components/Buttons/connectButton.tsx +++ b/packages/ui-kit/src/components/wallet-list/ConnectButton.tsx @@ -1,25 +1,28 @@ +'use client' import { ReactNode } from "react"; -import useWallet from "../../hooks/useWallet"; -import { useConnectModal } from "../Wallet/WalletModal"; +import { useWallet } from "@layerswap/wallet-core"; +import { useWalletListAdapters } from "./adapters"; const ConnectButton = ({ children, className, + disabled, }: { children: ReactNode; className?: string; + disabled?: boolean; }) => { - const { providers } = useWallet(); + const adapters = useWalletListAdapters() + const { providers } = useWallet(adapters.networks, undefined, undefined, { getNetworkId: adapters.getNetworkId }) const filteredProviders = providers.filter(p => !!p.autofillSupportedNetworks) - const { connect } = useConnectModal() return ( + ) + + const walletsListBlock = wallets.length > 0 && ( +
+ { + wallets.map((item, index) => ) + } +
+ ) + + if (layout === "overlay") { + return ( +
+
{walletsListBlock}
+
{connectButton}
+
+ ) + } + + return ( +
+ {connectButton} + {walletsListBlock} +
+ ) +} + +type WalletItemProps = { + account: AccountIdentity | Wallet, + selectable?: boolean, + token?: Token; + network?: Network; + selectedAddress: string | undefined; + onWalletSelect?: (props: SelectAccountProps) => void; + isCompatible?: boolean; + onSaveRequest?: (request: SaveAddressRequest) => void; +} +export const WalletItem: FC = ({ selectable, account: item, network, onWalletSelect, token, selectedAddress, isCompatible = true, onSaveRequest }) => { + const adapters = useWalletListAdapters() + + const { formatted: walletBalanceAmount, isLoading: isBalanceLoading } = adapters.useWalletBalance({ + address: isCompatible ? item.address : undefined, + network: isCompatible ? network : undefined, + token, + }) + + const isSelected = selectable && (item.addresses.length == 1 && item.address == selectedAddress) + + const saveType = network?.type ?? (item.providerName ? item.providerName.toLowerCase() as NetworkType : undefined) + const supportedNetworks = getWithdrawalSupportedNetworks(item) + const saveInDrawer = !(selectable && onWalletSelect && network) + const requestSave = (saveInDrawer && onSaveRequest) + ? (address: string) => onSaveRequest({ address, networkType: saveType, supportedNetworks }) + : undefined + + // The row hosts nested interactive elements (disconnect tooltip button, + // ExtendedAddress popover trigger), so it must not be a + + +

Disconnect

+
+ + } + { + isMulti && + + } + { + isSelected && +
+ +
+ } +
+ { + isMulti && isExpanded && +
+ { + item.addresses.map((address, index) => ) + } +
+ } +
+ ) +} + + +type NestedWalletAddressProps = { + address: string, + selectable?: boolean, + token?: Token; + network?: Network; + item: AccountIdentity | Wallet, + onWalletSelect?: (props: SelectAccountProps) => void; + selectedAddress: string | undefined; + isCompatible?: boolean; + onSaveToBook?: (address: string) => void; +} + +const NestedWalletAddress: FC = ({ selectable, address, network, onWalletSelect, token, item, selectedAddress, isCompatible, onSaveToBook }) => { + const adapters = useWalletListAdapters() + const { formatted: nestedWalletBalanceAmount, isLoading: isBalanceLoading } = adapters.useWalletBalance({ + address: isCompatible ? address : undefined, + network: isCompatible ? network : undefined, + token, + }) + + const isNestedSelected = selectable && address == selectedAddress + + // Not a - { - wallets.length > 0 && -
- { - wallets.map((item, index) => ) - } -
- } -
+ <> + + setSaveRequest(null)} /> + ) } @@ -87,333 +48,38 @@ type WalletItemProps = { onWalletSelect?: (props: SelectAccountProps) => void; isCompatible?: boolean; } -export const WalletItem: FC = ({ selectable, account: item, network, onWalletSelect, token, selectedAddress, isCompatible = true }) => { - const { networks } = useSettingsState() - const balanceNetwork = token ? networks.find(n => n.name === network?.name && n.tokens.some(t => t.symbol === token.symbol)) : undefined - - const { balances, isLoading: isBalanceLoading } = useBalance( - isCompatible ? item.address : undefined, - isCompatible ? balanceNetwork : undefined - ) - - const walletBalance = balances?.find(b => b?.token === token?.symbol) - - const isSelected = selectable && (item.addresses.length == 1 && item.address == selectedAddress) - const walletBalanceAmount = walletBalance?.amount !== undefined ? truncateDecimals(walletBalance.amount, token?.precision) : '' - - const [saveAddress, setSaveAddress] = useState(null) - const saveType = network?.type ?? (item.providerName ? item.providerName.toLowerCase() as NetworkType : undefined) - const supportedNetworks = getWithdrawalSupportedNetworks(item) - const saveInDrawer = !(selectable && onWalletSelect && network) - - // The row hosts nested interactive elements (disconnect tooltip button, - // ExtendedAddress popover trigger), so it must not be a - - -

Disconnect

-
- - } - { - isMulti && - - } - { - isSelected && -
- -
- } -
- { - isMulti && isExpanded && -
- { - item.addresses.map((address, index) => ) - } -
- } - {saveInDrawer && setSaveAddress(null)} - header="Save address" - modalId="saveWalletToBook" - mode="fitHeight" - > - - {saveAddress && ( - setSaveAddress(null)} - /> - )} - - } -
+ <> + + setSaveRequest(null)} /> + ) } +const SaveAddressDrawer: FC<{ request: SaveAddressRequest | null, onClose: () => void }> = ({ request, onClose }) => ( + onClose()} + header="Save address" + modalId="saveWalletToBook" + mode="fitHeight" + > + + {request && ( + + )} + + +) -type NestedWalletAddressProps = { - address: string, - selectable?: boolean, - token?: Token; - network?: Network; - item: AccountIdentity | Wallet, - onWalletSelect?: (props: SelectAccountProps) => void; - selectedAddress: string | undefined; - isCompatible?: boolean; - onSaveToBook?: (address: string) => void; -} - -const NestedWalletAddress: FC = ({ selectable, address, network, onWalletSelect, token, item, selectedAddress, isCompatible, onSaveToBook }) => { - const { networks } = useSettingsState() - const balanceNetwork = token ? networks.find(n => n.name === network?.name && n.tokens.some(t => t.symbol === token.symbol)) : undefined - const { balances, isLoading: isBalanceLoading } = useBalance( - isCompatible ? address : undefined, - isCompatible ? balanceNetwork : undefined - ) - - const isNestedSelected = selectable && address == selectedAddress - const nestedWalletBalance = balances?.find(b => b?.token === token?.symbol) - const nestedWalletBalanceAmount = nestedWalletBalance?.amount !== undefined ? truncateDecimals(nestedWalletBalance.amount, token?.precision) : '' - - // Not a - + - + {!overlapping && (
diff --git a/packages/widget/core/src/components/Input/Amount/MinMax.tsx b/packages/widget/core/src/components/Input/Amount/MinMax.tsx index d67f5719f5..34a9a523f6 100644 --- a/packages/widget/core/src/components/Input/Amount/MinMax.tsx +++ b/packages/widget/core/src/components/Input/Amount/MinMax.tsx @@ -150,7 +150,7 @@ const MinMax = (props: MinMaxProps) => { onClick={handleSetMaxAmount} /> - {showMaxTooltip ? + {showMaxTooltip ?

Max is calculated based on your balance minus gas fee for the transaction

: null} diff --git a/packages/widget/core/src/components/Input/RoutePicker/RouteSortingMenu.tsx b/packages/widget/core/src/components/Input/RoutePicker/RouteSortingMenu.tsx index 4ee6b13127..114309d533 100644 --- a/packages/widget/core/src/components/Input/RoutePicker/RouteSortingMenu.tsx +++ b/packages/widget/core/src/components/Input/RoutePicker/RouteSortingMenu.tsx @@ -58,7 +58,7 @@ const RouteSortingMenu: FC = () => { - +
{sortingOptions.map((option) => ( - +
= ({ wallets, addresses, selecte - +
{rows.map(({ address, label, short, icon }) => { const checked = selectedAddresses.includes(address) From 9b9dd37c5ff10b674c14231e5a932d7beaf61c17 Mon Sep 17 00:00:00 2001 From: yasha-meursault Date: Tue, 18 Aug 2026 20:35:30 +0400 Subject: [PATCH 4/9] cleanup and small fixes --- .../components/wallet-list/ConnectWalletButton.tsx | 2 +- .../src/components/wallet-list/WalletsIcons.tsx | 2 +- packages/ui-kit/src/lib/portal.ts | 14 ++------------ packages/widget/core/src/hooks/useWallet.ts | 2 -- 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/packages/ui-kit/src/components/wallet-list/ConnectWalletButton.tsx b/packages/ui-kit/src/components/wallet-list/ConnectWalletButton.tsx index 06182b916c..d58d9452ab 100644 --- a/packages/ui-kit/src/components/wallet-list/ConnectWalletButton.tsx +++ b/packages/ui-kit/src/components/wallet-list/ConnectWalletButton.tsx @@ -65,7 +65,7 @@ const ConnectWalletButton: FC = ({ provider, onConnect, descriptionText, return